diff --git a/client/CPreGame.cpp b/client/CPreGame.cpp index 237b4acee..133e33c46 100644 --- a/client/CPreGame.cpp +++ b/client/CPreGame.cpp @@ -621,7 +621,7 @@ CSelectionScreen::CSelectionScreen(CMenuScreen::EState Type, CMenuScreen::EMulti opt = new OptionsTab(); //scenario options tab opt->recActions = DISPOSE; - randMapTab = new RandomMapTab(); + randMapTab = new CRandomMapTab(); randMapTab->getMapInfoChanged() += bind(&CSelectionScreen::changeSelection, this, _1); randMapTab->recActions = DISPOSE; } @@ -650,7 +650,7 @@ CSelectionScreen::CSelectionScreen(CMenuScreen::EState Type, CMenuScreen::EMulti randomBtn->callback = [&]() { toggleTab(randMapTab); - changeSelection(&randMapTab->getMapInfo()); + changeSelection(randMapTab->getMapInfo()); }; start = new CAdventureMapButton(CGI->generaltexth->zelp[103], bind(&CSelectionScreen::startScenario, this), 411, 535, "SCNRBEG.DEF", SDLK_b); @@ -801,6 +801,7 @@ void CSelectionScreen::toggleTab(CIntObject *tab) void CSelectionScreen::changeSelection(const CMapInfo * to) { + if(current == to) return; if(multiPlayer == CMenuScreen::MULTI_NETWORK_GUEST) { vstd::clear_pointer(current); @@ -814,17 +815,14 @@ void CSelectionScreen::changeSelection(const CMapInfo * to) if(screenType != CMenuScreen::campaignList) { updateStartInfo(to ? to->fileURI : "", sInfo, to ? to->mapHeader.get() : NULL); - if(screenType == CMenuScreen::newGame) { if(to && to->isRandomMap) { - sInfo.createRandomMap = true; sInfo.mapGenOptions = std::shared_ptr(new CMapGenOptions(randMapTab->getMapGenOptions())); } else { - sInfo.createRandomMap = false; sInfo.mapGenOptions.reset(); } } @@ -884,6 +882,26 @@ void CSelectionScreen::startScenario() if(!current) return; + if(sInfo.mapGenOptions) + { + // Update player settings for RMG + BOOST_FOREACH(const auto & psetPair, sInfo.playerInfos) + { + const auto & pset = psetPair.second; + sInfo.mapGenOptions->setStartingTownForPlayer(pset.color, pset.castle); + if(pset.playerID != PlayerSettings::PLAYER_AI) + { + sInfo.mapGenOptions->setPlayerTypeForStandardPlayer(pset.color, EPlayerType::HUMAN); + } + } + + if(!sInfo.mapGenOptions->checkOptions()) + { + GH.pushInt(CInfoWindow::create(CGI->generaltexth->allTexts[751])); + return; + } + } + saveGameName.clear(); if(screenType == CMenuScreen::loadGame) { @@ -1599,7 +1617,7 @@ const CMapInfo * SelectionTab::getSelectedMapInfo() const return curItems.empty() ? nullptr : curItems[selectionPos]; } -RandomMapTab::RandomMapTab() +CRandomMapTab::CRandomMapTab() { OBJ_CONSTRUCTION; bg = new CPicture("RANMAPBK", 0, 6); @@ -1623,7 +1641,7 @@ RandomMapTab::RandomMapTab() // Two levels twoLevelsBtn = new CHighlightableButton(0, 0, std::map(), CGI->generaltexth->zelp[202].second, false, "RANUNDR", nullptr, 346, 81); - twoLevelsBtn->select(true); + //twoLevelsBtn->select(true); for now, deactivated twoLevelsBtn->callback = [&]() { mapGenOptions.setHasTwoLevels(true); updateMapInfo(); }; twoLevelsBtn->callback2 = [&]() { mapGenOptions.setHasTwoLevels(false); updateMapInfo(); }; @@ -1643,7 +1661,7 @@ RandomMapTab::RandomMapTab() addButtonsWithRandToGroup(playersCntGroup, numberDefs, 1, 8, NUMBERS_WIDTH, 204, 212); playersCntGroup->onChange = [&](int btnId) { - mapGenOptions.setPlayersCnt(btnId); + mapGenOptions.setPlayerCount(btnId); deactivateButtonsFrom(teamsCntGroup, btnId); deactivateButtonsFrom(compOnlyPlayersCntGroup, 8 - btnId + 1); validatePlayersCnt(btnId); @@ -1657,7 +1675,7 @@ RandomMapTab::RandomMapTab() addButtonsWithRandToGroup(teamsCntGroup, numberDefs, 0, 7, NUMBERS_WIDTH, 214, 222); teamsCntGroup->onChange = [&](int btnId) { - mapGenOptions.setTeamsCnt(btnId); + mapGenOptions.setTeamCount(btnId); updateMapInfo(); }; @@ -1669,7 +1687,7 @@ RandomMapTab::RandomMapTab() compOnlyPlayersCntGroup->select(0, true); compOnlyPlayersCntGroup->onChange = [&](int btnId) { - mapGenOptions.setCompOnlyPlayersCnt(btnId); + mapGenOptions.setCompOnlyPlayerCount(btnId); deactivateButtonsFrom(compOnlyTeamsCntGroup, btnId); validateCompOnlyPlayersCnt(btnId); updateMapInfo(); @@ -1683,7 +1701,7 @@ RandomMapTab::RandomMapTab() deactivateButtonsFrom(compOnlyTeamsCntGroup, 0); compOnlyTeamsCntGroup->onChange = [&](int btnId) { - mapGenOptions.setCompOnlyTeamsCnt(btnId); + mapGenOptions.setCompOnlyTeamCount(btnId); updateMapInfo(); }; @@ -1714,17 +1732,10 @@ RandomMapTab::RandomMapTab() showRandMaps = new CAdventureMapButton("", CGI->generaltexth->zelp[252].second, 0, 54, 535, "RANSHOW"); // Initialize map info object - mapInfo.isRandomMap = true; - shared_ptr mapHeader(new CMapHeader()); - mapHeader->version = EMapFormat::SOD; - mapHeader->name = CGI->generaltexth->allTexts[740]; - mapHeader->description = CGI->generaltexth->allTexts[741]; - mapHeader->difficulty = 1; // Normal - mapInfo.mapHeader = mapHeader; updateMapInfo(); } -void RandomMapTab::addButtonsWithRandToGroup(CHighlightableButtonsGroup * group, const std::vector & defs, int nStart, int nEnd, int btnWidth, int helpStartIndex, int helpRandIndex) const +void CRandomMapTab::addButtonsWithRandToGroup(CHighlightableButtonsGroup * group, const std::vector & defs, int nStart, int nEnd, int btnWidth, int helpStartIndex, int helpRandIndex) const { addButtonsToGroup(group, defs, nStart, nEnd, btnWidth, helpStartIndex); @@ -1735,7 +1746,7 @@ void RandomMapTab::addButtonsWithRandToGroup(CHighlightableButtonsGroup * group, group->select(CMapGenOptions::RANDOM_SIZE, true); } -void RandomMapTab::addButtonsToGroup(CHighlightableButtonsGroup * group, const std::vector & defs, int nStart, int nEnd, int btnWidth, int helpStartIndex) const +void CRandomMapTab::addButtonsToGroup(CHighlightableButtonsGroup * group, const std::vector & defs, int nStart, int nEnd, int btnWidth, int helpStartIndex) const { // Buttons are relative to button group, TODO better solution? SObjectConstruction obj__i(group); @@ -1746,7 +1757,7 @@ void RandomMapTab::addButtonsToGroup(CHighlightableButtonsGroup * group, const s } } -void RandomMapTab::deactivateButtonsFrom(CHighlightableButtonsGroup * group, int startId) +void CRandomMapTab::deactivateButtonsFrom(CHighlightableButtonsGroup * group, int startId) { BOOST_FOREACH(CHighlightableButton * btn, group->buttons) { @@ -1767,42 +1778,42 @@ void RandomMapTab::deactivateButtonsFrom(CHighlightableButtonsGroup * group, int } } -void RandomMapTab::validatePlayersCnt(int playersCnt) +void CRandomMapTab::validatePlayersCnt(int playersCnt) { if(playersCnt == CMapGenOptions::RANDOM_SIZE) { return; } - if(mapGenOptions.getTeamsCnt() >= playersCnt) + if(mapGenOptions.getTeamCount() >= playersCnt) { - mapGenOptions.setTeamsCnt(playersCnt - 1); - teamsCntGroup->select(mapGenOptions.getTeamsCnt(), true); + mapGenOptions.setTeamCount(playersCnt - 1); + teamsCntGroup->select(mapGenOptions.getTeamCount(), true); } - if(mapGenOptions.getCompOnlyPlayersCnt() > 8 - playersCnt) + if(mapGenOptions.getCompOnlyPlayerCount() > 8 - playersCnt) { - mapGenOptions.setCompOnlyPlayersCnt(8 - playersCnt); - compOnlyPlayersCntGroup->select(mapGenOptions.getCompOnlyPlayersCnt(), true); + mapGenOptions.setCompOnlyPlayerCount(8 - playersCnt); + compOnlyPlayersCntGroup->select(mapGenOptions.getCompOnlyPlayerCount(), true); } - validateCompOnlyPlayersCnt(mapGenOptions.getCompOnlyPlayersCnt()); + validateCompOnlyPlayersCnt(mapGenOptions.getCompOnlyPlayerCount()); } -void RandomMapTab::validateCompOnlyPlayersCnt(int compOnlyPlayersCnt) +void CRandomMapTab::validateCompOnlyPlayersCnt(int compOnlyPlayersCnt) { if(compOnlyPlayersCnt == CMapGenOptions::RANDOM_SIZE) { return; } - if(mapGenOptions.getCompOnlyTeamsCnt() >= compOnlyPlayersCnt) + if(mapGenOptions.getCompOnlyTeamCount() >= compOnlyPlayersCnt) { - mapGenOptions.setCompOnlyTeamsCnt(compOnlyPlayersCnt - 1); - compOnlyTeamsCntGroup->select(mapGenOptions.getCompOnlyTeamsCnt(), true); + mapGenOptions.setCompOnlyTeamCount(compOnlyPlayersCnt - 1); + compOnlyTeamsCntGroup->select(mapGenOptions.getCompOnlyTeamCount(), true); } } -void RandomMapTab::showAll(SDL_Surface * to) +void CRandomMapTab::showAll(SDL_Surface * to) { CIntObject::showAll(to); @@ -1832,24 +1843,34 @@ void RandomMapTab::showAll(SDL_Surface * to) printAtLoc(CGI->generaltexth->allTexts[758], 68, 465, FONT_SMALL, Colors::WHITE, to); } -void RandomMapTab::updateMapInfo() +void CRandomMapTab::updateMapInfo() { - mapInfo.mapHeader->height = mapGenOptions.getHeight(); - mapInfo.mapHeader->width = mapGenOptions.getWidth(); - mapInfo.mapHeader->twoLevel = mapGenOptions.getHasTwoLevels(); + // Generate header info + mapInfo = make_unique(); + mapInfo->isRandomMap = true; + auto mapHeader = std::make_shared(); + mapHeader->version = EMapFormat::SOD; + mapHeader->name = CGI->generaltexth->allTexts[740]; + mapHeader->description = CGI->generaltexth->allTexts[741]; + mapHeader->difficulty = 1; // Normal + mapInfo->mapHeader = mapHeader; + mapInfo->mapHeader->height = mapGenOptions.getHeight(); + mapInfo->mapHeader->width = mapGenOptions.getWidth(); + mapInfo->mapHeader->twoLevel = mapGenOptions.getHasTwoLevels(); // Generate player information - mapInfo.mapHeader->players.clear(); - int playersToGen = (mapGenOptions.getPlayersCnt() == CMapGenOptions::RANDOM_SIZE - || mapGenOptions.getCompOnlyPlayersCnt() == CMapGenOptions::RANDOM_SIZE) - ? 8 : mapGenOptions.getPlayersCnt() + mapGenOptions.getCompOnlyPlayersCnt(); - mapInfo.mapHeader->howManyTeams = playersToGen; + mapInfo->mapHeader->players.clear(); + int playersToGen = (mapGenOptions.getPlayerCount() == CMapGenOptions::RANDOM_SIZE + || mapGenOptions.getCompOnlyPlayerCount() == CMapGenOptions::RANDOM_SIZE) + ? 8 : mapGenOptions.getPlayerCount() + mapGenOptions.getCompOnlyPlayerCount(); + mapInfo->mapHeader->howManyTeams = playersToGen; for(int i = 0; i < playersToGen; ++i) { PlayerInfo player; + player.isFactionRandom = true; player.canComputerPlay = true; - if(i >= mapGenOptions.getPlayersCnt() && mapGenOptions.getPlayersCnt() != CMapGenOptions::RANDOM_SIZE) + if(i >= mapGenOptions.getPlayerCount() && mapGenOptions.getPlayerCount() != CMapGenOptions::RANDOM_SIZE) { player.canHumanPlay = false; } @@ -1860,23 +1881,23 @@ void RandomMapTab::updateMapInfo() player.team = TeamID(i); player.hasMainTown = true; player.generateHeroAtMainTown = true; - mapInfo.mapHeader->players.push_back(player); + mapInfo->mapHeader->players.push_back(player); } - mapInfoChanged(&mapInfo); + mapInfoChanged(mapInfo.get()); } -CFunctionList & RandomMapTab::getMapInfoChanged() +CFunctionList & CRandomMapTab::getMapInfoChanged() { return mapInfoChanged; } -const CMapInfo & RandomMapTab::getMapInfo() const +const CMapInfo * CRandomMapTab::getMapInfo() const { - return mapInfo; + return mapInfo.get(); } -const CMapGenOptions & RandomMapTab::getMapGenOptions() const +const CMapGenOptions & CRandomMapTab::getMapGenOptions() const { return mapGenOptions; } diff --git a/client/CPreGame.h b/client/CPreGame.h index 577e30394..d9253bcc2 100644 --- a/client/CPreGame.h +++ b/client/CPreGame.h @@ -29,7 +29,7 @@ class CCampaignState; class CConnection; class JsonNode; class CMapGenOptions; -class RandomMapTab; +class CRandomMapTab; struct CPackForSelectionScreen; struct PlayerInfo; @@ -282,139 +282,32 @@ public: bool canUseThisHero(PlayerColor player, int ID ); }; -/** - * The random map tab shows options for generating a random map. - */ -class RandomMapTab : public CIntObject +/// The random map tab shows options for generating a random map. +class CRandomMapTab : public CIntObject { public: - /** - * C-tor. - */ - RandomMapTab(); + CRandomMapTab(); - /** - * Shows the interface and the visual representation of this tab. - * - * @param to where the graphics should be inserted - */ void showAll(SDL_Surface * to); - - /** - * Updates the map info object and fires the associated callback method. - */ void updateMapInfo(); - - /** - * Gets the map info changed callback method list object. This event - * occurs when the updateMapInfo method has been called or the options - * of this tab have been changed. - * - * @return the map info changed callback method list object - */ CFunctionList & getMapInfoChanged(); - - /** - * Gets the created map info object. - * - * @return the created map info object - */ - const CMapInfo & getMapInfo() const; - - /** - * Gets the map generation options. - * - * @return the map generation options - */ + const CMapInfo * getMapInfo() const; const CMapGenOptions & getMapGenOptions() const; private: - /** - * Adds buttons specified by the defs list to the given buttons group. - * - * @param group the button group where the buttons should be added to - * @param defs the names of the button defs - * @param startIndex start index of the defs vector - * @param endIndex end index of the defs vector - * @param btnWidth width of one button(fixed width) - * @param helpStartIndex the index of the first help msg entry - */ void addButtonsToGroup(CHighlightableButtonsGroup * group, const std::vector & defs, int startIndex, int endIndex, int btnWidth, int helpStartIndex) const; - - /** - * Adds buttons specified by the defs list and the random button to the given buttons group. Auto-selects the - * random button. - * - * @param group the button group where the buttons should be added to - * @param defs the names of the button defs - * @param startIndex start index of the defs vector - * @param endIndex end index of the defs vector - * @param btnWidth width of one button(fixed width) - * @param helpStartIndex the index of the first help msg entry - * @param helpRandIndex the index of the random help msg entry - */ void addButtonsWithRandToGroup(CHighlightableButtonsGroup * group, const std::vector & defs, int startIndex, int endIndex, int btnWidth, int helpStartIndex, int helpRandIndex) const; - - /** - * Deactives buttons of a highlightable button group beginning from startId. Buttons with a id - * lower than startId will be activated/reseted. - * - * @param group the associated highlightable button group - * @param startId the id of the first button to deactivate - */ void deactivateButtonsFrom(CHighlightableButtonsGroup * group, int startId); - - /** - * Validates players count and updates teams count, comp only players/teams count if necessary. - * - * @param playersCnt the players count to validate - */ void validatePlayersCnt(int playersCnt); - - /** - * Validates computer only players count and updates comp only teams count if necessary. - * - * @param compOnlyPlayersCnt the computer only players count to validate - */ void validateCompOnlyPlayersCnt(int compOnlyPlayersCnt); - /** the background image of the rmg options tab */ CPicture * bg; - - /** the map size buttons group */ - CHighlightableButtonsGroup * mapSizeBtnGroup; - - /** the two levels highlightable button */ - CHighlightableButton * twoLevelsBtn; - - /** the players count group */ - CHighlightableButtonsGroup * playersCntGroup; - - /** the teams count group */ - CHighlightableButtonsGroup * teamsCntGroup; - - /** the computer only players count group */ - CHighlightableButtonsGroup * compOnlyPlayersCntGroup; - - /** the computer only teams count group */ - CHighlightableButtonsGroup * compOnlyTeamsCntGroup; - - /** the water content group */ - CHighlightableButtonsGroup * waterContentGroup; - - /** the monster strength group */ - CHighlightableButtonsGroup * monsterStrengthGroup; - - /** show previously created random maps button */ + CHighlightableButton * twoLevelsBtn; + CHighlightableButtonsGroup * mapSizeBtnGroup, * playersCntGroup, * teamsCntGroup, * compOnlyPlayersCntGroup, + * compOnlyTeamsCntGroup, * waterContentGroup, * monsterStrengthGroup; CAdventureMapButton * showRandMaps; - - /** the map options selected by the user */ CMapGenOptions mapGenOptions; - - /** map info object describing a randomly created map */ - CMapInfo mapInfo; - - /** callback method which gets called when the random options have been changed */ + unique_ptr mapInfo; CFunctionList mapInfoChanged; }; @@ -452,7 +345,7 @@ public: CPicture *bg; //general bg image InfoCard *card; OptionsTab *opt; - RandomMapTab * randMapTab; + CRandomMapTab * randMapTab; CAdventureMapButton *start, *back; SelectionTab *sel; diff --git a/config/rmg.json b/config/rmg.json index a4a600848..ca2f72cfa 100644 --- a/config/rmg.json +++ b/config/rmg.json @@ -1,66 +1,167 @@ // Defines random map templates. { - "Small Ring" : + "Analogy" : { - "minSize" : "s", "maxSize" : "s+u", - "minHumanCnt" : 1, "maxHumanCnt" : 8, "minTotalCnt" : 2, "maxTotalCnt" : 8, + "minSize" : "m", "maxSize" : "m", + "players" : "4", "zones" : { "1" : { - "type" : "humanStart", "baseSize" : 11, "owner" : 1, - "playerTowns" : { "minCastles" : 1 }, "allowedTownTypes" : [ "all" ], "matchTerrainToTown" : true + "type" : "playerStart", "size" : 1, "owner" : 1, + "playerTowns" : { "castles" : 1 }, "neutralTowns" : { "towns" : 1 }, "townsAreSameType" : true }, "2" : { - "type" : "humanStart", "baseSize" : 11, "owner" : 2, - "playerTowns" : { "minCastles" : 1 }, "allowedTownTypes" : [ "all" ], "matchTerrainToTown" : true + "type" : "playerStart", "size" : 1, "owner" : 2, + "playerTowns" : { "castles" : 1 }, "neutralTowns" : { "towns" : 1 }, "townsAreSameType" : true }, "3" : { - "type" : "humanStart", "baseSize" : 11, "owner" : 3, - "playerTowns" : { "minCastles" : 1 }, "allowedTownTypes" : [ "all" ], "matchTerrainToTown" : true + "type" : "playerStart", "size" : 1, "owner" : 3, + "playerTowns" : { "castles" : 1 }, "neutralTowns" : { "towns" : 1 }, "townsAreSameType" : true }, "4" : { - "type" : "humanStart", "baseSize" : 11, "owner" : 4, - "playerTowns" : { "minCastles" : 1 }, "allowedTownTypes" : [ "all" ], "matchTerrainToTown" : true + "type" : "playerStart", "size" : 1, "owner" : 4, + "playerTowns" : { "castles" : 1 }, "neutralTowns" : { "towns" : 1 }, "townsAreSameType" : true }, "5" : { - "type" : "humanStart", "baseSize" : 11, "owner" : 5, - "playerTowns" : { "minCastles" : 1 }, "allowedTownTypes" : [ "all" ], "matchTerrainToTown" : true - }, - "6" : - { - "type" : "humanStart", "baseSize" : 11, "owner" : 6, - "playerTowns" : { "minCastles" : 1 }, "allowedTownTypes" : [ "all" ], "matchTerrainToTown" : true - }, - "7" : - { - "type" : "humanStart", "baseSize" : 11, "owner" : 7, - "playerTowns" : { "minCastles" : 1 }, "allowedTownTypes" : [ "all" ], "matchTerrainToTown" : true - }, - "8" : - { - "type" : "humanStart", "baseSize" : 11, "owner" : 8, - "playerTowns" : { "minCastles" : 1 }, "allowedTownTypes" : [ "all" ], "matchTerrainToTown" : true + "type" : "treasure", "size" : 2, "terrainTypes" : [ "sand" ], "neutralTowns" : { "castles" : 1 } } }, "connections" : [ - { "a" : "1", "b" : "2", "guardStrength" : 4500 }, - { "a" : "2", "b" : "3", "guardStrength" : 4500 }, - { "a" : "3", "b" : "4", "guardStrength" : 4500 }, - { "a" : "4", "b" : "5", "guardStrength" : 4500 }, - { "a" : "5", "b" : "6", "guardStrength" : 4500 }, - { "a" : "6", "b" : "7", "guardStrength" : 4500 }, - { "a" : "7", "b" : "8", "guardStrength" : 4500 }, - { "a" : "8", "b" : "1", "guardStrength" : 4500 }, - { "a" : "4", "b" : "1", "guardStrength" : 4500 }, - { "a" : "5", "b" : "1", "guardStrength" : 4500 }, - { "a" : "6", "b" : "1", "guardStrength" : 4500 }, - { "a" : "7", "b" : "1", "guardStrength" : 4500 } + { "a" : "1", "b" : "5", "guard" : 1000 }, + { "a" : "2", "b" : "5", "guard" : 1000 }, + { "a" : "3", "b" : "5", "guard" : 1000 }, + { "a" : "4", "b" : "5", "guard" : 1000 } + ] + }, + "Upgrade" : + { + "minSize" : "m", "maxSize" : "m", + "players" : "2", + "zones" : + { + "1" : + { + "type" : "playerStart", "size" : 1, "owner" : 1, + "playerTowns" : { "castles" : 1 } + }, + "2" : + { + "type" : "playerStart", "size" : 1, "owner" : 2, + "playerTowns" : { "castles" : 1 } + }, + "3" : + { + "type" : "treasure", "size" : 2, "neutralTowns" : { "towns" : 1 }, "townTypeLikeZone" : "1" + }, + "4" : + { + "type" : "treasure", "size" : 2, "neutralTowns" : { "towns" : 1 }, "townTypeLikeZone" : "2" + }, + "5" : + { + "type" : "treasure", "size" : 3, "neutralTowns" : { "castles" : 1 }, "terrainTypes" : [ "sand" ] + } + }, + "connections" : + [ + { "a" : "1", "b" : "3", "guard" : 1000 }, + { "a" : "1", "b" : "5", "guard" : 4000 }, + { "a" : "2", "b" : "4", "guard" : 1000 }, + { "a" : "2", "b" : "5", "guard" : 4000 }, + { "a" : "3", "b" : "5", "guard" : 2000 }, + { "a" : "4", "b" : "5", "guard" : 2000 } + ] + }, + "Golden Ring" : + { + "minSize" : "m", "maxSize" : "m", + "players" : "3", + "zones" : + { + "1" : + { + "type" : "playerStart", "size" : 3, "owner" : 1, + "playerTowns" : { "castles" : 1 } + }, + "2" : + { + "type" : "playerStart", "size" : 3, "owner" : 2, + "playerTowns" : { "castles" : 1 } + }, + "3" : + { + "type" : "playerStart", "size" : 3, "owner" : 3, + "playerTowns" : { "castles" : 1 } + }, + "4" : { "type" : "treasure", "size" : 1, "terrainTypeLikeZone" : "1" }, + "5" : { "type" : "treasure", "size" : 1, "terrainTypeLikeZone" : "1" }, + "6" : { "type" : "treasure", "size" : 1, "terrainTypeLikeZone" : "2" }, + "7" : { "type" : "treasure", "size" : 1, "terrainTypeLikeZone" : "2" }, + "8" : { "type" : "treasure", "size" : 1, "terrainTypeLikeZone" : "3" }, + "9" : { "type" : "treasure", "size" : 1, "terrainTypeLikeZone" : "3" }, + "10" : { "type" : "treasure", "size" : 1, "neutralTowns" : { "towns" : 1 } }, + "11" : { "type" : "treasure", "size" : 1, "neutralTowns" : { "towns" : 1 } }, + "12" : { "type" : "treasure", "size" : 1, "neutralTowns" : { "towns" : 1 } } + }, + "connections" : + [ + { "a" : "1", "b" : "4", "guard" : 1000 }, + { "a" : "1", "b" : "5", "guard" : 1000 }, + { "a" : "2", "b" : "6", "guard" : 1000 }, + { "a" : "2", "b" : "7", "guard" : 1000 }, + { "a" : "3", "b" : "8", "guard" : 1000 }, + { "a" : "3", "b" : "9", "guard" : 1000 }, + { "a" : "4", "b" : "10", "guard" : 1000 }, + { "a" : "5", "b" : "12", "guard" : 1000 }, + { "a" : "6", "b" : "10", "guard" : 1000 }, + { "a" : "7", "b" : "11", "guard" : 1000 }, + { "a" : "8", "b" : "12", "guard" : 1000 }, + { "a" : "9", "b" : "11", "guard" : 1000 } + ] + }, + "Unfair Game" : + { + "minSize" : "m", "maxSize" : "m", + "players" : "2", "cpu" : "2", + "zones" : + { + "1" : + { + "type" : "playerStart", "size" : 2, "owner" : 1, + "playerTowns" : { "castles" : 1 } + }, + "2" : + { + "type" : "playerStart", "size" : 2, "owner" : 2, + "playerTowns" : { "castles" : 1 } + }, + "3" : + { + "type" : "cpuStart", "size" : 3, "owner" : 3, + "playerTowns" : { "castles" : 1 } + }, + "4" : + { + "type" : "cpuStart", "size" : 3, "owner" : 4, + "playerTowns" : { "castles" : 1 } + }, + "5" : { "type" : "treasure", "size" : 1, "terrainTypeLikeZone" : "3" }, + "6" : { "type" : "treasure", "size" : 1, "terrainTypeLikeZone" : "4" } + }, + "connections" : + [ + { "a" : "1", "b" : "3", "guard" : 2000 }, + { "a" : "1", "b" : "4", "guard" : 2000 }, + { "a" : "2", "b" : "3", "guard" : 2000 }, + { "a" : "2", "b" : "4", "guard" : 2000 }, + { "a" : "3", "b" : "5", "guard" : 1000 }, + { "a" : "4", "b" : "6", "guard" : 1000 } ] } } diff --git a/lib/CGameState.cpp b/lib/CGameState.cpp index 73bb02140..36e638064 100644 --- a/lib/CGameState.cpp +++ b/lib/CGameState.cpp @@ -862,22 +862,11 @@ void CGameState::init(StartInfo * si) { case StartInfo::NEW_GAME: { - if(scenarioOps->createRandomMap) + if(scenarioOps->createRandomMap()) { logGlobal->infoStream() << "Create random map."; CStopWatch sw; - // Create player settings for RMG - BOOST_FOREACH(const auto & pair, scenarioOps->playerInfos) - { - const auto & playerSettings = pair.second; - scenarioOps->mapGenOptions->setStartingTownForPlayer(playerSettings.color, playerSettings.castle); - if(playerSettings.playerID > 0) - { - scenarioOps->mapGenOptions->setPlayerTypeForStandardPlayer(playerSettings.color, EPlayerType::HUMAN); - } - } - // Gen map CMapGenerator mapGen(*(scenarioOps->mapGenOptions), scenarioOps->seedToBeUsed); map = mapGen.generate().release(); diff --git a/lib/Connection.h b/lib/Connection.h index 565de834d..2c1bc2890 100644 --- a/lib/Connection.h +++ b/lib/Connection.h @@ -1081,6 +1081,7 @@ public: void loadSerializable(std::set &data) { READ_CHECK_U32(length); + data.clear(); T ins; for(ui32 i=0;i &data) { READ_CHECK_U32(length); + data.clear(); T ins; for(ui32 i=0;i &data) { READ_CHECK_U32(length); + data.clear(); T ins; for(ui32 i=0;i &data) { READ_CHECK_U32(length); + data.clear(); T1 t; for(ui32 i=0;i mapGenOptions; // needs to be not nullptr if createRandomMap=true + bool createRandomMap() const { return mapGenOptions.get() != nullptr; } + shared_ptr mapGenOptions; shared_ptr campState; @@ -113,13 +113,12 @@ struct StartInfo h & mapfileChecksum; h & turnTime; h & mapname; - h & createRandomMap; h & mapGenOptions; h & campState; } StartInfo() : mode(INVALID), difficulty(0), seedToBeUsed(0), seedPostInit(0), - mapfileChecksum(0), turnTime(0), createRandomMap(false) + mapfileChecksum(0), turnTime(0) { } diff --git a/lib/mapping/CMapEditManager.cpp b/lib/mapping/CMapEditManager.cpp index 9937ba805..d20372d16 100644 --- a/lib/mapping/CMapEditManager.cpp +++ b/lib/mapping/CMapEditManager.cpp @@ -897,9 +897,12 @@ CClearTerrainOperation::CClearTerrainOperation(CMap * map, CRandomGenerator * ge CTerrainSelection terrainSel(map); terrainSel.selectRange(MapRect(int3(0, 0, 0), map->width, map->height)); addOperation(make_unique(map, terrainSel, ETerrainType::WATER, gen)); - terrainSel.clearSelection(); - terrainSel.selectRange(MapRect(int3(0, 0, 1), map->width, map->height)); - addOperation(make_unique(map, terrainSel, ETerrainType::ROCK, gen)); + if(map->twoLevel) + { + terrainSel.clearSelection(); + terrainSel.selectRange(MapRect(int3(0, 0, 1), map->width, map->height)); + addOperation(make_unique(map, terrainSel, ETerrainType::ROCK, gen)); + } } std::string CClearTerrainOperation::getLabel() const diff --git a/lib/rmg/CMapGenerator.cpp b/lib/rmg/CMapGenerator.cpp index 6c4c04342..387e90441 100644 --- a/lib/rmg/CMapGenerator.cpp +++ b/lib/rmg/CMapGenerator.cpp @@ -11,9 +11,9 @@ #include "../StringConstants.h" #include "../filesystem/CResourceLoader.h" -CMapGenOptions::CMapGenOptions() : width(CMapHeader::MAP_SIZE_MIDDLE), height(CMapHeader::MAP_SIZE_MIDDLE), hasTwoLevels(true), - playersCnt(RANDOM_SIZE), teamsCnt(RANDOM_SIZE), compOnlyPlayersCnt(0), compOnlyTeamsCnt(RANDOM_SIZE), - waterContent(EWaterContent::RANDOM), monsterStrength(EMonsterStrength::RANDOM) +CMapGenOptions::CMapGenOptions() : width(CMapHeader::MAP_SIZE_MIDDLE), height(CMapHeader::MAP_SIZE_MIDDLE), hasTwoLevels(false), + playerCount(RANDOM_SIZE), teamCount(RANDOM_SIZE), compOnlyPlayerCount(0), compOnlyTeamCount(RANDOM_SIZE), + waterContent(EWaterContent::RANDOM), monsterStrength(EMonsterStrength::RANDOM), mapTemplate(nullptr) { resetPlayersMap(); } @@ -50,50 +50,50 @@ void CMapGenOptions::setHasTwoLevels(bool value) hasTwoLevels = value; } -si8 CMapGenOptions::getPlayersCnt() const +si8 CMapGenOptions::getPlayerCount() const { - return playersCnt; + return playerCount; } -void CMapGenOptions::setPlayersCnt(si8 value) +void CMapGenOptions::setPlayerCount(si8 value) { assert((value >= 1 && value <= PlayerColor::PLAYER_LIMIT_I) || value == RANDOM_SIZE); - playersCnt = value; + playerCount = value; resetPlayersMap(); } -si8 CMapGenOptions::getTeamsCnt() const +si8 CMapGenOptions::getTeamCount() const { - return teamsCnt; + return teamCount; } -void CMapGenOptions::setTeamsCnt(si8 value) +void CMapGenOptions::setTeamCount(si8 value) { - assert(playersCnt == RANDOM_SIZE || (value >= 0 && value < playersCnt) || value == RANDOM_SIZE); - teamsCnt = value; + assert(playerCount == RANDOM_SIZE || (value >= 0 && value < playerCount) || value == RANDOM_SIZE); + teamCount = value; } -si8 CMapGenOptions::getCompOnlyPlayersCnt() const +si8 CMapGenOptions::getCompOnlyPlayerCount() const { - return compOnlyPlayersCnt; + return compOnlyPlayerCount; } -void CMapGenOptions::setCompOnlyPlayersCnt(si8 value) +void CMapGenOptions::setCompOnlyPlayerCount(si8 value) { - assert(value == RANDOM_SIZE || (value >= 0 && value <= PlayerColor::PLAYER_LIMIT_I - playersCnt)); - compOnlyPlayersCnt = value; + assert(value == RANDOM_SIZE || (value >= 0 && value <= PlayerColor::PLAYER_LIMIT_I - playerCount)); + compOnlyPlayerCount = value; resetPlayersMap(); } -si8 CMapGenOptions::getCompOnlyTeamsCnt() const +si8 CMapGenOptions::getCompOnlyTeamCount() const { - return compOnlyTeamsCnt; + return compOnlyTeamCount; } -void CMapGenOptions::setCompOnlyTeamsCnt(si8 value) +void CMapGenOptions::setCompOnlyTeamCount(si8 value) { - assert(value == RANDOM_SIZE || compOnlyPlayersCnt == RANDOM_SIZE || (value >= 0 && value <= std::max(compOnlyPlayersCnt - 1, 0))); - compOnlyTeamsCnt = value; + assert(value == RANDOM_SIZE || compOnlyPlayerCount == RANDOM_SIZE || (value >= 0 && value <= std::max(compOnlyPlayerCount - 1, 0))); + compOnlyTeamCount = value; } EWaterContent::EWaterContent CMapGenOptions::getWaterContent() const @@ -119,8 +119,8 @@ void CMapGenOptions::setMonsterStrength(EMonsterStrength::EMonsterStrength value void CMapGenOptions::resetPlayersMap() { players.clear(); - int realPlayersCnt = playersCnt == RANDOM_SIZE ? static_cast(PlayerColor::PLAYER_LIMIT_I) : playersCnt; - int realCompOnlyPlayersCnt = compOnlyPlayersCnt == RANDOM_SIZE ? (PlayerColor::PLAYER_LIMIT_I - realPlayersCnt) : compOnlyPlayersCnt; + int realPlayersCnt = playerCount == RANDOM_SIZE ? static_cast(PlayerColor::PLAYER_LIMIT_I) : playerCount; + int realCompOnlyPlayersCnt = compOnlyPlayerCount == RANDOM_SIZE ? (PlayerColor::PLAYER_LIMIT_I - realPlayersCnt) : compOnlyPlayerCount; for(int color = 0; color < (realPlayersCnt + realCompOnlyPlayersCnt); ++color) { CPlayerSettings player; @@ -150,6 +150,23 @@ void CMapGenOptions::setPlayerTypeForStandardPlayer(PlayerColor color, EPlayerTy it->second.setPlayerType(playerType); } +const CRmgTemplate * CMapGenOptions::getMapTemplate() const +{ + return mapTemplate; +} + +void CMapGenOptions::setMapTemplate(const CRmgTemplate * value) +{ + mapTemplate = value; + //TODO validate & adapt options according to template + assert(0); +} + +const std::map & CMapGenOptions::getAvailableTemplates() const +{ + return CRmgTemplateStorage::get().getTemplates(); +} + void CMapGenOptions::finalize() { CRandomGenerator gen; @@ -158,83 +175,39 @@ void CMapGenOptions::finalize() void CMapGenOptions::finalize(CRandomGenerator & gen) { - if(playersCnt == RANDOM_SIZE) + if(!mapTemplate) { - // 1 human is required at least - auto humanPlayers = countHumanPlayers(); - if(humanPlayers == 0) humanPlayers = 1; - playersCnt = gen.getInteger(humanPlayers, PlayerColor::PLAYER_LIMIT_I); - - // 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() == playersCnt) break; - if(it->second.getPlayerType() == EPlayerType::AI) - { - players.erase(it); - } - else - { - --itrev; - } - } - } - if(teamsCnt == RANDOM_SIZE) - { - teamsCnt = gen.getInteger(0, playersCnt - 1); - } - if(compOnlyPlayersCnt == RANDOM_SIZE) - { - compOnlyPlayersCnt = gen.getInteger(0, 8 - playersCnt); - auto totalPlayersCnt = playersCnt + compOnlyPlayersCnt; - - // 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; - if(it->second.getPlayerType() == EPlayerType::COMP_ONLY) - { - players.erase(it); - } - else - { - --itrev; - } - } - - // Add some comp only players if necessary - auto compOnlyPlayersToAdd = totalPlayersCnt - players.size(); - for(int i = 0; i < compOnlyPlayersToAdd; ++i) - { - CPlayerSettings pSettings; - pSettings.setPlayerType(EPlayerType::COMP_ONLY); - pSettings.setColor(getNextPlayerColor()); - players[pSettings.getColor()] = pSettings; - } - } - if(compOnlyTeamsCnt == RANDOM_SIZE) - { - compOnlyTeamsCnt = gen.getInteger(0, std::max(compOnlyPlayersCnt - 1, 0)); + mapTemplate = getPossibleTemplate(gen); + assert(mapTemplate); } - // There should be at least 2 players (1-player-maps aren't allowed) - if(playersCnt + compOnlyPlayersCnt < 2) + if(playerCount == RANDOM_SIZE) { - CPlayerSettings pSettings; - pSettings.setPlayerType(EPlayerType::AI); - pSettings.setColor(getNextPlayerColor()); - players[pSettings.getColor()] = pSettings; - playersCnt = 2; + auto possiblePlayers = mapTemplate->getPlayers().getNumbers(); + possiblePlayers.erase(possiblePlayers.begin(), possiblePlayers.lower_bound(countHumanPlayers())); + assert(!possiblePlayers.empty()); + playerCount = *std::next(possiblePlayers.begin(), gen.getInteger(0, possiblePlayers.size() - 1)); + updatePlayers(); + } + if(teamCount == RANDOM_SIZE) + { + teamCount = gen.getInteger(0, playerCount - 1); + } + if(compOnlyPlayerCount == RANDOM_SIZE) + { + auto possiblePlayers = mapTemplate->getCpuPlayers().getNumbers(); + compOnlyPlayerCount = *std::next(possiblePlayers.begin(), gen.getInteger(0, possiblePlayers.size() - 1)); + updateCompOnlyPlayers(); + } + if(compOnlyTeamCount == RANDOM_SIZE) + { + compOnlyTeamCount = gen.getInteger(0, std::max(compOnlyPlayerCount - 1, 0)); } // 1 team isn't allowed - if(teamsCnt == 1 && compOnlyPlayersCnt == 0) + if(teamCount == 1 && compOnlyPlayerCount == 0) { - teamsCnt = 0; + teamCount = 0; } if(waterContent == EWaterContent::RANDOM) @@ -247,6 +220,56 @@ void CMapGenOptions::finalize(CRandomGenerator & gen) } } +void CMapGenOptions::updatePlayers() +{ + // 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() == playerCount) break; + if(it->second.getPlayerType() == EPlayerType::AI) + { + players.erase(it); + } + else + { + --itrev; + } + } +} + +void CMapGenOptions::updateCompOnlyPlayers() +{ + auto totalPlayersCnt = playerCount + compOnlyPlayerCount; + + // 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; + if(it->second.getPlayerType() == EPlayerType::COMP_ONLY) + { + players.erase(it); + } + else + { + --itrev; + } + } + + // Add some comp only players if necessary + auto compOnlyPlayersToAdd = totalPlayersCnt - players.size(); + for(int i = 0; i < compOnlyPlayersToAdd; ++i) + { + CPlayerSettings pSettings; + pSettings.setPlayerType(EPlayerType::COMP_ONLY); + pSettings.setColor(getNextPlayerColor()); + players[pSettings.getColor()] = pSettings; + } +} + int CMapGenOptions::countHumanPlayers() const { return static_cast(boost::count_if(players, [](const std::pair & pair) @@ -268,6 +291,74 @@ PlayerColor CMapGenOptions::getNextPlayerColor() const return PlayerColor(0); } +bool CMapGenOptions::checkOptions() const +{ + assert(countHumanPlayers() > 0); + if(mapTemplate) + { + return true; + } + else + { + CRandomGenerator gen; + return getPossibleTemplate(gen) != nullptr; + } +} + +const CRmgTemplate * CMapGenOptions::getPossibleTemplate(CRandomGenerator & gen) const +{ + // Find potential templates + const auto & tpls = getAvailableTemplates(); + std::list potentialTpls; + BOOST_FOREACH(const auto & tplPair, tpls) + { + const auto & tpl = tplPair.second; + CRmgTemplate::CSize tplSize(width, height, hasTwoLevels); + if(tplSize >= tpl.getMinSize() && tplSize <= tpl.getMaxSize()) + { + bool isPlayerCountValid = false; + if(playerCount != RANDOM_SIZE) + { + if(tpl.getPlayers().isInRange(playerCount)) isPlayerCountValid = true; + } + else + { + // Human players shouldn't be banned when playing with random player count + auto playerNumbers = tpl.getPlayers().getNumbers(); + if(playerNumbers.lower_bound(countHumanPlayers()) != playerNumbers.end()) + { + isPlayerCountValid = true; + } + } + + if(isPlayerCountValid) + { + bool isCpuPlayerCountValid = false; + if(compOnlyPlayerCount != RANDOM_SIZE) + { + if(tpl.getCpuPlayers().isInRange(compOnlyPlayerCount)) isCpuPlayerCountValid = true; + } + else + { + isCpuPlayerCountValid = true; + } + + if(isCpuPlayerCountValid) potentialTpls.push_back(&tpl); + } + } + } + + // Select tpl + if(potentialTpls.empty()) + { + return nullptr; + } + else + { + return *std::next(potentialTpls.begin(), gen.getInteger(0, potentialTpls.size() - 1)); + } +} + CMapGenOptions::CPlayerSettings::CPlayerSettings() : color(0), startingTown(RANDOM_TOWN), playerType(EPlayerType::AI) { @@ -291,7 +382,12 @@ si32 CMapGenOptions::CPlayerSettings::getStartingTown() const void CMapGenOptions::CPlayerSettings::setStartingTown(si32 value) { - assert(value >= -1 && value < static_cast(VLC->townh->factions.size())); + assert(value >= -1); + if(value >= 0) + { + assert(value < static_cast(VLC->townh->factions.size())); + assert(VLC->townh->factions[value]->town != nullptr); + } startingTown = value; } @@ -320,9 +416,6 @@ std::unique_ptr CMapGenerator::generate() { mapGenOptions.finalize(gen); - //TODO select a template based on the map gen options or adapt it if necessary - CRandomMapTemplateStorage::get(); - map = make_unique(); editManager = map->getEditManager(); editManager->getUndoManager().setUndoRedoLimit(0); @@ -339,13 +432,12 @@ 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 , "; - ss << "Random seed was " << randomSeed << ", size " << map->width << "x"; - ss << map->height << ", levels " << (map->twoLevel ? "2" : "1") << ", "; - ss << "humans " << static_cast(mapGenOptions.getPlayersCnt()) << ", computers "; - ss << static_cast(mapGenOptions.getCompOnlyPlayersCnt()) << ", water " << waterContentStr[mapGenOptions.getWaterContent()]; - ss << ", monster " << monsterStrengthStr[mapGenOptions.getMonsterStrength()] << ", second expansion map"; + std::stringstream ss; + ss << boost::str(boost::format(std::string("Map created by the Random Map Generator.\nTemplate was %s, Random seed was %d, size %dx%d") + + ", levels %s, humans %d, computers %d, water %s, monster %s, second expansion map") % mapGenOptions.getMapTemplate()->getName() % + randomSeed % map->width % map->height % (map->twoLevel ? "2" : "1") % static_cast(mapGenOptions.getPlayerCount()) % + static_cast(mapGenOptions.getCompOnlyPlayerCount()) % waterContentStr[mapGenOptions.getWaterContent()] % + monsterStrengthStr[mapGenOptions.getMonsterStrength()]); BOOST_FOREACH(const auto & pair, mapGenOptions.getPlayersSettings()) { @@ -371,32 +463,32 @@ void CMapGenerator::addPlayerInfo() 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(); + int playerCount = i == 0 ? mapGenOptions.getPlayerCount() : mapGenOptions.getCompOnlyPlayerCount(); + int teamCount = i == 0 ? mapGenOptions.getTeamCount() : mapGenOptions.getCompOnlyTeamCount(); - if(playersCnt == 0) + if(playerCount == 0) { continue; } - int playersPerTeam = playersCnt / - (teamsCnt == 0 ? playersCnt : teamsCnt); - int teamsCntNorm = teamsCnt; - if(teamsCntNorm == 0) + int playersPerTeam = playerCount / + (teamCount == 0 ? playerCount : teamCount); + int teamCountNorm = teamCount; + if(teamCountNorm == 0) { - teamsCntNorm = playersCnt; + teamCountNorm = playerCount; } - for(int j = 0; j < teamsCntNorm; ++j) + for(int j = 0; j < teamCountNorm; ++j) { for(int k = 0; k < playersPerTeam; ++k) { teamNumbers[i].push_back(j + teamOffset); } } - for(int j = 0; j < playersCnt - teamsCntNorm * playersPerTeam; ++j) + for(int j = 0; j < playerCount - teamCountNorm * playersPerTeam; ++j) { teamNumbers[i].push_back(j + teamOffset); } - teamOffset += teamsCntNorm; + teamOffset += teamCountNorm; } // Team numbers are assigned randomly to every player @@ -416,23 +508,22 @@ void CMapGenerator::addPlayerInfo() map->players[pSettings.getColor().getNum()] = player; } - map->howManyTeams = (mapGenOptions.getTeamsCnt() == 0 ? mapGenOptions.getPlayersCnt() : mapGenOptions.getTeamsCnt()) - + (mapGenOptions.getCompOnlyTeamsCnt() == 0 ? mapGenOptions.getCompOnlyPlayersCnt() : mapGenOptions.getCompOnlyTeamsCnt()); + map->howManyTeams = (mapGenOptions.getTeamCount() == 0 ? mapGenOptions.getPlayerCount() : mapGenOptions.getTeamCount()) + + (mapGenOptions.getCompOnlyTeamCount() == 0 ? mapGenOptions.getCompOnlyPlayerCount() : mapGenOptions.getCompOnlyTeamCount()); } void CMapGenerator::genTerrain() { map->initTerrain(); editManager->clearTerrain(&gen); - editManager->getTerrainSelection().selectRange(MapRect(int3(10, 10, 0), 20, 30)); + editManager->getTerrainSelection().selectRange(MapRect(int3(4, 4, 0), 24, 30)); editManager->drawTerrain(ETerrainType::GRASS, &gen); } void CMapGenerator::genTowns() { //FIXME mock gen - const int3 townPos[2] = { int3(17, 13, 0), int3(25,13, 0) }; - const int townTypes[2] = { ETownType::CASTLE, ETownType::DUNGEON }; + const int3 townPos[2] = { int3(11, 7, 0), int3(19,7, 0) }; for(size_t i = 0; i < map->players.size(); ++i) { @@ -443,7 +534,9 @@ void CMapGenerator::genTowns() int side = i % 2; CGTownInstance * town = new CGTownInstance(); town->ID = Obj::TOWN; - town->subID = townTypes[side]; + int townId = mapGenOptions.getPlayersSettings().find(PlayerColor(i))->second.getStartingTown(); + if(townId == CMapGenOptions::CPlayerSettings::RANDOM_TOWN) townId = gen.getInteger(0, 8); // Default towns + town->subID = townId; town->tempOwner = owner; town->defInfo = VLC->dobjinfo->gobjs[town->ID][town->subID]; town->builtBuildings.insert(BuildingID::FORT); @@ -452,7 +545,7 @@ void CMapGenerator::genTowns() // Update player info playerInfo.allowedFactions.clear(); - playerInfo.allowedFactions.insert(townTypes[side]); + playerInfo.allowedFactions.insert(townId); playerInfo.hasMainTown = true; playerInfo.posOfMainTown = town->pos - int3(2, 0, 0); playerInfo.generateHeroAtMainTown = true; @@ -471,403 +564,495 @@ void CMapGenerator::addHeaderInfo() addPlayerInfo(); } -CTemplateZoneTowns::CTemplateZoneTowns() : minTowns(0), minCastles(0), townDensity(0), castleDensity(0) +CRmgTemplateZone::CTownInfo::CTownInfo() : townCount(0), castleCount(0), townDensity(0), castleDensity(0) { } -int CTemplateZoneTowns::getMinTowns() const +int CRmgTemplateZone::CTownInfo::getTownCount() const { - return minTowns; + return townCount; } -void CTemplateZoneTowns::setMinTowns(int value) +void CRmgTemplateZone::CTownInfo::setTownCount(int value) { - assert(value >= 0); - minTowns = value; + if(value < 0) throw std::runtime_error("Negative value for town count not allowed."); + townCount = value; } -int CTemplateZoneTowns::getMinCastles() const +int CRmgTemplateZone::CTownInfo::getCastleCount() const { - return minCastles; + return castleCount; } -void CTemplateZoneTowns::setMinCastles(int value) +void CRmgTemplateZone::CTownInfo::setCastleCount(int value) { - assert(value >= 0); - minCastles = value; + if(value < 0) throw std::runtime_error("Negative value for castle count not allowed."); + castleCount = value; } -int CTemplateZoneTowns::getTownDensity() const +int CRmgTemplateZone::CTownInfo::getTownDensity() const { return townDensity; } -void CTemplateZoneTowns::setTownDensity(int value) +void CRmgTemplateZone::CTownInfo::setTownDensity(int value) { - assert(value >= 0); + if(value < 0) throw std::runtime_error("Negative value for town density not allowed."); townDensity = value; } -int CTemplateZoneTowns::getCastleDensity() const +int CRmgTemplateZone::CTownInfo::getCastleDensity() const { return castleDensity; } -void CTemplateZoneTowns::setCastleDensity(int value) +void CRmgTemplateZone::CTownInfo::setCastleDensity(int value) { - assert(value >= 0); + if(value < 0) throw std::runtime_error("Negative value for castle density not allowed."); castleDensity = value; } -CTemplateZone::CTemplateZone() : id(0), type(ETemplateZoneType::HUMAN_START), baseSize(0), owner(0), - neutralTownsAreSameType(false), matchTerrainToTown(true) +CRmgTemplateZone::CRmgTemplateZone() : id(0), type(ETemplateZoneType::PLAYER_START), size(1), + townsAreSameType(false), matchTerrainToTown(true) { - + townTypes = getDefaultTownTypes(); + terrainTypes = getDefaultTerrainTypes(); } -TTemplateZoneId CTemplateZone::getId() const +TRmgTemplateZoneId CRmgTemplateZone::getId() const { return id; } -void CTemplateZone::setId(TTemplateZoneId value) +void CRmgTemplateZone::setId(TRmgTemplateZoneId value) { + if(value <= 0) throw std::runtime_error("Zone id should be greater than 0."); id = value; } -ETemplateZoneType::ETemplateZoneType CTemplateZone::getType() const +ETemplateZoneType::ETemplateZoneType CRmgTemplateZone::getType() const { return type; } -void CTemplateZone::setType(ETemplateZoneType::ETemplateZoneType value) +void CRmgTemplateZone::setType(ETemplateZoneType::ETemplateZoneType value) { type = value; } -int CTemplateZone::getBaseSize() const +int CRmgTemplateZone::getSize() const { - return baseSize; + return size; } -void CTemplateZone::setBaseSize(int value) +void CRmgTemplateZone::setSize(int value) { - assert(value >= 0); - baseSize = value; + if(value <= 0) throw std::runtime_error("Zone size needs to be greater than 0."); + size = value; } -int CTemplateZone::getOwner() const +boost::optional CRmgTemplateZone::getOwner() const { return owner; } -void CTemplateZone::setOwner(int value) +void CRmgTemplateZone::setOwner(boost::optional value) { + if(!(*value >= 0 && *value <= PlayerColor::PLAYER_LIMIT_I)) throw std::runtime_error("Owner has to be in range 0 to max player count."); owner = value; } -const CTemplateZoneTowns & CTemplateZone::getPlayerTowns() const +const CRmgTemplateZone::CTownInfo & CRmgTemplateZone::getPlayerTowns() const { return playerTowns; } -void CTemplateZone::setPlayerTowns(const CTemplateZoneTowns & value) +void CRmgTemplateZone::setPlayerTowns(const CTownInfo & value) { playerTowns = value; } -const CTemplateZoneTowns & CTemplateZone::getNeutralTowns() const +const CRmgTemplateZone::CTownInfo & CRmgTemplateZone::getNeutralTowns() const { return neutralTowns; } -void CTemplateZone::setNeutralTowns(const CTemplateZoneTowns & value) +void CRmgTemplateZone::setNeutralTowns(const CTownInfo & value) { neutralTowns = value; } -bool CTemplateZone::getNeutralTownsAreSameType() const +bool CRmgTemplateZone::getTownsAreSameType() const { - return neutralTownsAreSameType; + return townsAreSameType; } -void CTemplateZone::setNeutralTownsAreSameType(bool value) +void CRmgTemplateZone::setTownsAreSameType(bool value) { - neutralTownsAreSameType = value; + townsAreSameType = value; } -const std::set & CTemplateZone::getAllowedTownTypes() const +const std::set & CRmgTemplateZone::getTownTypes() const { - return allowedTownTypes; + return townTypes; } -void CTemplateZone::setAllowedTownTypes(const std::set & value) +void CRmgTemplateZone::setTownTypes(const std::set & value) { - allowedTownTypes = value; + townTypes = value; } -bool CTemplateZone::getMatchTerrainToTown() const +std::set CRmgTemplateZone::getDefaultTownTypes() const +{ + std::set defaultTowns; + auto towns = VLC->townh->getDefaultAllowed(); + for(int i = 0; i < towns.size(); ++i) + { + if(towns[i]) defaultTowns.insert(i); + } + return defaultTowns; +} + +bool CRmgTemplateZone::getMatchTerrainToTown() const { return matchTerrainToTown; } -void CTemplateZone::setMatchTerrainToTown(bool value) +void CRmgTemplateZone::setMatchTerrainToTown(bool value) { matchTerrainToTown = value; } -const std::set & CTemplateZone::getTerrainTypes() const +const std::set & CRmgTemplateZone::getTerrainTypes() const { return terrainTypes; } -void CTemplateZone::setTerrainTypes(const std::set & value) +void CRmgTemplateZone::setTerrainTypes(const std::set & value) { assert(value.find(ETerrainType::WRONG) == value.end() && value.find(ETerrainType::BORDER) == value.end() && - value.find(ETerrainType::WATER) == value.end()); + value.find(ETerrainType::WATER) == value.end() && value.find(ETerrainType::ROCK) == value.end()); terrainTypes = value; } -CTemplateZoneConnection::CTemplateZoneConnection() : zoneA(0), zoneB(0), guardStrength(0) +std::set CRmgTemplateZone::getDefaultTerrainTypes() const +{ + std::set terTypes; + static const ETerrainType::EETerrainType allowedTerTypes[] = { ETerrainType::DIRT, ETerrainType::SAND, ETerrainType::GRASS, ETerrainType::SNOW, + ETerrainType::SWAMP, ETerrainType::ROUGH, ETerrainType::SUBTERRANEAN, ETerrainType::LAVA }; + for(int i = 0; i < ARRAY_COUNT(allowedTerTypes); ++i) terTypes.insert(allowedTerTypes[i]); + return terTypes; +} + +boost::optional CRmgTemplateZone::getTerrainTypeLikeZone() const +{ + return terrainTypeLikeZone; +} + +void CRmgTemplateZone::setTerrainTypeLikeZone(boost::optional value) +{ + terrainTypeLikeZone = value; +} + +boost::optional CRmgTemplateZone::getTownTypeLikeZone() const +{ + return townTypeLikeZone; +} + +void CRmgTemplateZone::setTownTypeLikeZone(boost::optional value) +{ + townTypeLikeZone = value; +} + +CRmgTemplateZoneConnection::CRmgTemplateZoneConnection() : zoneA(0), zoneB(0), guardStrength(0) { } -TTemplateZoneId CTemplateZoneConnection::getZoneA() const +TRmgTemplateZoneId CRmgTemplateZoneConnection::getZoneA() const { return zoneA; } -void CTemplateZoneConnection::setZoneA(TTemplateZoneId value) +void CRmgTemplateZoneConnection::setZoneA(TRmgTemplateZoneId value) { zoneA = value; } -TTemplateZoneId CTemplateZoneConnection::getZoneB() const +TRmgTemplateZoneId CRmgTemplateZoneConnection::getZoneB() const { return zoneB; } -void CTemplateZoneConnection::setZoneB(TTemplateZoneId value) +void CRmgTemplateZoneConnection::setZoneB(TRmgTemplateZoneId value) { zoneB = value; } -int CTemplateZoneConnection::getGuardStrength() const +int CRmgTemplateZoneConnection::getGuardStrength() const { return guardStrength; } -void CTemplateZoneConnection::setGuardStrength(int value) +void CRmgTemplateZoneConnection::setGuardStrength(int value) { - assert(value >= 0); + if(value < 0) throw std::runtime_error("Negative value for guard strenth not allowed."); guardStrength = value; } -CRandomMapTemplateSize::CRandomMapTemplateSize() : width(CMapHeader::MAP_SIZE_MIDDLE), height(CMapHeader::MAP_SIZE_MIDDLE), under(true) +CRmgTemplate::CSize::CSize() : width(CMapHeader::MAP_SIZE_MIDDLE), height(CMapHeader::MAP_SIZE_MIDDLE), under(true) { } -CRandomMapTemplateSize::CRandomMapTemplateSize(int width, int height, bool under) : width(width), height(height), under(under) +CRmgTemplate::CSize::CSize(int width, int height, bool under) : under(under) { - + setWidth(width); + setHeight(height); } -int CRandomMapTemplateSize::getWidth() const +int CRmgTemplate::CSize::getWidth() const { return width; } -void CRandomMapTemplateSize::setWidth(int value) +void CRmgTemplate::CSize::setWidth(int value) { - assert(value >= 1); + if(value <= 0) throw std::runtime_error("Width > 0 failed."); width = value; } -int CRandomMapTemplateSize::getHeight() const +int CRmgTemplate::CSize::getHeight() const { return height; } -void CRandomMapTemplateSize::setHeight(int value) +void CRmgTemplate::CSize::setHeight(int value) { - assert(value >= 1); + if(value <= 0) throw std::runtime_error("Height > 0 failed."); height = value; } -bool CRandomMapTemplateSize::getUnder() const +bool CRmgTemplate::CSize::getUnder() const { return under; } -void CRandomMapTemplateSize::setUnder(bool value) +void CRmgTemplate::CSize::setUnder(bool value) { under = value; } -CRandomMapTemplate::CRandomMapTemplate() : minHumanCnt(1), maxHumanCnt(PlayerColor::PLAYER_LIMIT_I), minTotalCnt(2), - maxTotalCnt(PlayerColor::PLAYER_LIMIT_I) +bool CRmgTemplate::CSize::operator<=(const CSize & value) const { - minSize = CRandomMapTemplateSize(CMapHeader::MAP_SIZE_SMALL, CMapHeader::MAP_SIZE_SMALL, false); - maxSize = CRandomMapTemplateSize(CMapHeader::MAP_SIZE_XLARGE, CMapHeader::MAP_SIZE_XLARGE, true); + if(width < value.width && height < value.height) + { + return true; + } + else if(width == value.width && height == value.height) + { + return under ? value.under : true; + } + else + { + return false; + } } -const std::string & CRandomMapTemplate::getName() const +bool CRmgTemplate::CSize::operator>=(const CSize & value) const +{ + if(width > value.width && height > value.height) + { + return true; + } + else if(width == value.width && height == value.height) + { + return under ? true : !value.under; + } + else + { + return false; + } +} + +CRmgTemplate::CRmgTemplate() +{ + +} + +const std::string & CRmgTemplate::getName() const { return name; } -void CRandomMapTemplate::setName(const std::string & value) +void CRmgTemplate::setName(const std::string & value) { name = value; } -const CRandomMapTemplateSize & CRandomMapTemplate::getMinSize() const +const CRmgTemplate::CSize & CRmgTemplate::getMinSize() const { return minSize; } -void CRandomMapTemplate::setMinSize(const CRandomMapTemplateSize & value) +void CRmgTemplate::setMinSize(const CSize & value) { minSize = value; } -const CRandomMapTemplateSize & CRandomMapTemplate::getMaxSize() const +const CRmgTemplate::CSize & CRmgTemplate::getMaxSize() const { return maxSize; } -void CRandomMapTemplate::setMaxSize(const CRandomMapTemplateSize & value) +void CRmgTemplate::setMaxSize(const CSize & value) { maxSize = value; } -int CRandomMapTemplate::getMinHumanCnt() const +const CRmgTemplate::CPlayerCountRange & CRmgTemplate::getPlayers() const { - return minHumanCnt; + return players; } -void CRandomMapTemplate::setMinHumanCnt(int value) +void CRmgTemplate::setPlayers(const CPlayerCountRange & value) { - assert(value >= 1 && value <= PlayerColor::PLAYER_LIMIT_I); - minHumanCnt = value; + players = value; } -int CRandomMapTemplate::getMaxHumanCnt() const +const CRmgTemplate::CPlayerCountRange & CRmgTemplate::getCpuPlayers() const { - return maxHumanCnt; + return cpuPlayers; } -void CRandomMapTemplate::setMaxHumanCnt(int value) +void CRmgTemplate::setCpuPlayers(const CPlayerCountRange & value) { - assert(value >= 1 && value <= PlayerColor::PLAYER_LIMIT_I); - maxHumanCnt = value; + cpuPlayers = value; } -int CRandomMapTemplate::getMinTotalCnt() const -{ - return minTotalCnt; -} - -void CRandomMapTemplate::setMinTotalCnt(int value) -{ - assert(value >= 2 && value <= PlayerColor::PLAYER_LIMIT_I); - minTotalCnt = value; -} - -int CRandomMapTemplate::getMaxTotalCnt() const -{ - return maxTotalCnt; -} - -void CRandomMapTemplate::setMaxTotalCnt(int value) -{ - assert(value >= 2 && value <= PlayerColor::PLAYER_LIMIT_I); - maxTotalCnt = value; -} - -const std::map & CRandomMapTemplate::getZones() const +const std::map & CRmgTemplate::getZones() const { return zones; } -void CRandomMapTemplate::setZones(const std::map & value) +void CRmgTemplate::setZones(const std::map & value) { zones = value; } -const std::list & CRandomMapTemplate::getConnections() const +const std::list & CRmgTemplate::getConnections() const { return connections; } -void CRandomMapTemplate::setConnections(const std::list & value) +void CRmgTemplate::setConnections(const std::list & value) { connections = value; } -const std::map & CRmTemplateLoader::getTemplates() const +void CRmgTemplate::validate() const +{ + //TODO add some validation checks, throw on failure +} + +void CRmgTemplate::CPlayerCountRange::addRange(int lower, int upper) +{ + range.push_back(std::make_pair(lower, upper)); +} + +void CRmgTemplate::CPlayerCountRange::addNumber(int value) +{ + range.push_back(std::make_pair(value, value)); +} + +bool CRmgTemplate::CPlayerCountRange::isInRange(int count) const +{ + BOOST_FOREACH(const auto & pair, range) + { + if(count >= pair.first && count <= pair.second) return true; + } + return false; +} + +std::set CRmgTemplate::CPlayerCountRange::getNumbers() const +{ + std::set numbers; + BOOST_FOREACH(const auto & pair, range) + { + for(int i = pair.first; i <= pair.second; ++i) numbers.insert(i); + } + return numbers; +} + +const std::map & CRmgTemplateLoader::getTemplates() const { return templates; } -void CJsonRmTemplateLoader::loadTemplates() +void CJsonRmgTemplateLoader::loadTemplates() { const JsonNode rootNode(ResourceID("config/rmg.json")); BOOST_FOREACH(const auto & templatePair, rootNode.Struct()) { - CRandomMapTemplate tpl; - tpl.setName(templatePair.first); - const auto & templateNode = templatePair.second; - - // Parse main template data - tpl.setMinSize(parseMapTemplateSize(templateNode["minSize"].String())); - tpl.setMaxSize(parseMapTemplateSize(templateNode["maxSize"].String())); - tpl.setMinHumanCnt(templateNode["minHumanCnt"].Float()); - tpl.setMaxHumanCnt(templateNode["maxHumanCnt"].Float()); - tpl.setMinTotalCnt(templateNode["minTotalCnt"].Float()); - tpl.setMaxTotalCnt(templateNode["maxTotalCnt"].Float()); - - // Parse zones - std::map zones; - BOOST_FOREACH(const auto & zonePair, templateNode["zones"].Struct()) + CRmgTemplate tpl; + try { - CTemplateZone zone; - auto zoneId = boost::lexical_cast(zonePair.first); - zone.setId(zoneId); - const auto & zoneNode = zonePair.second; - zone.setType(getZoneType(zoneNode["type"].String())); - zone.setBaseSize(zoneNode["baseSize"].Float()); - zone.setOwner(zoneNode["owner"].Float()); - zone.setPlayerTowns(parseTemplateZoneTowns(zoneNode["playerTowns"])); - zone.setNeutralTowns(parseTemplateZoneTowns(zoneNode["neutralTowns"])); - zone.setAllowedTownTypes(getFactions(zoneNode["allowedTownTypes"].Vector())); - zone.setMatchTerrainToTown(zoneNode["matchTerrainToTown"].Bool()); - zone.setTerrainTypes(parseTerrainTypes(zoneNode["terrainTypes"].Vector())); - zone.setNeutralTownsAreSameType((zoneNode["neutralTownsAreSameType"].Bool())); - zones[zone.getId()] = zone; - } - tpl.setZones(zones); + tpl.setName(templatePair.first); + const auto & templateNode = templatePair.second; - // Parse connections - std::list connections; - BOOST_FOREACH(const auto & connPair, templateNode["connections"].Vector()) - { - CTemplateZoneConnection conn; - conn.setZoneA(boost::lexical_cast(connPair["a"].String())); - conn.setZoneB(boost::lexical_cast(connPair["b"].String())); - conn.setGuardStrength(connPair["guardStrength"].Float()); - connections.push_back(conn); + // Parse main template data + tpl.setMinSize(parseMapTemplateSize(templateNode["minSize"].String())); + tpl.setMaxSize(parseMapTemplateSize(templateNode["maxSize"].String())); + tpl.setPlayers(parsePlayers(templateNode["players"].String())); + tpl.setCpuPlayers(parsePlayers(templateNode["cpu"].String())); + + // Parse zones + std::map zones; + BOOST_FOREACH(const auto & zonePair, templateNode["zones"].Struct()) + { + CRmgTemplateZone zone; + auto zoneId = boost::lexical_cast(zonePair.first); + zone.setId(zoneId); + const auto & zoneNode = zonePair.second; + zone.setType(parseZoneType(zoneNode["type"].String())); + zone.setSize(zoneNode["size"].Float()); + if(!zoneNode["owner"].isNull()) zone.setOwner(zoneNode["owner"].Float()); + zone.setPlayerTowns(parseTemplateZoneTowns(zoneNode["playerTowns"])); + zone.setNeutralTowns(parseTemplateZoneTowns(zoneNode["neutralTowns"])); + zone.setTownTypes(parseTownTypes(zoneNode["townTypes"].Vector(), zone.getDefaultTownTypes())); + zone.setMatchTerrainToTown(zoneNode["matchTerrainToTown"].Bool()); + zone.setTerrainTypes(parseTerrainTypes(zoneNode["terrainTypes"].Vector(), zone.getDefaultTerrainTypes())); + zone.setTownsAreSameType((zoneNode["townsAreSameType"].Bool())); + if(!zoneNode["terrainTypeLikeZone"].isNull()) zone.setTerrainTypeLikeZone(boost::lexical_cast(zoneNode["terrainTypeLikeZone"].String())); + if(!zoneNode["townTypeLikeZone"].isNull()) zone.setTownTypeLikeZone(boost::lexical_cast(zoneNode["townTypeLikeZone"].String())); + zones[zone.getId()] = zone; + } + tpl.setZones(zones); + + // Parse connections + std::list connections; + BOOST_FOREACH(const auto & connPair, templateNode["connections"].Vector()) + { + CRmgTemplateZoneConnection conn; + conn.setZoneA(boost::lexical_cast(connPair["a"].String())); + conn.setZoneB(boost::lexical_cast(connPair["b"].String())); + conn.setGuardStrength(connPair["guard"].Float()); + connections.push_back(conn); + } + tpl.setConnections(connections); + tpl.validate(); + templates[tpl.getName()] = tpl; + } + catch(const std::exception & e) + { + logGlobal->errorStream() << boost::format("Template %s has errors. Message: %s.") % tpl.getName() % std::string(e.what()); } - tpl.setConnections(connections); - templates[tpl.getName()] = tpl; } } -CRandomMapTemplateSize CJsonRmTemplateLoader::parseMapTemplateSize(const std::string & text) const +CRmgTemplate::CSize CJsonRmgTemplateLoader::parseMapTemplateSize(const std::string & text) const { - CRandomMapTemplateSize size; + CRmgTemplate::CSize size; if(text.empty()) return size; std::vector parts; @@ -895,92 +1080,120 @@ CRandomMapTemplateSize CJsonRmTemplateLoader::parseMapTemplateSize(const std::st return size; } -ETemplateZoneType::ETemplateZoneType CJsonRmTemplateLoader::getZoneType(const std::string & type) const +ETemplateZoneType::ETemplateZoneType CJsonRmgTemplateLoader::parseZoneType(const std::string & type) const { static const std::map zoneTypeMapping = boost::assign::map_list_of - ("humanStart", ETemplateZoneType::HUMAN_START)("computerStart", ETemplateZoneType::COMPUTER_START) + ("playerStart", ETemplateZoneType::PLAYER_START)("cpuStart", ETemplateZoneType::CPU_START) ("treasure", ETemplateZoneType::TREASURE)("junction", ETemplateZoneType::JUNCTION); auto it = zoneTypeMapping.find(type); - assert(it != zoneTypeMapping.end()); + if(it == zoneTypeMapping.end()) throw std::runtime_error("Zone type unknown."); return it->second; } -CTemplateZoneTowns CJsonRmTemplateLoader::parseTemplateZoneTowns(const JsonNode & node) const +CRmgTemplateZone::CTownInfo CJsonRmgTemplateLoader::parseTemplateZoneTowns(const JsonNode & node) const { - CTemplateZoneTowns towns; - towns.setMinTowns(node["minTowns"].Float()); - towns.setMinCastles(node["minCastles"].Float()); + CRmgTemplateZone::CTownInfo towns; + towns.setTownCount(node["towns"].Float()); + towns.setCastleCount(node["castles"].Float()); towns.setTownDensity(node["townDensity"].Float()); towns.setCastleDensity(node["castleDensity"].Float()); return towns; } -std::set CJsonRmTemplateLoader::getFactions(const std::vector factionStrings) const +std::set CJsonRmgTemplateLoader::parseTownTypes(const JsonVector & townTypesVector, const std::set & defaultTownTypes) const { - std::set factions; - BOOST_FOREACH(const auto & factionNode, factionStrings) + std::set townTypes; + BOOST_FOREACH(const auto & townTypeNode, townTypesVector) { - auto factionStr = factionNode.String(); - if(factionStr == "all") - { - factions.clear(); - BOOST_FOREACH(auto factionPtr, VLC->townh->factions) - { - factions.insert(factionPtr->index); - } - return factions; - } + auto townTypeStr = townTypeNode.String(); + if(townTypeStr == "all") return defaultTownTypes; + + bool foundFaction = false; BOOST_FOREACH(auto factionPtr, VLC->townh->factions) { - if(factionStr == factionPtr->name) + if(factionPtr->town != nullptr && townTypeStr == factionPtr->name) { - factions.insert(factionPtr->index); - break; + townTypes.insert(factionPtr->index); + foundFaction = true; } } + if(!foundFaction) throw std::runtime_error("Given faction is invalid."); } - return factions; + return townTypes; } -std::set CJsonRmTemplateLoader::parseTerrainTypes(const std::vector terTypeStrings) const +std::set CJsonRmgTemplateLoader::parseTerrainTypes(const JsonVector & terTypeStrings, const std::set & defaultTerrainTypes) const { std::set terTypes; BOOST_FOREACH(const auto & node, terTypeStrings) { const auto & terTypeStr = node.String(); - if(terTypeStr == "all") + if(terTypeStr == "all") return defaultTerrainTypes; + auto pos = vstd::find_pos(GameConstants::TERRAIN_NAMES, terTypeStr); + if (pos != -1) { - for(int i = 0; i < GameConstants::TERRAIN_TYPES; ++i) terTypes.insert(ETerrainType(i)); - break; + terTypes.insert(ETerrainType(pos)); + } + else + { + throw std::runtime_error("Terrain type is invalid."); } - terTypes.insert(ETerrainType(vstd::find_pos(GameConstants::TERRAIN_NAMES, terTypeStr))); } return terTypes; } -boost::mutex CRandomMapTemplateStorage::smx; +CRmgTemplate::CPlayerCountRange CJsonRmgTemplateLoader::parsePlayers(const std::string & players) const +{ + CRmgTemplate::CPlayerCountRange playerRange; + if(players.empty()) + { + playerRange.addNumber(0); + return playerRange; + } + std::vector commaParts; + boost::split(commaParts, players, boost::is_any_of(",")); + BOOST_FOREACH(const auto & commaPart, commaParts) + { + std::vector rangeParts; + boost::split(rangeParts, commaPart, boost::is_any_of("-")); + if(rangeParts.size() == 2) + { + auto lower = boost::lexical_cast(rangeParts[0]); + auto upper = boost::lexical_cast(rangeParts[1]); + playerRange.addRange(lower, upper); + } + else if(rangeParts.size() == 1) + { + auto val = boost::lexical_cast(rangeParts.front()); + playerRange.addNumber(val); + } + } + return playerRange; +} -CRandomMapTemplateStorage & CRandomMapTemplateStorage::get() +boost::mutex CRmgTemplateStorage::smx; + +CRmgTemplateStorage & CRmgTemplateStorage::get() { TLockGuard _(smx); - static CRandomMapTemplateStorage storage; + static CRmgTemplateStorage storage; return storage; } -const std::map & CRandomMapTemplateStorage::getTemplates() const +const std::map & CRmgTemplateStorage::getTemplates() const { return templates; } -CRandomMapTemplateStorage::CRandomMapTemplateStorage() +CRmgTemplateStorage::CRmgTemplateStorage() { - auto jsonLoader = make_unique(); + auto jsonLoader = make_unique(); jsonLoader->loadTemplates(); const auto & tpls = jsonLoader->getTemplates(); templates.insert(tpls.begin(), tpls.end()); } -CRandomMapTemplateStorage::~CRandomMapTemplateStorage() +CRmgTemplateStorage::~CRmgTemplateStorage() { } diff --git a/lib/rmg/CMapGenerator.h b/lib/rmg/CMapGenerator.h index d9ff73591..68a307b4b 100644 --- a/lib/rmg/CMapGenerator.h +++ b/lib/rmg/CMapGenerator.h @@ -19,6 +19,166 @@ class CTerrainViewPatternConfig; class CMapEditManager; class JsonNode; +typedef std::vector JsonVector; + +namespace ETemplateZoneType +{ +enum ETemplateZoneType +{ + PLAYER_START, + CPU_START, + TREASURE, + JUNCTION +}; +} + +typedef int TRmgTemplateZoneId; + +/// The CRmgTemplateZone describes a zone in a template. +class DLL_LINKAGE CRmgTemplateZone +{ +public: + class DLL_LINKAGE CTownInfo + { + public: + CTownInfo(); + + int getTownCount() const; /// Default: 0 + void setTownCount(int value); + int getCastleCount() const; /// Default: 0 + void setCastleCount(int value); + int getTownDensity() const; /// Default: 0 + void setTownDensity(int value); + int getCastleDensity() const; /// Default: 0 + void setCastleDensity(int value); + + private: + int townCount, castleCount, townDensity, castleDensity; + }; + + CRmgTemplateZone(); + + TRmgTemplateZoneId getId() const; /// Default: 0 + void setId(TRmgTemplateZoneId value); + ETemplateZoneType::ETemplateZoneType getType() const; /// Default: ETemplateZoneType::PLAYER_START + void setType(ETemplateZoneType::ETemplateZoneType value); + int getSize() const; /// Default: 1 + void setSize(int value); + boost::optional getOwner() const; + void setOwner(boost::optional value); + const CTownInfo & getPlayerTowns() const; + void setPlayerTowns(const CTownInfo & value); + const CTownInfo & getNeutralTowns() const; + void setNeutralTowns(const CTownInfo & value); + bool getTownsAreSameType() const; /// Default: false + void setTownsAreSameType(bool value); + const std::set & getTownTypes() const; /// Default: all + void setTownTypes(const std::set & value); + std::set getDefaultTownTypes() const; + bool getMatchTerrainToTown() const; /// Default: true + void setMatchTerrainToTown(bool value); + const std::set & getTerrainTypes() const; /// Default: all + void setTerrainTypes(const std::set & value); + std::set getDefaultTerrainTypes() const; + boost::optional getTerrainTypeLikeZone() const; + void setTerrainTypeLikeZone(boost::optional value); + boost::optional getTownTypeLikeZone() const; + void setTownTypeLikeZone(boost::optional value); + +private: + TRmgTemplateZoneId id; + ETemplateZoneType::ETemplateZoneType type; + int size; + boost::optional owner; + CTownInfo playerTowns, neutralTowns; + bool townsAreSameType; + std::set townTypes; + bool matchTerrainToTown; + std::set terrainTypes; + boost::optional terrainTypeLikeZone, townTypeLikeZone; +}; + +/// The CRmgTemplateZoneConnection describes the connection between two zones. +class DLL_LINKAGE CRmgTemplateZoneConnection +{ +public: + CRmgTemplateZoneConnection(); + + TRmgTemplateZoneId getZoneA() const; /// Default: 0 + void setZoneA(TRmgTemplateZoneId value); + TRmgTemplateZoneId getZoneB() const; /// Default: 0 + void setZoneB(TRmgTemplateZoneId value); + int getGuardStrength() const; /// Default: 0 + void setGuardStrength(int value); + +private: + TRmgTemplateZoneId zoneA, zoneB; + int guardStrength; +}; + +/// The CRmgTemplate describes a random map template. +class DLL_LINKAGE CRmgTemplate +{ +public: + class CSize + { + public: + CSize(); + CSize(int width, int height, bool under); + + int getWidth() const; /// Default: CMapHeader::MAP_SIZE_MIDDLE + void setWidth(int value); + int getHeight() const; /// Default: CMapHeader::MAP_SIZE_MIDDLE + void setHeight(int value); + bool getUnder() const; /// Default: true + void setUnder(bool value); + bool operator<=(const CSize & value) const; + bool operator>=(const CSize & value) const; + + private: + int width, height; + bool under; + }; + + class CPlayerCountRange + { + public: + void addRange(int lower, int upper); + void addNumber(int value); + bool isInRange(int count) const; + std::set getNumbers() const; + + private: + std::list > range; + }; + + CRmgTemplate(); + + const std::string & getName() const; + void setName(const std::string & value); + const CSize & getMinSize() const; + void setMinSize(const CSize & value); + const CSize & getMaxSize() const; + void setMaxSize(const CSize & value); + const CPlayerCountRange & getPlayers() const; + void setPlayers(const CPlayerCountRange & value); + const CPlayerCountRange & getCpuPlayers() const; + void setCpuPlayers(const CPlayerCountRange & value); + const std::map & getZones() const; + void setZones(const std::map & value); + const std::list & getConnections() const; + void setConnections(const std::list & value); + + void validate() const; /// Tests template on validity and throws exception on failure + +private: + std::string name; + CSize minSize, maxSize; + CPlayerCountRange players, cpuPlayers; + std::map zones; + std::list connections; +}; + namespace EWaterContent { enum EWaterContent @@ -105,21 +265,21 @@ public: /// The count of the players ranging from 1 to PlayerColor::PLAYER_LIMIT or RANDOM_SIZE for random. If you call /// this method, all player settings are reset to default settings. - si8 getPlayersCnt() const; - void setPlayersCnt(si8 value); + si8 getPlayerCount() const; + void setPlayerCount(si8 value); /// The count of the teams ranging from 0 to or RANDOM_SIZE for random. - si8 getTeamsCnt() const; - void setTeamsCnt(si8 value); + si8 getTeamCount() const; + void setTeamCount(si8 value); /// The count of the computer only players ranging from 0 to or RANDOM_SIZE for random. /// If you call this method, all player settings are reset to default settings. - si8 getCompOnlyPlayersCnt() const; - void setCompOnlyPlayersCnt(si8 value); + si8 getCompOnlyPlayerCount() const; + void setCompOnlyPlayerCount(si8 value); /// The count of the computer only teams ranging from 0 to or RANDOM_SIZE for random. - si8 getCompOnlyTeamsCnt() const; - void setCompOnlyTeamsCnt(si8 value); + si8 getCompOnlyTeamCount() const; + void setCompOnlyTeamCount(si8 value); EWaterContent::EWaterContent getWaterContent() const; void setWaterContent(EWaterContent::EWaterContent value); @@ -132,37 +292,50 @@ public: const std::map & getPlayersSettings() const; void setStartingTownForPlayer(PlayerColor color, si32 town); /// Sets a player type for a standard player. A standard player is the opposite of a computer only player. The - /// values which can be chosen for the player type are EPlayerType::AI or EPlayerType::HUMAN. Calling this method - /// has no effect for the map itself, but it adds some informational text for the map description. + /// values which can be chosen for the player type are EPlayerType::AI or EPlayerType::HUMAN. void setPlayerTypeForStandardPlayer(PlayerColor color, EPlayerType::EPlayerType playerType); + /// The random map template to generate the map with or empty/not set if the template should be chosen randomly. + /// Default: Not set/random. + const CRmgTemplate * getMapTemplate() const; + void setMapTemplate(const CRmgTemplate * value); + + const std::map & getAvailableTemplates() const; + /// Finalizes the options. All random sizes for various properties will be overwritten by numbers from - /// a random number generator by keeping the options in a valid state. + /// a random number generator by keeping the options in a valid state. Check options should return true, otherwise + /// this function fails. void finalize(); void finalize(CRandomGenerator & gen); + /// Returns false if there is no template available which fits to the currently selected options. + bool checkOptions() const; + static const si8 RANDOM_SIZE = -1; private: void resetPlayersMap(); int countHumanPlayers() const; PlayerColor getNextPlayerColor() const; + void updateCompOnlyPlayers(); + void updatePlayers(); + const CRmgTemplate * getPossibleTemplate(CRandomGenerator & gen) const; si32 width, height; bool hasTwoLevels; - si8 playersCnt, teamsCnt, compOnlyPlayersCnt, compOnlyTeamsCnt; + si8 playerCount, teamCount, compOnlyPlayerCount, compOnlyTeamCount; EWaterContent::EWaterContent waterContent; EMonsterStrength::EMonsterStrength monsterStrength; std::map players; + const CRmgTemplate * mapTemplate; public: template 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 & players; + h & width & height & hasTwoLevels & playerCount & teamCount & compOnlyPlayerCount; + h & compOnlyTeamCount & waterContent & monsterStrength & players; + //TODO add name of template to class, enables selection of a template by a user } }; @@ -194,185 +367,45 @@ private: /* Implementation/Detail classes, Private API */ /* ---------------------------------------------------------------------------- */ -namespace ETemplateZoneType -{ -enum ETemplateZoneType -{ - HUMAN_START, - COMPUTER_START, - TREASURE, - JUNCTION -}; -} - -/// The CTemplateZoneTowns holds info about towns in a template zone. -class DLL_LINKAGE CTemplateZoneTowns +/// The CRmgTemplateLoader is a abstract base class for loading templates. +class DLL_LINKAGE CRmgTemplateLoader { public: - CTemplateZoneTowns(); - - int getMinTowns() const; /// Default: 0 - void setMinTowns(int value); - int getMinCastles() const; /// Default: 0 - void setMinCastles(int value); - int getTownDensity() const; /// Default: 0 - void setTownDensity(int value); - int getCastleDensity() const; /// Default: 0 - void setCastleDensity(int value); - -private: - int minTowns, minCastles, townDensity, castleDensity; -}; - -typedef int TTemplateZoneId; - -/// The CTemplateZone describes a zone in a template. -class DLL_LINKAGE CTemplateZone -{ -public: - CTemplateZone(); - - TTemplateZoneId getId() const; /// Default: 0 = not set; - void setId(TTemplateZoneId value); - ETemplateZoneType::ETemplateZoneType getType() const; /// Default: ETemplateZoneType::HUMAN_START - void setType(ETemplateZoneType::ETemplateZoneType value); - int getBaseSize() const; /// Default: 0 = not set; - void setBaseSize(int value); - int getOwner() const; /// Default: 0 = not set; - void setOwner(int value); - const CTemplateZoneTowns & getPlayerTowns() const; - void setPlayerTowns(const CTemplateZoneTowns & value); - const CTemplateZoneTowns & getNeutralTowns() const; - void setNeutralTowns(const CTemplateZoneTowns & value); - bool getNeutralTownsAreSameType() const; /// Default: false - void setNeutralTownsAreSameType(bool value); - const std::set & getAllowedTownTypes() const; - void setAllowedTownTypes(const std::set & value); - bool getMatchTerrainToTown() const; /// Default: false - void setMatchTerrainToTown(bool value); - const std::set & getTerrainTypes() const; - void setTerrainTypes(const std::set & value); - -private: - TTemplateZoneId id; - ETemplateZoneType::ETemplateZoneType type; - int baseSize; - int owner; - CTemplateZoneTowns playerTowns, neutralTowns; - bool neutralTownsAreSameType; - std::set allowedTownTypes; - bool matchTerrainToTown; - std::set terrainTypes; -}; - -/// The CTemplateZoneConnection describes the connection between two zones. -class DLL_LINKAGE CTemplateZoneConnection -{ -public: - CTemplateZoneConnection(); - - TTemplateZoneId getZoneA() const; /// Default: 0 = not set; - void setZoneA(TTemplateZoneId value); - TTemplateZoneId getZoneB() const; /// Default: 0 = not set; - void setZoneB(TTemplateZoneId value); - int getGuardStrength() const; /// Default: 0 - void setGuardStrength(int value); - -private: - TTemplateZoneId zoneA, zoneB; - int guardStrength; -}; - -/// The CRandomMapTemplateSize describes the dimensions of the template. -class CRandomMapTemplateSize -{ -public: - CRandomMapTemplateSize(); - CRandomMapTemplateSize(int width, int height, bool under); - - int getWidth() const; /// Default: CMapHeader::MAP_SIZE_MIDDLE - void setWidth(int value); - int getHeight() const; /// Default: CMapHeader::MAP_SIZE_MIDDLE - void setHeight(int value); - bool getUnder() const; /// Default: true - void setUnder(bool value); - -private: - int width, height; - bool under; -}; - -/// The CRandomMapTemplate describes a random map template. -class DLL_LINKAGE CRandomMapTemplate -{ -public: - CRandomMapTemplate(); - - const std::string & getName() const; - void setName(const std::string & value); - const CRandomMapTemplateSize & getMinSize() const; /// Default: CMapHeader::MAP_SIZE_SMALL x CMapHeader::MAP_SIZE_SMALL x wo under - void setMinSize(const CRandomMapTemplateSize & value); - const CRandomMapTemplateSize & getMaxSize() const; /// Default: CMapHeader::MAP_SIZE_XLARGE x CMapHeader::MAP_SIZE_XLARGE x under - void setMaxSize(const CRandomMapTemplateSize & value); - int getMinHumanCnt() const; /// Default: 1 - void setMinHumanCnt(int value); - int getMaxHumanCnt() const; /// Default: PlayerColor::PLAYER_LIMIT_I - void setMaxHumanCnt(int value); - int getMinTotalCnt() const; /// Default: 2 - void setMinTotalCnt(int value); - int getMaxTotalCnt() const; /// Default: PlayerColor::PLAYER_LIMIT_I - void setMaxTotalCnt(int value); - const std::map & getZones() const; - void setZones(const std::map & value); - const std::list & getConnections() const; - void setConnections(const std::list & value); - -private: - std::string name; - CRandomMapTemplateSize minSize, maxSize; - int minHumanCnt, maxHumanCnt, minTotalCnt, maxTotalCnt; - std::map zones; - std::list connections; -}; - -/// The CRmTemplateLoader is a abstract base class for loading templates. -class DLL_LINKAGE CRmTemplateLoader -{ -public: - virtual ~CRmTemplateLoader() { }; + virtual ~CRmgTemplateLoader() { }; virtual void loadTemplates() = 0; - const std::map & getTemplates() const; + const std::map & getTemplates() const; protected: - std::map templates; + std::map templates; }; -/// The CJsonRmTemplateLoader loads templates from a JSON file. -class DLL_LINKAGE CJsonRmTemplateLoader : public CRmTemplateLoader +/// The CJsonRmgTemplateLoader loads templates from a JSON file. +class DLL_LINKAGE CJsonRmgTemplateLoader : public CRmgTemplateLoader { public: void loadTemplates() override; private: - CRandomMapTemplateSize parseMapTemplateSize(const std::string & text) const; - CTemplateZoneTowns parseTemplateZoneTowns(const JsonNode & node) const; - ETemplateZoneType::ETemplateZoneType getZoneType(const std::string & type) const; - std::set getFactions(const std::vector factionStrings) const; - std::set parseTerrainTypes(const std::vector terTypeStrings) const; + CRmgTemplate::CSize parseMapTemplateSize(const std::string & text) const; + CRmgTemplateZone::CTownInfo parseTemplateZoneTowns(const JsonNode & node) const; + ETemplateZoneType::ETemplateZoneType parseZoneType(const std::string & type) const; + std::set parseTownTypes(const JsonVector & townTypesVector, const std::set & defaultTownTypes) const; + std::set parseTerrainTypes(const JsonVector & terTypeStrings, const std::set & defaultTerrainTypes) const; + CRmgTemplate::CPlayerCountRange parsePlayers(const std::string & players) const; }; -/// The CRandomMapTemplateStorage is a singleton object where templates are stored and which can be accessed from anywhere. -class DLL_LINKAGE CRandomMapTemplateStorage +/// The CRmgTemplateStorage is a singleton object where templates are stored and which can be accessed from anywhere. +class DLL_LINKAGE CRmgTemplateStorage { public: - static CRandomMapTemplateStorage & get(); + static CRmgTemplateStorage & get(); - const std::map & getTemplates() const; + const std::map & getTemplates() const; private: - CRandomMapTemplateStorage(); - ~CRandomMapTemplateStorage(); + CRmgTemplateStorage(); + ~CRmgTemplateStorage(); static boost::mutex smx; - std::map templates; /// Key: Template name + std::map templates; /// Key: Template name }; diff --git a/server/CVCMIServer.cpp b/server/CVCMIServer.cpp index 4262db095..ede287888 100644 --- a/server/CVCMIServer.cpp +++ b/server/CVCMIServer.cpp @@ -325,7 +325,7 @@ CGameHandler * CVCMIServer::initGhFromHostingConnection(CConnection &c) StartInfo si; c >> si; //get start options - if(!si.createRandomMap) + if(!si.createRandomMap()) { bool mapFound = CResourceHandler::get()->existsResource(ResourceID(si.mapname, EResType::MAP));