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

Merge remote-tracking branch 'vcmi/develop' into battle_improvements

This commit is contained in:
Ivan Savenko 2022-12-26 01:15:44 +02:00
commit bb65246aa3
32 changed files with 1714 additions and 253 deletions

View File

@ -244,7 +244,7 @@ jobs:
- name: Trigger Android
uses: peter-evans/repository-dispatch@v1
if: ${{ (github.ref == 'refs/heads/develop' || github.ref == 'refs/heads/master') && matrix.platform == 'mxe' }}
if: ${{ (github.ref == 'refs/heads/develop' || github.ref == 'refs/heads/beta' || github.ref == 'refs/heads/master') && matrix.platform == 'mxe' }}
with:
token: ${{ secrets.VCMI_ANDROID_ACCESS_TOKEN }}
repository: vcmi/vcmi-android

View File

@ -1,7 +1,8 @@
[![GitHub](https://github.com/vcmi/vcmi/actions/workflows/github.yml/badge.svg)](https://github.com/vcmi/vcmi/actions/workflows/github.yml)
[![AppVeyor Build status](https://ci.appveyor.com/api/projects/status/github/vcmi/vcmi?branch=develop&svg=true)](https://ci.appveyor.com/project/vcmi/vcmi)
[![Coverity Scan Build Status](https://scan.coverity.com/projects/vcmi/badge.svg)](https://scan.coverity.com/projects/vcmi)
[![Github Downloads](https://img.shields.io/github/downloads/vcmi/vcmi/1.0.0/total)](https://github.com/vcmi/vcmi/releases/tag/1.0.0)
[![Github Downloads](https://img.shields.io/github/downloads/vcmi/vcmi/1.1.0/total)](https://github.com/vcmi/vcmi/releases/tag/1.1.0)
[![Github Downloads](https://img.shields.io/github/downloads/vcmi/vcmi/total)](https://github.com/vcmi/vcmi/releases)
# VCMI Project
VCMI is work-in-progress attempt to recreate engine for Heroes III, giving it new and extended possibilities.

View File

@ -26,6 +26,7 @@ set(client_SRCS
gui/Geometries.cpp
gui/SDL_Extensions.cpp
gui/NotificationHandler.cpp
gui/InterfaceObjectConfigurable.cpp
widgets/AdventureMapClasses.cpp
widgets/Buttons.cpp
@ -113,6 +114,7 @@ set(client_HEADERS
gui/SDL_Extensions.h
gui/SDL_Pixels.h
gui/NotificationHandler.h
gui/InterfaceObjectConfigurable.h
widgets/AdventureMapClasses.h
widgets/Buttons.h

View File

@ -165,6 +165,7 @@ struct SSetCaptureState
};
#define OBJ_CONSTRUCTION SObjectConstruction obj__i(this)
#define OBJ_CONSTRUCTION_TARGETED(obj) SObjectConstruction obj__i(obj)
#define OBJECT_CONSTRUCTION_CAPTURING(actions) defActions = actions; SSetCaptureState obj__i1(true, actions); SObjectConstruction obj__i(this)
#define OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(actions) SSetCaptureState obj__i1(true, actions); SObjectConstruction obj__i(this)

View File

@ -0,0 +1,389 @@
/*
* InterfaceBuilder.cpp, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#include "StdInc.h"
#include "InterfaceObjectConfigurable.h"
#include "../CGameInfo.h"
#include "../gui/CAnimation.h"
#include "../gui/CGuiHandler.h"
#include "../widgets/CComponent.h"
#include "../widgets/Buttons.h"
#include "../widgets/MiscWidgets.h"
#include "../widgets/ObjectLists.h"
#include "../widgets/TextControls.h"
#include "../windows/GUIClasses.h"
#include "../windows/InfoWindows.h"
#include "../../lib/CGeneralTextHandler.h"
InterfaceObjectConfigurable::InterfaceObjectConfigurable(const JsonNode & config, int used, Point offset):
InterfaceObjectConfigurable(used, offset)
{
init(config);
}
InterfaceObjectConfigurable::InterfaceObjectConfigurable(int used, Point offset):
CIntObject(used, offset)
{
REGISTER_BUILDER("picture", &InterfaceObjectConfigurable::buildPicture);
REGISTER_BUILDER("image", &InterfaceObjectConfigurable::buildImage);
REGISTER_BUILDER("texture", &InterfaceObjectConfigurable::buildTexture);
REGISTER_BUILDER("animation", &InterfaceObjectConfigurable::buildAnimation);
REGISTER_BUILDER("label", &InterfaceObjectConfigurable::buildLabel);
REGISTER_BUILDER("toggleGroup", &InterfaceObjectConfigurable::buildToggleGroup);
REGISTER_BUILDER("toggleButton", &InterfaceObjectConfigurable::buildToggleButton);
REGISTER_BUILDER("button", &InterfaceObjectConfigurable::buildButton);
REGISTER_BUILDER("labelGroup", &InterfaceObjectConfigurable::buildLabelGroup);
REGISTER_BUILDER("slider", &InterfaceObjectConfigurable::buildSlider);
}
void InterfaceObjectConfigurable::registerBuilder(const std::string & type, BuilderFunction f)
{
builders[type] = f;
}
void InterfaceObjectConfigurable::addCallback(const std::string & callbackName, std::function<void(int)> callback)
{
callbacks[callbackName] = callback;
}
void InterfaceObjectConfigurable::init(const JsonNode &config)
{
OBJ_CONSTRUCTION;
logGlobal->debug("Building configurable interface object");
for(auto & item : config["variables"].Struct())
{
logGlobal->debug("Read variable named %s", item.first);
variables[item.first] = item.second;
}
int unnamedObjectId = 0;
const std::string unnamedObjectPrefix = "__widget_";
for(const auto & item : config["items"].Vector())
{
std::string name = item["name"].isNull()
? unnamedObjectPrefix + std::to_string(unnamedObjectId++)
: item["name"].String();
logGlobal->debug("Building widget with name %s", name);
widgets[name] = buildWidget(item);
}
}
std::string InterfaceObjectConfigurable::readText(const JsonNode & config) const
{
if(config.isNull())
return "";
if(config.isNumber())
{
logGlobal->debug("Reading text from generaltext handler id:%d", config.Integer());
return CGI->generaltexth->allTexts[config.Integer()];
}
const std::string delimiter = "/";
std::string s = config.String();
logGlobal->debug("Reading text from translations by key: %s", s);
JsonNode translated = CGI->generaltexth->localizedTexts;
for(size_t p = s.find(delimiter); p != std::string::npos; p = s.find(delimiter))
{
translated = translated[s.substr(0, p)];
s.erase(0, p + delimiter.length());
}
if(s == config.String())
{
logGlobal->warn("Reading non-translated text: %s", s);
return s;
}
return translated[s].String();
}
Point InterfaceObjectConfigurable::readPosition(const JsonNode & config) const
{
Point p;
logGlobal->debug("Reading point");
p.x = config["x"].Integer();
p.y = config["y"].Integer();
return p;
}
Rect InterfaceObjectConfigurable::readRect(const JsonNode & config) const
{
Rect p;
logGlobal->debug("Reading rect");
p.x = config["x"].Integer();
p.y = config["y"].Integer();
p.w = config["w"].Integer();
p.h = config["h"].Integer();
return p;
}
ETextAlignment InterfaceObjectConfigurable::readTextAlignment(const JsonNode & config) const
{
logGlobal->debug("Reading text alignment");
if(!config.isNull())
{
if(config.String() == "center")
return ETextAlignment::CENTER;
if(config.String() == "left")
return ETextAlignment::TOPLEFT;
if(config.String() == "right")
return ETextAlignment::BOTTOMRIGHT;
}
logGlobal->debug("Uknown text alignment attribute");
return ETextAlignment::CENTER;
}
SDL_Color InterfaceObjectConfigurable::readColor(const JsonNode & config) const
{
logGlobal->debug("Reading color");
if(!config.isNull())
{
if(config.String() == "yellow")
return Colors::YELLOW;
if(config.String() == "white")
return Colors::WHITE;
if(config.String() == "gold")
return Colors::METALLIC_GOLD;
if(config.String() == "green")
return Colors::GREEN;
if(config.String() == "orange")
return Colors::ORANGE;
if(config.String() == "bright-yellow")
return Colors::BRIGHT_YELLOW;
}
logGlobal->debug("Uknown color attribute");
return Colors::DEFAULT_KEY_COLOR;
}
EFonts InterfaceObjectConfigurable::readFont(const JsonNode & config) const
{
logGlobal->debug("Reading font");
if(!config.isNull())
{
if(config.String() == "big")
return EFonts::FONT_BIG;
if(config.String() == "medium")
return EFonts::FONT_MEDIUM;
if(config.String() == "small")
return EFonts::FONT_SMALL;
if(config.String() == "tiny")
return EFonts::FONT_TINY;
}
logGlobal->debug("Uknown font attribute");
return EFonts::FONT_TIMES;
}
std::pair<std::string, std::string> InterfaceObjectConfigurable::readHintText(const JsonNode & config) const
{
logGlobal->debug("Reading hint text");
std::pair<std::string, std::string> result;
if(!config.isNull())
{
if(config.isNumber())
{
logGlobal->debug("Reading hint text (zelp) from generaltext handler id:%d", config.Integer());
return CGI->generaltexth->zelp[config.Integer()];
}
if(config.getType() == JsonNode::JsonType::DATA_STRUCT)
{
result.first = readText(config["hover"]);
result.second = readText(config["help"]);
return result;
}
if(config.getType() == JsonNode::JsonType::DATA_STRING)
{
logGlobal->debug("Reading non-translated hint: %s", config.String());
result.first = result.second = config.String();
}
}
return result;
}
std::shared_ptr<CPicture> InterfaceObjectConfigurable::buildPicture(const JsonNode & config) const
{
logGlobal->debug("Building widget CPicture");
auto image = config["image"].String();
auto position = readPosition(config["position"]);
auto pic = std::make_shared<CPicture>(image, position.x, position.y);
if(!config["visible"].isNull())
pic->visible = config["visible"].Bool();
return pic;
}
std::shared_ptr<CLabel> InterfaceObjectConfigurable::buildLabel(const JsonNode & config) const
{
logGlobal->debug("Building widget CLabel");
auto font = readFont(config["font"]);
auto alignment = readTextAlignment(config["alignment"]);
auto color = readColor(config["color"]);
auto text = readText(config["text"]);
auto position = readPosition(config["position"]);
return std::make_shared<CLabel>(position.x, position.y, font, alignment, color, text);
}
std::shared_ptr<CToggleGroup> InterfaceObjectConfigurable::buildToggleGroup(const JsonNode & config) const
{
logGlobal->debug("Building widget CToggleGroup");
auto position = readPosition(config["position"]);
auto group = std::make_shared<CToggleGroup>(0);
group->pos += position;
if(!config["items"].isNull())
{
OBJ_CONSTRUCTION_TARGETED(group.get());
int itemIdx = -1;
for(const auto & item : config["items"].Vector())
{
itemIdx = item["index"].isNull() ? itemIdx + 1 : item["index"].Integer();
group->addToggle(itemIdx, std::dynamic_pointer_cast<CToggleBase>(buildWidget(item)));
}
}
if(!config["selected"].isNull())
group->setSelected(config["selected"].Integer());
if(!config["callback"].isNull())
group->addCallback(callbacks.at(config["callback"].String()));
return group;
}
std::shared_ptr<CToggleButton> InterfaceObjectConfigurable::buildToggleButton(const JsonNode & config) const
{
logGlobal->debug("Building widget CToggleButton");
auto position = readPosition(config["position"]);
auto image = config["image"].String();
auto zelp = readHintText(config["zelp"]);
auto button = std::make_shared<CToggleButton>(position, image, zelp);
if(!config["selected"].isNull())
button->setSelected(config["selected"].Bool());
if(!config["imageOrder"].isNull())
{
auto imgOrder = config["imageOrder"].Vector();
assert(imgOrder.size() >= 4);
button->setImageOrder(imgOrder[0].Integer(), imgOrder[1].Integer(), imgOrder[2].Integer(), imgOrder[3].Integer());
}
if(!config["callback"].isNull())
button->addCallback(callbacks.at(config["callback"].String()));
return button;
}
std::shared_ptr<CButton> InterfaceObjectConfigurable::buildButton(const JsonNode & config) const
{
logGlobal->debug("Building widget CButton");
auto position = readPosition(config["position"]);
auto image = config["image"].String();
auto zelp = readHintText(config["zelp"]);
auto button = std::make_shared<CButton>(position, image, zelp);
if(!config["items"].isNull())
{
for(const auto & item : config["items"].Vector())
{
button->addOverlay(buildWidget(item));
}
}
if(!config["callback"].isNull())
button->addCallback(std::bind(callbacks.at(config["callback"].String()), 0));
return button;
}
std::shared_ptr<CLabelGroup> InterfaceObjectConfigurable::buildLabelGroup(const JsonNode & config) const
{
logGlobal->debug("Building widget CLabelGroup");
auto font = readFont(config["font"]);
auto alignment = readTextAlignment(config["alignment"]);
auto color = readColor(config["color"]);
auto group = std::make_shared<CLabelGroup>(font, alignment, color);
if(!config["items"].isNull())
{
for(const auto & item : config["items"].Vector())
{
auto position = readPosition(item["position"]);
auto text = readText(item["text"]);
group->add(position.x, position.y, text);
}
}
return group;
}
std::shared_ptr<CSlider> InterfaceObjectConfigurable::buildSlider(const JsonNode & config) const
{
logGlobal->debug("Building widget CSlider");
auto position = readPosition(config["position"]);
int length = config["size"].Integer();
auto style = config["style"].String() == "brown" ? CSlider::BROWN : CSlider::BLUE;
auto itemsVisible = config["itemsVisible"].Integer();
auto itemsTotal = config["itemsTotal"].Integer();
auto value = config["selected"].Integer();
bool horizontal = config["orientation"].String() == "horizontal";
return std::make_shared<CSlider>(position, length, callbacks.at(config["callback"].String()), itemsVisible, itemsTotal, value, horizontal, style);
}
std::shared_ptr<CAnimImage> InterfaceObjectConfigurable::buildImage(const JsonNode & config) const
{
logGlobal->debug("Building widget CAnimImage");
auto position = readPosition(config["position"]);
auto image = config["image"].String();
int group = config["group"].isNull() ? 0 : config["group"].Integer();
int frame = config["frame"].isNull() ? 0 : config["frame"].Integer();
return std::make_shared<CAnimImage>(image, frame, group, position.x, position.y);
}
std::shared_ptr<CFilledTexture> InterfaceObjectConfigurable::buildTexture(const JsonNode & config) const
{
logGlobal->debug("Building widget CFilledTexture");
auto image = config["image"].String();
auto rect = readRect(config["rect"]);
return std::make_shared<CFilledTexture>(image, rect);
}
std::shared_ptr<CShowableAnim> InterfaceObjectConfigurable::buildAnimation(const JsonNode & config) const
{
logGlobal->debug("Building widget CShowableAnim");
auto position = readPosition(config["position"]);
auto image = config["image"].String();
ui8 flags = 0;
if(!config["repeat"].Bool())
flags |= CShowableAnim::EFlags::PLAY_ONCE;
int group = config["group"].isNull() ? 0 : config["group"].Integer();
auto anim = std::make_shared<CShowableAnim>(position.x, position.y, image, flags, 4, group);
if(!config["alpha"].isNull())
anim->setAlpha(config["alpha"].Integer());
if(!config["callback"].isNull())
anim->callback = std::bind(callbacks.at(config["callback"].String()), 0);
if(!config["frames"].isNull())
{
auto b = config["frames"]["start"].Integer();
auto e = config["frames"]["end"].Integer();
anim->set(group, b, e);
}
return anim;
}
std::shared_ptr<CIntObject> InterfaceObjectConfigurable::buildWidget(JsonNode config) const
{
assert(!config.isNull());
logGlobal->debug("Building widget from config");
//overrides from variables
for(auto & item : config["overrides"].Struct())
{
logGlobal->debug("Config attribute %s was overriden by variable %s", item.first, item.second.String());
config[item.first] = variables[item.second.String()];
}
auto type = config["type"].String();
auto buildIterator = builders.find(type);
if(buildIterator != builders.end())
return (buildIterator->second)(config);
logGlobal->error("Builder with type %s is not registered", type);
return nullptr;
}

View File

@ -0,0 +1,85 @@
/*
* InterfaceBuilder.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 "CIntObject.h"
#include "../../lib/JsonNode.h"
class CPicture;
class CLabel;
class CToggleGroup;
class CToggleButton;
class CButton;
class CLabelGroup;
class CSlider;
class CAnimImage;
class CShowableAnim;
class CFilledTexture;
#define REGISTER_BUILDER(type, method) registerBuilder(type, std::bind(method, this, std::placeholders::_1))
class InterfaceObjectConfigurable: public CIntObject
{
public:
InterfaceObjectConfigurable(int used=0, Point offset=Point());
InterfaceObjectConfigurable(const JsonNode & config, int used=0, Point offset=Point());
protected:
using BuilderFunction = std::function<std::shared_ptr<CIntObject>(const JsonNode &)>;
void registerBuilder(const std::string &, BuilderFunction);
//must be called after adding callbacks
void init(const JsonNode & config);
void addCallback(const std::string & callbackName, std::function<void(int)> callback);
JsonNode variables;
template<class T>
const std::shared_ptr<T> widget(const std::string & name) const
{
auto iter = widgets.find(name);
if(iter == widgets.end())
return nullptr;
return std::dynamic_pointer_cast<T>(iter->second);
}
//basic serializers
Point readPosition(const JsonNode &) const;
Rect readRect(const JsonNode &) const;
ETextAlignment readTextAlignment(const JsonNode &) const;
SDL_Color readColor(const JsonNode &) const;
EFonts readFont(const JsonNode &) const;
std::string readText(const JsonNode &) const;
std::pair<std::string, std::string> readHintText(const JsonNode &) const;
//basic widgets
std::shared_ptr<CPicture> buildPicture(const JsonNode &) const;
std::shared_ptr<CLabel> buildLabel(const JsonNode &) const;
std::shared_ptr<CToggleGroup> buildToggleGroup(const JsonNode &) const;
std::shared_ptr<CToggleButton> buildToggleButton(const JsonNode &) const;
std::shared_ptr<CButton> buildButton(const JsonNode &) const;
std::shared_ptr<CLabelGroup> buildLabelGroup(const JsonNode &) const;
std::shared_ptr<CSlider> buildSlider(const JsonNode &) const;
std::shared_ptr<CAnimImage> buildImage(const JsonNode &) const;
std::shared_ptr<CShowableAnim> buildAnimation(const JsonNode &) const;
std::shared_ptr<CFilledTexture> buildTexture(const JsonNode &) const;
//composite widgets
std::shared_ptr<CIntObject> buildWidget(JsonNode config) const;
private:
std::map<std::string, BuilderFunction> builders;
std::map<std::string, std::shared_ptr<CIntObject>> widgets;
std::map<std::string, std::function<void(int)>> callbacks;
};

View File

@ -1,5 +1,5 @@
/*
* NotificationHandler.cpp, part of VCMI engine
* NotificationHandler.h, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*

View File

@ -27,127 +27,68 @@
#include "../../lib/CGeneralTextHandler.h"
#include "../../lib/mapping/CMapInfo.h"
#include "../../lib/rmg/CMapGenOptions.h"
#include "../../lib/CModHandler.h"
#include "../../lib/rmg/CRmgTemplateStorage.h"
RandomMapTab::RandomMapTab()
RandomMapTab::RandomMapTab():
InterfaceObjectConfigurable()
{
recActions = 0;
mapGenOptions = std::make_shared<CMapGenOptions>();
OBJ_CONSTRUCTION;
background = std::make_shared<CPicture>("RANMAPBK", 0, 6);
labelHeadlineBig = std::make_shared<CLabel>(222, 36, FONT_BIG, ETextAlignment::CENTER, Colors::YELLOW, CGI->generaltexth->allTexts[738]);
labelHeadlineSmall = std::make_shared<CLabel>(222, 56, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE, CGI->generaltexth->allTexts[739]);
labelMapSize = std::make_shared<CLabel>(104, 97, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE, CGI->generaltexth->allTexts[752]);
groupMapSize = std::make_shared<CToggleGroup>(0);
groupMapSize->pos.y += 81;
groupMapSize->pos.x += 158;
const std::vector<std::string> mapSizeBtns = {"RANSIZS", "RANSIZM", "RANSIZL", "RANSIZX"};
addButtonsToGroup(groupMapSize.get(), mapSizeBtns, 0, 3, 47, 198);
groupMapSize->setSelected(1);
groupMapSize->addCallback([&](int btnId)
const JsonNode config(ResourceID("config/widgets/randomMapTab.json"));
addCallback("toggleMapSize", [&](int btnId)
{
auto mapSizeVal = getPossibleMapSizes();
mapGenOptions->setWidth(mapSizeVal[btnId]);
mapGenOptions->setHeight(mapSizeVal[btnId]);
if(mapGenOptions->getMapTemplate())
if(!mapGenOptions->getMapTemplate()->matchesSize(int3{mapGenOptions->getWidth(), mapGenOptions->getHeight(), 1 + mapGenOptions->getHasTwoLevels()}))
setTemplate(nullptr);
updateMapInfoByHost();
});
buttonTwoLevels = std::make_shared<CToggleButton>(Point(346, 81), "RANUNDR", CGI->generaltexth->zelp[202]);
buttonTwoLevels->setSelected(true);
buttonTwoLevels->addCallback([&](bool on)
addCallback("toggleTwoLevels", [&](bool on)
{
mapGenOptions->setHasTwoLevels(on);
if(mapGenOptions->getMapTemplate())
if(!mapGenOptions->getMapTemplate()->matchesSize(int3{mapGenOptions->getWidth(), mapGenOptions->getHeight(), 1 + mapGenOptions->getHasTwoLevels()}))
setTemplate(nullptr);
updateMapInfoByHost();
});
labelGroupForOptions = std::make_shared<CLabelGroup>(FONT_SMALL, ETextAlignment::TOPLEFT, Colors::WHITE);
// Create number defs list
std::vector<std::string> numberDefs;
for(int i = 0; i <= 8; ++i)
{
numberDefs.push_back("RANNUM" + boost::lexical_cast<std::string>(i));
}
const int NUMBERS_WIDTH = 32;
const int BTNS_GROUP_LEFT_MARGIN = 67;
labelGroupForOptions->add(68, 133, CGI->generaltexth->allTexts[753]);
groupMaxPlayers = std::make_shared<CToggleGroup>(0);
groupMaxPlayers->pos.y += 153;
groupMaxPlayers->pos.x += BTNS_GROUP_LEFT_MARGIN;
addButtonsWithRandToGroup(groupMaxPlayers.get(), numberDefs, 1, 8, NUMBERS_WIDTH, 204, 212);
groupMaxPlayers->addCallback([&](int btnId)
addCallback("setPlayersCount", [&](int btnId)
{
mapGenOptions->setPlayerCount(btnId);
deactivateButtonsFrom(groupMaxTeams.get(), btnId);
// deactive some CompOnlyPlayers buttons to prevent total number of players exceeds PlayerColor::PLAYER_LIMIT_I
deactivateButtonsFrom(groupCompOnlyPlayers.get(), PlayerColor::PLAYER_LIMIT_I - btnId + 1);
validatePlayersCnt(btnId);
setMapGenOptions(mapGenOptions);
updateMapInfoByHost();
});
labelGroupForOptions->add(68, 199, CGI->generaltexth->allTexts[754]);
groupMaxTeams = std::make_shared<CToggleGroup>(0);
groupMaxTeams->pos.y += 219;
groupMaxTeams->pos.x += BTNS_GROUP_LEFT_MARGIN;
addButtonsWithRandToGroup(groupMaxTeams.get(), numberDefs, 0, 7, NUMBERS_WIDTH, 214, 222);
groupMaxTeams->addCallback([&](int btnId)
addCallback("setTeamsCount", [&](int btnId)
{
mapGenOptions->setTeamCount(btnId);
updateMapInfoByHost();
});
labelGroupForOptions->add(68, 265, CGI->generaltexth->allTexts[755]);
groupCompOnlyPlayers = std::make_shared<CToggleGroup>(0);
groupCompOnlyPlayers->pos.y += 285;
groupCompOnlyPlayers->pos.x += BTNS_GROUP_LEFT_MARGIN;
addButtonsWithRandToGroup(groupCompOnlyPlayers.get(), numberDefs, 0, 7, NUMBERS_WIDTH, 224, 232);
groupCompOnlyPlayers->addCallback([&](int btnId)
addCallback("setCompOnlyPlayers", [&](int btnId)
{
mapGenOptions->setCompOnlyPlayerCount(btnId);
// deactive some MaxPlayers buttons to prevent total number of players exceeds PlayerColor::PLAYER_LIMIT_I
deactivateButtonsFrom(groupMaxPlayers.get(), PlayerColor::PLAYER_LIMIT_I - btnId + 1);
deactivateButtonsFrom(groupCompOnlyTeams.get(), (btnId == 0 ? 1 : btnId));
validateCompOnlyPlayersCnt(btnId);
setMapGenOptions(mapGenOptions);
updateMapInfoByHost();
});
labelGroupForOptions->add(68, 331, CGI->generaltexth->allTexts[756]);
groupCompOnlyTeams = std::make_shared<CToggleGroup>(0);
groupCompOnlyTeams->pos.y += 351;
groupCompOnlyTeams->pos.x += BTNS_GROUP_LEFT_MARGIN;
addButtonsWithRandToGroup(groupCompOnlyTeams.get(), numberDefs, 0, 6, NUMBERS_WIDTH, 234, 241);
deactivateButtonsFrom(groupCompOnlyTeams.get(), 1);
groupCompOnlyTeams->addCallback([&](int btnId)
addCallback("setCompOnlyTeams", [&](int btnId)
{
mapGenOptions->setCompOnlyTeamCount(btnId);
updateMapInfoByHost();
});
labelGroupForOptions->add(68, 398, CGI->generaltexth->allTexts[757]);
const int WIDE_BTN_WIDTH = 85;
groupWaterContent = std::make_shared<CToggleGroup>(0);
groupWaterContent->pos.y += 419;
groupWaterContent->pos.x += BTNS_GROUP_LEFT_MARGIN;
const std::vector<std::string> waterContentBtns = {"RANNONE", "RANNORM", "RANISLD"};
addButtonsWithRandToGroup(groupWaterContent.get(), waterContentBtns, 0, 2, WIDE_BTN_WIDTH, 243, 246);
groupWaterContent->addCallback([&](int btnId)
addCallback("setWaterContent", [&](int btnId)
{
mapGenOptions->setWaterContent(static_cast<EWaterContent::EWaterContent>(btnId));
updateMapInfoByHost();
});
labelGroupForOptions->add(68, 465, CGI->generaltexth->allTexts[758]);
groupMonsterStrength = std::make_shared<CToggleGroup>(0);
groupMonsterStrength->pos.y += 485;
groupMonsterStrength->pos.x += BTNS_GROUP_LEFT_MARGIN;
const std::vector<std::string> monsterStrengthBtns = {"RANWEAK", "RANNORM", "RANSTRG"};
addButtonsWithRandToGroup(groupMonsterStrength.get(), monsterStrengthBtns, 2, 4, WIDE_BTN_WIDTH, 248, 251, EMonsterStrength::RANDOM, false);
groupMonsterStrength->addCallback([&](int btnId)
addCallback("setMonsterStrength", [&](int btnId)
{
if(btnId < 0)
mapGenOptions->setMonsterStrength(EMonsterStrength::RANDOM);
@ -155,9 +96,31 @@ RandomMapTab::RandomMapTab()
mapGenOptions->setMonsterStrength(static_cast<EMonsterStrength::EMonsterStrength>(btnId)); //value 2 to 4
updateMapInfoByHost();
});
buttonShowRandomMaps = std::make_shared<CButton>(Point(54, 535), "RANSHOW", CGI->generaltexth->zelp[252]);
//new callbacks available only from mod
addCallback("templateSelection", [&](int)
{
GH.pushIntT<TemplatesDropBox>(*this, int3{mapGenOptions->getWidth(), mapGenOptions->getHeight(), 1 + mapGenOptions->getHasTwoLevels()});
});
addCallback("teamAlignments", [&](int)
{
GH.pushIntT<TeamAlignmentsWidget>(*this);
});
for(auto road : VLC->terrainTypeHandler->roads())
{
std::string cbRoadType = "selectRoad_" + road.name;
addCallback(cbRoadType, [&, road](bool on)
{
mapGenOptions->setRoadEnabled(road.name, on);
updateMapInfoByHost();
});
}
init(config);
updateMapInfoByHost();
}
@ -192,6 +155,7 @@ void RandomMapTab::updateMapInfoByHost()
mapInfo->mapHeader->howManyTeams = playersToGen;
std::set<TeamID> occupiedTeams;
for(int i = 0; i < playersToGen; ++i)
{
PlayerInfo player;
@ -205,60 +169,153 @@ void RandomMapTab::updateMapInfoByHost()
{
player.canHumanPlay = true;
}
player.team = TeamID(i);
auto team = mapGenOptions->getPlayersSettings().at(PlayerColor(i)).getTeam();
player.team = team;
occupiedTeams.insert(team);
player.hasMainTown = true;
player.generateHeroAtMainTown = true;
mapInfo->mapHeader->players.push_back(player);
}
for(auto & player : mapInfo->mapHeader->players)
{
for(int i = 0; player.team == TeamID::NO_TEAM; ++i)
{
TeamID team(i);
if(!occupiedTeams.count(team))
{
player.team = team;
occupiedTeams.insert(team);
}
}
}
mapInfoChanged(mapInfo, mapGenOptions);
}
void RandomMapTab::setMapGenOptions(std::shared_ptr<CMapGenOptions> opts)
{
groupMapSize->setSelected(vstd::find_pos(getPossibleMapSizes(), opts->getWidth()));
buttonTwoLevels->setSelected(opts->getHasTwoLevels());
groupMaxPlayers->setSelected(opts->getPlayerCount());
groupMaxTeams->setSelected(opts->getTeamCount());
groupCompOnlyPlayers->setSelected(opts->getCompOnlyPlayerCount());
groupCompOnlyTeams->setSelected(opts->getCompOnlyTeamCount());
groupWaterContent->setSelected(opts->getWaterContent());
groupMonsterStrength->setSelected(opts->getMonsterStrength());
}
void RandomMapTab::addButtonsWithRandToGroup(CToggleGroup * group, const std::vector<std::string> & defs, int nStart, int nEnd, int btnWidth, int helpStartIndex, int helpRandIndex, int randIndex, bool animIdfromBtnId) const
{
addButtonsToGroup(group, defs, nStart, nEnd, btnWidth, helpStartIndex, animIdfromBtnId);
// Buttons are relative to button group, TODO better solution?
SObjectConstruction obj__i(group);
const std::string RANDOM_DEF = "RANRAND";
group->addToggle(randIndex, std::make_shared<CToggleButton>(Point(256, 0), RANDOM_DEF, CGI->generaltexth->zelp[helpRandIndex]));
group->setSelected(randIndex);
}
void RandomMapTab::addButtonsToGroup(CToggleGroup * group, const std::vector<std::string> & defs, int nStart, int nEnd, int btnWidth, int helpStartIndex, bool animIdfromBtnId) const
{
// Buttons are relative to button group, TODO better solution?
SObjectConstruction obj__i(group);
int cnt = nEnd - nStart + 1;
for(int i = 0; i < cnt; ++i)
mapGenOptions = opts;
//prepare allowed options
for(int i = 0; i <= PlayerColor::PLAYER_LIMIT_I; ++i)
{
auto button = std::make_shared<CToggleButton>(Point(i * btnWidth, 0), animIdfromBtnId ? defs[i + nStart] : defs[i], CGI->generaltexth->zelp[helpStartIndex + i]);
// For blocked state we should use pressed image actually
button->setImageOrder(0, 1, 1, 3);
group->addToggle(i + nStart, button);
playerCountAllowed.insert(i);
compCountAllowed.insert(i);
playerTeamsAllowed.insert(i);
compTeamsAllowed.insert(i);
}
auto * tmpl = mapGenOptions->getMapTemplate();
if(tmpl)
{
playerCountAllowed = tmpl->getPlayers().getNumbers();
compCountAllowed = tmpl->getCpuPlayers().getNumbers();
}
if(mapGenOptions->getPlayerCount() != CMapGenOptions::RANDOM_SIZE)
{
vstd::erase_if(compCountAllowed,
[opts](int el){
return PlayerColor::PLAYER_LIMIT_I - opts->getPlayerCount() < el;
});
vstd::erase_if(playerTeamsAllowed,
[opts](int el){
return opts->getPlayerCount() <= el;
});
if(!playerTeamsAllowed.count(opts->getTeamCount()))
opts->setTeamCount(CMapGenOptions::RANDOM_SIZE);
}
if(mapGenOptions->getCompOnlyPlayerCount() != CMapGenOptions::RANDOM_SIZE)
{
vstd::erase_if(playerCountAllowed,
[opts](int el){
return PlayerColor::PLAYER_LIMIT_I - opts->getCompOnlyPlayerCount() < el;
});
vstd::erase_if(compTeamsAllowed,
[opts](int el){
return opts->getCompOnlyPlayerCount() <= el;
});
if(!compTeamsAllowed.count(opts->getCompOnlyTeamCount()))
opts->setCompOnlyTeamCount(CMapGenOptions::RANDOM_SIZE);
}
if(auto w = widget<CToggleGroup>("groupMapSize"))
w->setSelected(vstd::find_pos(getPossibleMapSizes(), opts->getWidth()));
if(auto w = widget<CToggleButton>("buttonTwoLevels"))
w->setSelected(opts->getHasTwoLevels());
if(auto w = widget<CToggleGroup>("groupMaxPlayers"))
{
w->setSelected(opts->getPlayerCount());
deactivateButtonsFrom(*w, playerCountAllowed);
}
if(auto w = widget<CToggleGroup>("groupMaxTeams"))
{
w->setSelected(opts->getTeamCount());
deactivateButtonsFrom(*w, playerTeamsAllowed);
}
if(auto w = widget<CToggleGroup>("groupCompOnlyPlayers"))
{
w->setSelected(opts->getCompOnlyPlayerCount());
deactivateButtonsFrom(*w, compCountAllowed);
}
if(auto w = widget<CToggleGroup>("groupCompOnlyTeams"))
{
w->setSelected(opts->getCompOnlyTeamCount());
deactivateButtonsFrom(*w, compTeamsAllowed);
}
if(auto w = widget<CToggleGroup>("groupWaterContent"))
{
w->setSelected(opts->getWaterContent());
if(opts->getMapTemplate())
{
std::set<int> allowedWater(opts->getMapTemplate()->getWaterContentAllowed().begin(), opts->getMapTemplate()->getWaterContentAllowed().end());
deactivateButtonsFrom(*w, allowedWater);
}
else
deactivateButtonsFrom(*w, {-1});
}
if(auto w = widget<CToggleGroup>("groupMonsterStrength"))
w->setSelected(opts->getMonsterStrength());
if(auto w = widget<CButton>("templateButton"))
{
if(tmpl)
w->addTextOverlay(tmpl->getName(), EFonts::FONT_SMALL);
else
w->addTextOverlay(readText(variables["defaultTemplate"]), EFonts::FONT_SMALL);
}
for(auto r : VLC->terrainTypeHandler->roads())
{
if(auto w = widget<CToggleButton>(r.name))
{
w->setSelected(opts->isRoadEnabled(r.name));
}
}
}
void RandomMapTab::deactivateButtonsFrom(CToggleGroup * group, int startId)
void RandomMapTab::setTemplate(const CRmgTemplate * tmpl)
{
logGlobal->debug("Blocking buttons from %d", startId);
for(auto toggle : group->buttons)
mapGenOptions->setMapTemplate(tmpl);
setMapGenOptions(mapGenOptions);
if(auto w = widget<CButton>("templateButton"))
{
if(tmpl)
w->addTextOverlay(tmpl->getName(), EFonts::FONT_SMALL);
else
w->addTextOverlay(readText(variables["defaultTemplate"]), EFonts::FONT_SMALL);
}
updateMapInfoByHost();
}
void RandomMapTab::deactivateButtonsFrom(CToggleGroup & group, const std::set<int> & allowed)
{
logGlobal->debug("Blocking buttons");
for(auto toggle : group.buttons)
{
if(auto button = std::dynamic_pointer_cast<CToggleButton>(toggle.second))
{
if(startId == CMapGenOptions::RANDOM_SIZE || toggle.first < startId)
if(allowed.count(CMapGenOptions::RANDOM_SIZE)
|| allowed.count(toggle.first)
|| toggle.first == CMapGenOptions::RANDOM_SIZE)
{
button->block(false);
}
@ -270,45 +327,243 @@ void RandomMapTab::deactivateButtonsFrom(CToggleGroup * group, int startId)
}
}
void RandomMapTab::validatePlayersCnt(int playersCnt)
{
if(playersCnt == CMapGenOptions::RANDOM_SIZE)
{
return;
}
if(mapGenOptions->getTeamCount() >= playersCnt)
{
mapGenOptions->setTeamCount(playersCnt - 1);
groupMaxTeams->setSelected(mapGenOptions->getTeamCount());
}
// total players should not exceed PlayerColor::PLAYER_LIMIT_I (8 in homm3)
if(mapGenOptions->getCompOnlyPlayerCount() + playersCnt > PlayerColor::PLAYER_LIMIT_I)
{
mapGenOptions->setCompOnlyPlayerCount(PlayerColor::PLAYER_LIMIT_I - playersCnt);
groupCompOnlyPlayers->setSelected(mapGenOptions->getCompOnlyPlayerCount());
}
validateCompOnlyPlayersCnt(mapGenOptions->getCompOnlyPlayerCount());
}
void RandomMapTab::validateCompOnlyPlayersCnt(int compOnlyPlayersCnt)
{
if(compOnlyPlayersCnt == CMapGenOptions::RANDOM_SIZE)
{
return;
}
if(mapGenOptions->getCompOnlyTeamCount() >= compOnlyPlayersCnt)
{
int compOnlyTeamCount = compOnlyPlayersCnt == 0 ? 0 : compOnlyPlayersCnt - 1;
mapGenOptions->setCompOnlyTeamCount(compOnlyTeamCount);
updateMapInfoByHost();
groupCompOnlyTeams->setSelected(compOnlyTeamCount);
}
}
std::vector<int> RandomMapTab::getPossibleMapSizes()
{
return {CMapHeader::MAP_SIZE_SMALL, CMapHeader::MAP_SIZE_MIDDLE, CMapHeader::MAP_SIZE_LARGE, CMapHeader::MAP_SIZE_XLARGE};
return {CMapHeader::MAP_SIZE_SMALL, CMapHeader::MAP_SIZE_MIDDLE, CMapHeader::MAP_SIZE_LARGE, CMapHeader::MAP_SIZE_XLARGE, CMapHeader::MAP_SIZE_HUGE, CMapHeader::MAP_SIZE_XHUGE, CMapHeader::MAP_SIZE_GIANT};
}
TemplatesDropBox::ListItem::ListItem(const JsonNode & config, TemplatesDropBox & _dropBox, Point position)
: InterfaceObjectConfigurable(LCLICK | HOVER, position),
dropBox(_dropBox)
{
OBJ_CONSTRUCTION;
init(config);
if(auto w = widget<CPicture>("hoverImage"))
{
pos.w = w->pos.w;
pos.h = w->pos.h;
}
type |= REDRAW_PARENT;
}
void TemplatesDropBox::ListItem::updateItem(int idx, const CRmgTemplate * _item)
{
if(auto w = widget<CLabel>("labelName"))
{
item = _item;
if(item)
{
w->setText(item->getName());
}
else
{
if(idx)
w->setText("");
else
w->setText(readText(dropBox.variables["defaultTemplate"]));
}
}
}
void TemplatesDropBox::ListItem::hover(bool on)
{
auto h = widget<CPicture>("hoverImage");
auto w = widget<CLabel>("labelName");
if(h && w)
{
if(w->getText().empty())
{
hovered = false;
h->visible = false;
}
else
{
h->visible = on;
}
}
redraw();
}
void TemplatesDropBox::ListItem::clickLeft(tribool down, bool previousState)
{
if(down && hovered)
{
dropBox.setTemplate(item);
}
}
TemplatesDropBox::TemplatesDropBox(RandomMapTab & randomMapTab, int3 size):
InterfaceObjectConfigurable(LCLICK | HOVER),
randomMapTab(randomMapTab)
{
REGISTER_BUILDER("templateListItem", &TemplatesDropBox::buildListItem);
curItems = VLC->tplh->getTemplates();
vstd::erase_if(curItems, [size](const CRmgTemplate * t){return !t->matchesSize(size);});
curItems.insert(curItems.begin(), nullptr); //default template
const JsonNode config(ResourceID("config/widgets/randomMapTemplateWidget.json"));
addCallback("sliderMove", std::bind(&TemplatesDropBox::sliderMove, this, std::placeholders::_1));
OBJ_CONSTRUCTION;
pos = randomMapTab.pos.topLeft();
pos.w = randomMapTab.pos.w;
pos.h = randomMapTab.pos.h;
init(config);
if(auto w = widget<CSlider>("slider"))
{
w->setAmount(curItems.size());
}
updateListItems();
}
std::shared_ptr<CIntObject> TemplatesDropBox::buildListItem(const JsonNode & config)
{
auto position = readPosition(config["position"]);
listItems.push_back(std::make_shared<ListItem>(config, *this, position));
return listItems.back();
}
void TemplatesDropBox::sliderMove(int slidPos)
{
auto w = widget<CSlider>("slider");
if(!w)
return; // ignore spurious call when slider is being created
updateListItems();
redraw();
}
void TemplatesDropBox::hover(bool on)
{
hovered = on;
}
void TemplatesDropBox::clickLeft(tribool down, bool previousState)
{
if(down && !hovered)
{
assert(GH.topInt().get() == this);
GH.popInt(GH.topInt());
}
}
void TemplatesDropBox::updateListItems()
{
if(auto w = widget<CSlider>("slider"))
{
int elemIdx = w->getValue();
for(auto item : listItems)
{
if(elemIdx < curItems.size())
{
item->updateItem(elemIdx, curItems[elemIdx]);
elemIdx++;
}
else
{
item->updateItem(elemIdx);
}
}
}
}
void TemplatesDropBox::setTemplate(const CRmgTemplate * tmpl)
{
randomMapTab.setTemplate(tmpl);
assert(GH.topInt().get() == this);
GH.popInt(GH.topInt());
}
TeamAlignmentsWidget::TeamAlignmentsWidget(RandomMapTab & randomMapTab):
InterfaceObjectConfigurable(),
randomMapTab(randomMapTab)
{
const JsonNode config(ResourceID("config/widgets/randomMapTeamsWidget.json"));
variables = config["variables"];
int humanPlayers = randomMapTab.obtainMapGenOptions().getPlayerCount();
int cpuPlayers = randomMapTab.obtainMapGenOptions().getCompOnlyPlayerCount();
int totalPlayers = humanPlayers == CMapGenOptions::RANDOM_SIZE || cpuPlayers == CMapGenOptions::RANDOM_SIZE
? PlayerColor::PLAYER_LIMIT_I : humanPlayers + cpuPlayers;
assert(totalPlayers <= PlayerColor::PLAYER_LIMIT_I);
auto settings = randomMapTab.obtainMapGenOptions().getPlayersSettings();
variables["totalPlayers"].Integer() = totalPlayers;
pos.w = variables["windowSize"]["x"].Integer() + totalPlayers * variables["cellMargin"]["x"].Integer();
pos.h = variables["windowSize"]["y"].Integer() + totalPlayers * variables["cellMargin"]["y"].Integer();
variables["backgroundRect"]["x"].Integer() = pos.x;
variables["backgroundRect"]["y"].Integer() = pos.y;
variables["backgroundRect"]["w"].Integer() = pos.w;
variables["backgroundRect"]["h"].Integer() = pos.h;
variables["okButtonPosition"]["x"].Integer() = variables["buttonsOffset"]["ok"]["x"].Integer();
variables["okButtonPosition"]["y"].Integer() = variables["buttonsOffset"]["ok"]["y"].Integer() + totalPlayers * variables["cellMargin"]["y"].Integer();
variables["cancelButtonPosition"]["x"].Integer() = variables["buttonsOffset"]["cancel"]["x"].Integer();
variables["cancelButtonPosition"]["y"].Integer() = variables["buttonsOffset"]["cancel"]["y"].Integer() + totalPlayers * variables["cellMargin"]["y"].Integer();
addCallback("ok", [&](int)
{
for(int plId = 0; plId < players.size(); ++plId)
{
randomMapTab.obtainMapGenOptions().setPlayerTeam(PlayerColor(plId), TeamID(players[plId]->getSelected()));
}
randomMapTab.updateMapInfoByHost();
assert(GH.topInt().get() == this);
GH.popInt(GH.topInt());
});
addCallback("cancel", [&](int)
{
assert(GH.topInt().get() == this);
GH.popInt(GH.topInt());
});
init(config);
center(pos);
OBJ_CONSTRUCTION;
for(int plId = 0; plId < totalPlayers; ++plId)
{
players.push_back(std::make_shared<CToggleGroup>([&, totalPlayers, plId](int sel)
{
variables["player_id"].Integer() = plId;
OBJ_CONSTRUCTION_TARGETED(players[plId].get());
for(int teamId = 0; teamId < totalPlayers; ++teamId)
{
auto button = std::dynamic_pointer_cast<CToggleButton>(players[plId]->buttons[teamId]);
assert(button);
if(sel == teamId)
{
button->addOverlay(buildWidget(variables["flagsAnimation"]));
}
else
{
button->addOverlay(nullptr);
}
}
}));
OBJ_CONSTRUCTION_TARGETED(players.back().get());
for(int teamId = 0; teamId < totalPlayers; ++teamId)
{
variables["point"]["x"].Integer() = variables["cellOffset"]["x"].Integer() + plId * variables["cellMargin"]["x"].Integer();
variables["point"]["y"].Integer() = variables["cellOffset"]["y"].Integer() + teamId * variables["cellMargin"]["y"].Integer();
auto button = buildWidget(variables["button"]);
players.back()->addToggle(teamId, std::dynamic_pointer_cast<CToggleBase>(button));
}
auto team = settings.at(PlayerColor(plId)).getTeam();
if(team == TeamID::NO_TEAM)
players.back()->setSelected(plId);
else
players.back()->setSelected(team.getNum());
}
}

View File

@ -13,6 +13,8 @@
#include "../../lib/FunctionList.h"
#include "../../lib/GameConstants.h"
#include "../../lib/rmg/CRmgTemplate.h"
#include "../gui/InterfaceObjectConfigurable.h"
VCMI_LIB_NAMESPACE_BEGIN
@ -23,42 +25,80 @@ VCMI_LIB_NAMESPACE_END
class CToggleButton;
class CLabel;
class CLabelGroup;
class CSlider;
class CPicture;
class RandomMapTab : public CIntObject
class RandomMapTab : public InterfaceObjectConfigurable
{
public:
RandomMapTab();
void updateMapInfoByHost();
void setMapGenOptions(std::shared_ptr<CMapGenOptions> opts);
void setTemplate(const CRmgTemplate *);
CMapGenOptions & obtainMapGenOptions() {return *mapGenOptions;}
CFunctionList<void(std::shared_ptr<CMapInfo>, std::shared_ptr<CMapGenOptions>)> mapInfoChanged;
private:
void addButtonsWithRandToGroup(CToggleGroup * group, const std::vector<std::string> & defs, int startIndex, int endIndex, int btnWidth, int helpStartIndex, int helpRandIndex, int randIndex = -1, bool animIdfromBtnId = true) const;
void addButtonsToGroup(CToggleGroup * group, const std::vector<std::string> & defs, int startIndex, int endIndex, int btnWidth, int helpStartIndex, bool animIdfromBtnId = true) const;
void deactivateButtonsFrom(CToggleGroup * group, int startId);
void validatePlayersCnt(int playersCnt);
void validateCompOnlyPlayersCnt(int compOnlyPlayersCnt);
void deactivateButtonsFrom(CToggleGroup & group, const std::set<int> & allowed);
std::vector<int> getPossibleMapSizes();
std::shared_ptr<CPicture> background;
std::shared_ptr<CLabel> labelHeadlineBig;
std::shared_ptr<CLabel> labelHeadlineSmall;
std::shared_ptr<CLabel> labelMapSize;
std::shared_ptr<CToggleGroup> groupMapSize;
std::shared_ptr<CToggleButton> buttonTwoLevels;
std::shared_ptr<CLabelGroup> labelGroupForOptions;
std::shared_ptr<CToggleGroup> groupMaxPlayers;
std::shared_ptr<CToggleGroup> groupMaxTeams;
std::shared_ptr<CToggleGroup> groupCompOnlyPlayers;
std::shared_ptr<CToggleGroup> groupCompOnlyTeams;
std::shared_ptr<CToggleGroup> groupWaterContent;
std::shared_ptr<CToggleGroup> groupMonsterStrength;
std::shared_ptr<CButton> buttonShowRandomMaps;
std::shared_ptr<CMapGenOptions> mapGenOptions;
std::shared_ptr<CMapInfo> mapInfo;
//options allowed - need to store as impact each other
std::set<int> playerCountAllowed, playerTeamsAllowed, compCountAllowed, compTeamsAllowed;
};
class TemplatesDropBox : public InterfaceObjectConfigurable
{
struct ListItem : public InterfaceObjectConfigurable
{
TemplatesDropBox & dropBox;
const CRmgTemplate * item = nullptr;
ListItem(const JsonNode &, TemplatesDropBox &, Point position);
void updateItem(int index, const CRmgTemplate * item = nullptr);
void hover(bool on) override;
void clickLeft(tribool down, bool previousState) override;
};
friend struct ListItem;
public:
TemplatesDropBox(RandomMapTab & randomMapTab, int3 size);
void hover(bool on) override;
void clickLeft(tribool down, bool previousState) override;
void setTemplate(const CRmgTemplate *);
private:
std::shared_ptr<CIntObject> buildListItem(const JsonNode & config);
void sliderMove(int slidPos);
void updateListItems();
RandomMapTab & randomMapTab;
std::vector<std::shared_ptr<ListItem>> listItems;
std::vector<const CRmgTemplate *> curItems;
};
class TeamAlignmentsWidget: public InterfaceObjectConfigurable
{
public:
TeamAlignmentsWidget(RandomMapTab & randomMapTab);
private:
RandomMapTab & randomMapTab;
std::shared_ptr<CFilledTexture> background;
std::shared_ptr<CLabelGroup> labels;
std::shared_ptr<CButton> buttonOk, buttonCancel;
std::vector<std::shared_ptr<CToggleGroup>> players;
std::vector<std::shared_ptr<CIntObject>> placeholders;
};

View File

@ -84,8 +84,11 @@ void CButton::addTextOverlay(const std::string & Text, EFonts font, SDL_Color co
void CButton::addOverlay(std::shared_ptr<CIntObject> newOverlay)
{
overlay = newOverlay;
addChild(newOverlay.get());
overlay->moveTo(overlay->pos.centerIn(pos).topLeft());
if(overlay)
{
addChild(newOverlay.get());
overlay->moveTo(overlay->pos.centerIn(pos).topLeft());
}
update();
}
@ -449,6 +452,11 @@ void CToggleGroup::selectionChanged(int to)
parent->redraw();
}
int CToggleGroup::getSelected() const
{
return selectedID;
}
CVolumeSlider::CVolumeSlider(const Point & position, const std::string & defName, const int value, const std::pair<std::string, std::string> * const help)
: CIntObject(LCLICK | RCLICK | WHEEL),
value(value),
@ -566,16 +574,21 @@ void CSlider::setScrollStep(int to)
scrollStep = to;
}
int CSlider::getAmount()
int CSlider::getAmount() const
{
return amount;
}
int CSlider::getValue()
int CSlider::getValue() const
{
return value;
}
int CSlider::getCapacity() const
{
return capacity;
}
void CSlider::moveLeft()
{
moveTo(value-1);

View File

@ -185,6 +185,7 @@ public:
/// in some cases, e.g. LoadGame difficulty selection, after refreshing the UI, the ToggleGroup should
/// reset all of it's child buttons to BLOCK state, then make selection again
void setSelectedOnly(int id);
int getSelected() const;
};
/// A typical slider for volume with an animated indicator
@ -256,8 +257,9 @@ public:
void setAmount(int to);
/// Accessors
int getAmount();
int getValue();
int getAmount() const;
int getValue() const;
int getCapacity() const;
void addCallback(std::function<void(int)> callback);

View File

@ -57,7 +57,7 @@ public:
};
/// area filled with specific texture
class CFilledTexture : CIntObject
class CFilledTexture : public CIntObject
{
SDL_Surface * texture;

View File

@ -16,8 +16,8 @@
"extraResourcesLimit" : 3
},
"minGuardStrength" : 2000,
"defaultRoadType" : "pc", //pd - dirt, pg - gravel, pc - cobblestone
"secondaryRoadType": "pd",
"defaultRoadType" : "cobblestoneRoad",
"secondaryRoadType": "dirtRoad",
"treasureValueLimit" : 20000, //generate pandora with gold for treasure above this limit
"prisons" :
{

View File

@ -113,5 +113,15 @@
"label" : "Hide complete quests",
"help" : "Hide all quests that already completed"
}
},
"randomMapTab":
{
"widgets":
{
"defaultTemplate": "default",
"templateLabel": "Template",
"teamAlignmentsButton": "Setup...",
"teamAlignmentsLabel": "Team alignments"
}
}
}

View File

@ -0,0 +1,510 @@
{
"items":
[
{
"name": "background",
"type": "picture",
"image": "RANMAPBK",
"position": {"x": 0, "y": 6}
},
{
"name": "labelHeadlineBig",
"type": "label",
"font": "big",
"alignment": "center",
"color": "yellow",
"text": 738,
"position": {"x": 222, "y": 36}
},
{
"name": "labelHeadlineSmall",
"type": "label",
"font": "small",
"alignment": "center",
"color": "white",
"text": 739,
"position": {"x": 222, "y": 56}
},
{
"name": "labelMapSize",
"type": "label",
"font": "small",
"alignment": "center",
"color": "white",
"text": 752,
"position": {"x": 104, "y": 97}
},
{
"name": "groupMapSize",
"type": "toggleGroup",
"position": {"x": 158, "y": 81},
"items":
[
{
"index": 0,
"type": "toggleButton",
"image": "RANSIZS",
"zelp": 198,
"imageOrder": [0, 1, 1, 3],
"position": {"x": 0, "y": 0},
},
{
"type": "toggleButton",
"image": "RANSIZM",
"zelp": 199,
"imageOrder": [0, 1, 1, 3],
"position": {"x": 47, "y": 0},
},
{
"type": "toggleButton",
"image": "RANSIZL",
"zelp": 200,
"imageOrder": [0, 1, 1, 3],
"position": {"x": 94, "y": 0},
},
{
"type": "toggleButton",
"image": "RANSIZX",
"zelp": 201,
"imageOrder": [0, 1, 1, 3],
"position": {"x": 141, "y": 0}
}
],
"selected": 1,
"callback": "toggleMapSize"
},
{
"name": "buttonTwoLevels",
"type": "toggleButton",
"image": "RANUNDR",
"position": {"x": 346, "y": 81},
"selected": true,
"callback": "toggleTwoLevels"
},
{
"name": "groupMaxPlayers",
"type": "toggleGroup",
"position": {"x": 67, "y": 153},
"items":
[
{
"index": 1,
"type": "toggleButton",
"image": "RANNUM1",
"zelp": 204,
"imageOrder": [0, 1, 1, 3],
"position": {"x": 0, "y": 0}
},
{
"type": "toggleButton",
"image": "RANNUM2",
"zelp": 205,
"imageOrder": [0, 1, 1, 3],
"position": {"x": 32, "y": 0}
},
{
"type": "toggleButton",
"image": "RANNUM3",
"zelp": 206,
"imageOrder": [0, 1, 1, 3],
"position": {"x": 64, "y": 0}
},
{
"type": "toggleButton",
"image": "RANNUM4",
"zelp": 207,
"imageOrder": [0, 1, 1, 3],
"position": {"x": 96, "y": 0}
},
{
"type": "toggleButton",
"image": "RANNUM5",
"zelp": 208,
"imageOrder": [0, 1, 1, 3],
"position": {"x": 128, "y": 0}
},
{
"type": "toggleButton",
"image": "RANNUM6",
"zelp": 209,
"imageOrder": [0, 1, 1, 3],
"position": {"x": 160, "y": 0}
},
{
"type": "toggleButton",
"image": "RANNUM7",
"zelp": 210,
"imageOrder": [0, 1, 1, 3],
"position": {"x": 192, "y": 0}
},
{
"type": "toggleButton",
"image": "RANNUM8",
"zelp": 211,
"imageOrder": [0, 1, 1, 3],
"position": {"x": 224, "y": 0}
},
{
"index": -1,
"type": "toggleButton",
"image": "RANRAND",
"zelp": 212,
"position": {"x": 256, "y": 0},
}
],
"selected": 7,
"callback": "setPlayersCount"
},
{
"name": "groupMaxTeams",
"type": "toggleGroup",
"position": {"x": 67, "y": 219},
"items":
[
{
"index": 0,
"type": "toggleButton",
"image": "RANNUM0",
"zelp": 214,
"imageOrder": [0, 1, 1, 3],
"position": {"x": 0, "y": 0}
},
{
"type": "toggleButton",
"image": "RANNUM1",
"zelp": 215,
"imageOrder": [0, 1, 1, 3],
"position": {"x": 32, "y": 0}
},
{
"type": "toggleButton",
"image": "RANNUM2",
"zelp": 216,
"imageOrder": [0, 1, 1, 3],
"position": {"x": 64, "y": 0}
},
{
"type": "toggleButton",
"image": "RANNUM3",
"zelp": 217,
"imageOrder": [0, 1, 1, 3],
"position": {"x": 96, "y": 0}
},
{
"type": "toggleButton",
"image": "RANNUM4",
"zelp": 218,
"imageOrder": [0, 1, 1, 3],
"position": {"x": 128, "y": 0}
},
{
"type": "toggleButton",
"image": "RANNUM5",
"zelp": 219,
"imageOrder": [0, 1, 1, 3],
"position": {"x": 160, "y": 0}
},
{
"type": "toggleButton",
"image": "RANNUM6",
"zelp": 220,
"imageOrder": [0, 1, 1, 3],
"position": {"x": 192, "y": 0}
},
{
"type": "toggleButton",
"image": "RANNUM7",
"zelp": 221,
"imageOrder": [0, 1, 1, 3],
"position": {"x": 224, "y": 0}
},
{
"index": -1,
"type": "toggleButton",
"image": "RANRAND",
"zelp": 222,
"position": {"x": 256, "y": 0},
}
],
"selected": 7,
"callback": "setTeamsCount"
},
{
"name": "groupCompOnlyPlayers",
"type": "toggleGroup",
"position": {"x": 67, "y": 285},
"items":
[
{
"index": 0,
"type": "toggleButton",
"image": "RANNUM0",
"zelp": 224,
"imageOrder": [0, 1, 1, 3],
"position": {"x": 0, "y": 0}
},
{
"type": "toggleButton",
"image": "RANNUM1",
"zelp": 225,
"imageOrder": [0, 1, 1, 3],
"position": {"x": 32, "y": 0}
},
{
"type": "toggleButton",
"image": "RANNUM2",
"zelp": 226,
"imageOrder": [0, 1, 1, 3],
"position": {"x": 64, "y": 0}
},
{
"type": "toggleButton",
"image": "RANNUM3",
"zelp": 227,
"imageOrder": [0, 1, 1, 3],
"position": {"x": 96, "y": 0}
},
{
"type": "toggleButton",
"image": "RANNUM4",
"zelp": 228,
"imageOrder": [0, 1, 1, 3],
"position": {"x": 128, "y": 0}
},
{
"type": "toggleButton",
"image": "RANNUM5",
"zelp": 229,
"imageOrder": [0, 1, 1, 3],
"position": {"x": 160, "y": 0}
},
{
"type": "toggleButton",
"image": "RANNUM6",
"zelp": 230,
"imageOrder": [0, 1, 1, 3],
"position": {"x": 192, "y": 0}
},
{
"type": "toggleButton",
"image": "RANNUM7",
"zelp": 231,
"imageOrder": [0, 1, 1, 3],
"position": {"x": 224, "y": 0}
},
{
"index": -1,
"type": "toggleButton",
"image": "RANRAND",
"zelp": 232,
"position": {"x": 256, "y": 0},
}
],
"selected": 7,
"callback": "setCompOnlyPlayers"
},
{
"name": "groupCompOnlyTeams",
"type": "toggleGroup",
"position": {"x": 67, "y": 351},
"items":
[
{
"index": 0,
"type": "toggleButton",
"image": "RANNUM0",
"zelp": 234,
"imageOrder": [0, 1, 1, 3],
"position": {"x": 0, "y": 0}
},
{
"type": "toggleButton",
"image": "RANNUM1",
"zelp": 235,
"imageOrder": [0, 1, 1, 3],
"position": {"x": 32, "y": 0}
},
{
"type": "toggleButton",
"image": "RANNUM2",
"zelp": 236,
"imageOrder": [0, 1, 1, 3],
"position": {"x": 64, "y": 0}
},
{
"type": "toggleButton",
"image": "RANNUM3",
"zelp": 237,
"imageOrder": [0, 1, 1, 3],
"position": {"x": 96, "y": 0}
},
{
"type": "toggleButton",
"image": "RANNUM4",
"zelp": 238,
"imageOrder": [0, 1, 1, 3],
"position": {"x": 128, "y": 0}
},
{
"type": "toggleButton",
"image": "RANNUM5",
"zelp": 239,
"imageOrder": [0, 1, 1, 3],
"position": {"x": 160, "y": 0}
},
{
"type": "toggleButton",
"image": "RANNUM6",
"zelp": 240,
"imageOrder": [0, 1, 1, 3],
"position": {"x": 192, "y": 0}
},
{
"index": -1,
"type": "toggleButton",
"image": "RANRAND",
"zelp": 241,
"position": {"x": 256, "y": 0},
}
],
"selected": 7,
"callback": "setCompOnlyTeams"
},
{
"name": "groupWaterContent",
"type": "toggleGroup",
"position": {"x": 67, "y": 419},
"items":
[
{
"index": 0,
"type": "toggleButton",
"image": "RANNONE",
"zelp": 243,
"imageOrder": [0, 1, 1, 3],
"position": {"x": 0, "y": 0}
},
{
"type": "toggleButton",
"image": "RANNORM",
"zelp": 244,
"imageOrder": [0, 1, 1, 3],
"position": {"x": 85, "y": 0}
},
{
"type": "toggleButton",
"image": "RANISLD",
"zelp": 245,
"imageOrder": [0, 1, 1, 3],
"position": {"x": 170, "y": 0}
},
{
"index": -1,
"type": "toggleButton",
"image": "RANRAND",
"zelp": 246,
"position": {"x": 256, "y": 0},
}
],
"selected": 3,
"callback": "setWaterContent"
},
{
"name": "groupMonsterStrength",
"type": "toggleGroup",
"position": {"x": 67, "y": 485},
"items":
[
{
"index": 2,
"type": "toggleButton",
"image": "RANWEAK",
"zelp": 248,
"imageOrder": [0, 1, 1, 3],
"position": {"x": 0, "y": 0}
},
{
"index": 3,
"type": "toggleButton",
"image": "RANNORM",
"zelp": 249,
"imageOrder": [0, 1, 1, 3],
"position": {"x": 85, "y": 0}
},
{
"index": 4,
"type": "toggleButton",
"image": "RANSTRG",
"zelp": 250,
"imageOrder": [0, 1, 1, 3],
"position": {"x": 170, "y": 0}
},
{
"index": -2,
"type": "toggleButton",
"image": "RANRAND",
"zelp": 251,
"position": {"x": 256, "y": 0},
}
],
"selected": 3,
"callback": "setMonsterStrength"
},
{
"name": "buttonShowRandomMaps",
"type": "button",
"position": {"x": 54, "y": 535},
"image": "RANSHOW",
"zelp": 252
},
{
"type": "labelGroup",
"font": "small",
"alignment": "left",
"color": "white",
"items":
[
{
"position": {"x": 68, "y": 133},
"text": 753
},
{
"position": {"x": 68, "y": 199},
"text": 754
},
{
"position": {"x": 68, "y": 265},
"text": 755
},
{
"position": {"x": 68, "y": 331},
"text": 756
},
{
"position": {"x": 68, "y": 398},
"text": 757
},
{
"position": {"x": 68, "y": 465},
"text": 758
}
]
}
]
}

6
debian/changelog vendored
View File

@ -1,3 +1,9 @@
vcmi (1.2.0) jammy; urgency=medium
* New upstream release
-- Ivan Savenko <saven.ivan@gmail.com> Fri, 23 Dec 2022 16:00:00 +0200
vcmi (1.1.0) jammy; urgency=medium
* New upstream release

View File

@ -38,6 +38,7 @@
<url type="bugtracker">https://github.com/vcmi/vcmi/issues</url>
<url type="faq">https://vcmi.eu/faq/</url>
<releases>
<release version="1.2.0" date="2022-12-24" type="development" />
<release version="1.1.0" date="2022-12-23" />
<release version="1.0.0" date="2022-09-11" />
<release version="0.99" date="2016-11-01" />

View File

@ -220,6 +220,7 @@ void TerrainTypeHandler::initRivers(const std::vector<std::string> & allConfigs)
{
RiverType info;
info.name = river.first;
info.fileName = river.second["animation"].String();
info.code = river.second["code"].String();
info.deltaName = river.second["delta"].String();
@ -255,6 +256,7 @@ void TerrainTypeHandler::initRoads(const std::vector<std::string> & allConfigs)
{
RoadType info;
info.name = road.first;
info.fileName = road.second["animation"].String();
info.code = road.second["code"].String();
info.movementCost = static_cast<ui8>(road.second["moveCost"].Float());
@ -295,7 +297,7 @@ void TerrainTypeHandler::recreateRiverMaps()
{
const auto * riverInfo = &riverTypes[i];
riverInfoByName[riverInfo->fileName] = riverInfo;
riverInfoByName[riverInfo->name] = riverInfo;
riverInfoByCode[riverInfo->code] = riverInfo;
riverInfoById[riverInfo->id] = riverInfo;
}
@ -307,7 +309,7 @@ void TerrainTypeHandler::recreateRoadMaps()
{
const auto * roadInfo = &roadTypes[i];
roadInfoByName[roadInfo->fileName] = roadInfo;
roadInfoByName[roadInfo->name] = roadInfo;
roadInfoByCode[roadInfo->code] = roadInfo;
roadInfoById[roadInfo->id] = roadInfo;
}

View File

@ -89,7 +89,7 @@ public:
class DLL_LINKAGE RiverType
{
public:
std::string name;
std::string fileName;
std::string code;
std::string deltaName;
@ -99,6 +99,10 @@ public:
template <typename Handler> void serialize(Handler& h, const int version)
{
if(version >= 806)
{
h & name;
}
h & fileName;
h & code;
h & deltaName;
@ -109,6 +113,7 @@ public:
class DLL_LINKAGE RoadType
{
public:
std::string name;
std::string fileName;
std::string code;
RoadId id;
@ -118,6 +123,10 @@ public:
template <typename Handler> void serialize(Handler& h, const int version)
{
if(version >= 806)
{
h & name;
}
h & fileName;
h & code;
h & id;

View File

@ -284,6 +284,9 @@ public:
static const int MAP_SIZE_MIDDLE = 72;
static const int MAP_SIZE_LARGE = 108;
static const int MAP_SIZE_XLARGE = 144;
static const int MAP_SIZE_HUGE = 180;
static const int MAP_SIZE_XHUGE = 216;
static const int MAP_SIZE_GIANT = 252;
CMapHeader();
virtual ~CMapHeader();

View File

@ -134,6 +134,12 @@ int CMapInfo::getMapSizeIconId() const
return 2;
case CMapHeader::MAP_SIZE_XLARGE:
return 3;
case CMapHeader::MAP_SIZE_HUGE:
return 4;
case CMapHeader::MAP_SIZE_XHUGE:
return 5;
case CMapHeader::MAP_SIZE_GIANT:
return 6;
default:
return 4;
}
@ -180,6 +186,12 @@ std::string CMapInfo::getMapSizeName() const
return "L";
case CMapHeader::MAP_SIZE_XLARGE:
return "XL";
case CMapHeader::MAP_SIZE_HUGE:
return "H";
case CMapHeader::MAP_SIZE_XHUGE:
return "XH";
case CMapHeader::MAP_SIZE_GIANT:
return "G";
default:
return "C";
}

View File

@ -136,12 +136,14 @@ void CMapGenOptions::resetPlayersMap()
{
std::map<PlayerColor, TFaction> rememberTownTypes;
std::map<PlayerColor, TeamID> rememberTeam;
for (auto p : players)
{
auto town = p.second.getStartingTown();
if (town != RANDOM_SIZE)
rememberTownTypes[p.first] = town;
rememberTeam[p.first] = p.second.getTeam();
}
@ -169,6 +171,7 @@ void CMapGenOptions::resetPlayersMap()
playerType = EPlayerType::COMP_ONLY;
}
player.setPlayerType(playerType);
player.setTeam(rememberTeam[pc]);
players[pc] = player;
if (vstd::contains(rememberTownTypes, pc))
@ -204,8 +207,50 @@ const CRmgTemplate * CMapGenOptions::getMapTemplate() const
void CMapGenOptions::setMapTemplate(const CRmgTemplate * value)
{
mapTemplate = value;
//TODO validate & adapt options according to template
//assert(0);
//validate & adapt options according to template
if(mapTemplate)
{
if(!mapTemplate->matchesSize(int3(getWidth(), getHeight(), 1 + getHasTwoLevels())))
{
auto sizes = mapTemplate->getMapSizes();
setWidth(sizes.first.x);
setHeight(sizes.first.y);
setHasTwoLevels(sizes.first.z - 1);
}
if(!mapTemplate->getPlayers().isInRange(getPlayerCount()))
setPlayerCount(RANDOM_SIZE);
if(!mapTemplate->getCpuPlayers().isInRange(getCompOnlyPlayerCount()))
setCompOnlyPlayerCount(RANDOM_SIZE);
if(!mapTemplate->getWaterContentAllowed().count(getWaterContent()))
setWaterContent(EWaterContent::RANDOM);
}
}
void CMapGenOptions::setMapTemplate(const std::string & name)
{
if(!name.empty())
setMapTemplate(VLC->tplh->getTemplate(name));
}
void CMapGenOptions::setRoadEnabled(const std::string & roadName, bool enable)
{
if(enable)
disabledRoads.erase(roadName);
else
disabledRoads.insert(roadName);
}
bool CMapGenOptions::isRoadEnabled(const std::string & roadName) const
{
return !disabledRoads.count(roadName);
}
void CMapGenOptions::setPlayerTeam(PlayerColor color, TeamID team)
{
auto it = players.find(color);
if(it == players.end()) assert(0);
it->second.setTeam(team);
}
void CMapGenOptions::finalize(CRandomGenerator & rand)
@ -391,7 +436,6 @@ PlayerColor CMapGenOptions::getNextPlayerColor() const
bool CMapGenOptions::checkOptions() const
{
assert(countHumanPlayers() > 0);
if(mapTemplate)
{
return true;
@ -452,7 +496,7 @@ const CRmgTemplate * CMapGenOptions::getPossibleTemplate(CRandomGenerator & rand
return *RandomGeneratorUtil::nextItem(templates, rand);
}
CMapGenOptions::CPlayerSettings::CPlayerSettings() : color(0), startingTown(RANDOM_TOWN), playerType(EPlayerType::AI)
CMapGenOptions::CPlayerSettings::CPlayerSettings() : color(0), startingTown(RANDOM_TOWN), playerType(EPlayerType::AI), team(TeamID::NO_TEAM)
{
}
@ -494,4 +538,14 @@ void CMapGenOptions::CPlayerSettings::setPlayerType(EPlayerType::EPlayerType val
playerType = value;
}
TeamID CMapGenOptions::CPlayerSettings::getTeam() const
{
return team;
}
void CMapGenOptions::CPlayerSettings::setTeam(TeamID value)
{
team = value;
}
VCMI_LIB_NAMESPACE_END

View File

@ -11,37 +11,12 @@
#pragma once
#include "../GameConstants.h"
#include "CRmgTemplate.h"
VCMI_LIB_NAMESPACE_BEGIN
class CRmgTemplate;
class CRandomGenerator;
namespace EWaterContent
{
enum EWaterContent
{
RANDOM = -1,
NONE,
NORMAL,
ISLANDS
};
}
namespace EMonsterStrength
{
enum EMonsterStrength
{
RANDOM = -2,
ZONE_WEAK = -1,
ZONE_NORMAL = 0,
ZONE_STRONG = 1,
GLOBAL_WEAK = 2,
GLOBAL_NORMAL = 3,
GLOBAL_STRONG = 4
};
}
namespace EPlayerType
{
enum EPlayerType
@ -76,6 +51,10 @@ public:
/// The default value is EPlayerType::AI.
EPlayerType::EPlayerType getPlayerType() const;
void setPlayerType(EPlayerType::EPlayerType value);
/// Team id for this player. TeamID::NO_TEAM by default - team will be randomly assigned
TeamID getTeam() const;
void setTeam(TeamID value);
/// Constant for a random town selection.
static const si32 RANDOM_TOWN = -1;
@ -84,6 +63,7 @@ public:
PlayerColor color;
si32 startingTown;
EPlayerType::EPlayerType playerType;
TeamID team;
public:
template <typename Handler>
@ -92,6 +72,8 @@ public:
h & color;
h & startingTown;
h & playerType;
if(version >= 806)
h & team;
}
};
@ -130,6 +112,9 @@ public:
EMonsterStrength::EMonsterStrength getMonsterStrength() const;
void setMonsterStrength(EMonsterStrength::EMonsterStrength value);
bool isRoadEnabled(const std::string & roadName) const;
void setRoadEnabled(const std::string & roadName, bool enable);
/// The first player colors belong to standard players and the last player colors belong to comp only players.
/// All standard players are by default of type EPlayerType::AI.
@ -138,11 +123,14 @@ public:
/// 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.
void setPlayerTypeForStandardPlayer(PlayerColor color, EPlayerType::EPlayerType playerType);
void setPlayerTeam(PlayerColor color, TeamID team = TeamID::NO_TEAM);
/// 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);
void setMapTemplate(const std::string & name);
std::vector<const CRmgTemplate *> getPossibleTemplates() const;
@ -171,6 +159,8 @@ private:
EWaterContent::EWaterContent waterContent;
EMonsterStrength::EMonsterStrength monsterStrength;
std::map<PlayerColor, CPlayerSettings> players;
std::set<std::string> disabledRoads;
const CRmgTemplate * mapTemplate;
public:
@ -187,7 +177,21 @@ public:
h & waterContent;
h & monsterStrength;
h & players;
//TODO add name of template to class, enables selection of a template by a user
std::string templateName;
if(mapTemplate && h.saving)
{
templateName = mapTemplate->getId();
}
if(version >= 806)
{
h & templateName;
if(!h.saving)
{
setMapTemplate(templateName);
}
h & disabledRoads;
}
}
};

View File

@ -76,6 +76,11 @@ void CMapGenerator::loadConfig()
config.pandoraMultiplierSpells = randomMapJson["pandoras"]["valueMultiplierSpells"].Integer();
config.pandoraSpellSchool = randomMapJson["pandoras"]["valueSpellSchool"].Integer();
config.pandoraSpell60 = randomMapJson["pandoras"]["valueSpell60"].Integer();
//override config with game options
if(!mapGenOptions.isRoadEnabled(config.secondaryRoadType))
config.secondaryRoadType = "";
if(!mapGenOptions.isRoadEnabled(config.defaultRoadType))
config.defaultRoadType = config.secondaryRoadType;
}
const CMapGenerator::Config & CMapGenerator::getConfig() const
@ -183,6 +188,7 @@ void CMapGenerator::addPlayerInfo()
enum ETeams {CPHUMAN = 0, CPUONLY = 1, AFTER_LAST = 2};
std::array<std::list<int>, 2> teamNumbers;
std::set<int> teamsTotal;
int teamOffset = 0;
int playerCount = 0;
@ -238,19 +244,26 @@ void CMapGenerator::addPlayerInfo()
player.canHumanPlay = true;
}
if (teamNumbers[j].empty())
if(pSettings.getTeam() != TeamID::NO_TEAM)
{
logGlobal->error("Not enough places in team for %s player", ((j == CPUONLY) ? "CPU" : "CPU or human"));
assert (teamNumbers[j].size());
player.team = pSettings.getTeam();
}
auto itTeam = RandomGeneratorUtil::nextItem(teamNumbers[j], rand);
player.team = TeamID(*itTeam);
teamNumbers[j].erase(itTeam);
else
{
if (teamNumbers[j].empty())
{
logGlobal->error("Not enough places in team for %s player", ((j == CPUONLY) ? "CPU" : "CPU or human"));
assert (teamNumbers[j].size());
}
auto itTeam = RandomGeneratorUtil::nextItem(teamNumbers[j], rand);
player.team = TeamID(*itTeam);
teamNumbers[j].erase(itTeam);
}
teamsTotal.insert(player.team.getNum());
map->map().players[pSettings.getColor().getNum()] = player;
}
map->map().howManyTeams = (mapGenOptions.getTeamCount() == 0 ? mapGenOptions.getPlayerCount() : mapGenOptions.getTeamCount())
+ (mapGenOptions.getCompOnlyTeamCount() == 0 ? mapGenOptions.getCompOnlyPlayerCount() : mapGenOptions.getCompOnlyTeamCount());
map->map().howManyTeams = teamsTotal.size();
}
void CMapGenerator::genZones()

View File

@ -501,9 +501,19 @@ void CRmgTemplate::setId(const std::string & value)
id = value;
}
void CRmgTemplate::setName(const std::string & value)
{
name = value;
}
const std::string & CRmgTemplate::getName() const
{
return name.empty() ? id : name;
return name;
}
const std::string & CRmgTemplate::getId() const
{
return id;
}
const CRmgTemplate::CPlayerCountRange & CRmgTemplate::getPlayers() const
@ -531,6 +541,11 @@ void CRmgTemplate::validate() const
//TODO add some validation checks, throw on failure
}
std::pair<int3, int3> CRmgTemplate::getMapSizes() const
{
return {minSize, maxSize};
}
void CRmgTemplate::CPlayerCountRange::addRange(int lower, int upper)
{
range.push_back(std::make_pair(lower, upper));

View File

@ -14,7 +14,6 @@
#include "../GameConstants.h"
#include "../ResourceSet.h"
#include "../Terrain.h"
#include "CMapGenOptions.h"
VCMI_LIB_NAMESPACE_BEGIN
@ -32,6 +31,31 @@ namespace ETemplateZoneType
};
}
namespace EWaterContent
{
enum EWaterContent
{
RANDOM = -1,
NONE,
NORMAL,
ISLANDS
};
}
namespace EMonsterStrength
{
enum EMonsterStrength
{
RANDOM = -2,
ZONE_WEAK = -1,
ZONE_NORMAL = 0,
ZONE_STRONG = 1,
GLOBAL_WEAK = 2,
GLOBAL_NORMAL = 3,
GLOBAL_STRONG = 4
};
}
class DLL_LINKAGE CTreasureInfo
{
public:
@ -194,10 +218,13 @@ public:
const std::set<EWaterContent::EWaterContent> & getWaterContentAllowed() const;
void setId(const std::string & value);
void setName(const std::string & value);
const std::string & getId() const;
const std::string & getName() const;
const CPlayerCountRange & getPlayers() const;
const CPlayerCountRange & getCpuPlayers() const;
std::pair<int3, int3> getMapSizes() const;
const Zones & getZones() const;
const std::vector<rmg::ZoneConnection> & getConnections() const;

View File

@ -32,8 +32,9 @@ void CRmgTemplateStorage::loadObject(std::string scope, std::string name, const
{
JsonDeserializer handler(nullptr, data);
auto fullKey = normalizeIdentifier(scope, CModHandler::scopeBuiltin(), name); //actually it's not used
templates[fullKey].setId(name);
templates[fullKey].setId(fullKey);
templates[fullKey].serializeJson(handler);
templates[fullKey].setName(name);
templates[fullKey].validate();
}
catch(const std::exception & e)

View File

@ -13,6 +13,7 @@
#include "CZonePlacer.h"
#include "../mapping/CMap.h"
#include "../mapping/CMapEditManager.h"
#include "CMapGenOptions.h"
#include "RmgMap.h"
#include "Zone.h"
#include "Functions.h"

View File

@ -19,6 +19,7 @@ class RmgMap;
class ObjectManager;
class ObjectTemplate;
class CMapGenerator;
class CRandomGenerator;
class rmgException : public std::exception
{

View File

@ -22,6 +22,9 @@ VCMI_LIB_NAMESPACE_BEGIN
void RoadPlacer::process()
{
if(generator.getConfig().defaultRoadType.empty() && generator.getConfig().secondaryRoadType.empty())
return; //do not generate roads at all
connectRoads();
}
@ -68,11 +71,15 @@ bool RoadPlacer::createRoad(const int3 & dst)
void RoadPlacer::drawRoads(bool secondary)
{
if((secondary && generator.getConfig().secondaryRoadType.empty())
|| (!secondary && generator.getConfig().defaultRoadType.empty()))
return;
zone.areaPossible().subtract(roads);
zone.freePaths().unite(roads);
map.getEditManager()->getTerrainSelection().setSelection(roads.getTilesVector());
std::string roadCode = (secondary ? generator.getConfig().secondaryRoadType : generator.getConfig().defaultRoadType);
RoadId roadType = VLC->terrainTypeHandler->getRoadByCode(roadCode)->id;
std::string roadName = (secondary ? generator.getConfig().secondaryRoadType : generator.getConfig().defaultRoadType);
RoadId roadType = VLC->terrainTypeHandler->getRoadByName(roadName)->id;
map.getEditManager()->drawRoad(roadType, &generator.rand);
}

View File

@ -1118,14 +1118,11 @@ int main(int argc, char * argv[])
}
#ifdef VCMI_ANDROID
void CVCMIServer::create(const std::vector<std::string> & args)
void CVCMIServer::create()
{
const char * foo = "android-server";
std::vector<const void *> argv = {foo};
for(auto & a : args)
argv.push_back(a.c_str());
main(argv.size(), const_cast<char **>(foo));
main(argv.size(), reinterpret_cast<char **>(const_cast<void **>(&*argv.begin())));
}
#elif defined(SINGLE_PROCESS_APP)
void CVCMIServer::create(boost::condition_variable * cond, const std::vector<std::string> & args)

View File

@ -114,7 +114,7 @@ public:
ui8 getIdOfFirstUnallocatedPlayer() const;
#ifdef VCMI_ANDROID
static void create(const std::vector<std::string> & args);
static void create();
#elif defined(SINGLE_PROCESS_APP)
static void create(boost::condition_variable * cond, const std::vector<std::string> & args);
#endif