Laserlicht 1 год назад
Родитель
Сommit
02bd52041b

+ 1 - 0
Mods/vcmi/config/vcmi/english.json

@@ -72,6 +72,7 @@
 	"vcmi.lobby.noUnderground" : "no underground",
 	"vcmi.lobby.sortDate" : "Sorts maps by change date",
 	"vcmi.lobby.backToLobby" : "Return to lobby",
+	"vcmi.lobby.author" : "Author",
 	
 	"vcmi.lobby.login.title" : "VCMI Online Lobby",
 	"vcmi.lobby.login.username" : "Username:",

+ 1 - 0
Mods/vcmi/config/vcmi/german.json

@@ -72,6 +72,7 @@
 	"vcmi.lobby.noUnderground" : "Kein Untergrund",
 	"vcmi.lobby.sortDate" : "Ordnet Karten nach Änderungsdatum",
 	"vcmi.lobby.backToLobby" : "Zur Lobby zurückkehren",
+	"vcmi.lobby.author" : "Author",
 	
 	"vcmi.lobby.login.title" : "VCMI Online Lobby",
 	"vcmi.lobby.login.username" : "Benutzername:",

+ 10 - 1
client/lobby/SelectionTab.cpp

@@ -44,6 +44,7 @@
 #include "../../lib/mapping/CMapHeader.h"
 #include "../../lib/mapping/MapFormat.h"
 #include "../../lib/TerrainHandler.h"
+#include "../../lib/TextOperations.h"
 
 bool mapSorter::operator()(const std::shared_ptr<ElementInfo> aaa, const std::shared_ptr<ElementInfo> bbb)
 {
@@ -391,7 +392,15 @@ void SelectionTab::showPopupWindow(const Point & cursorPosition)
 		return;
 
 	if(!curItems[py]->isFolder)
-		GH.windows().createAndPushWindow<CMapOverview>(curItems[py]->getNameTranslated(), curItems[py]->fullFileURI, curItems[py]->date, ResourcePath(curItems[py]->fileURI), tabType);
+		GH.windows().createAndPushWindow<CMapOverview>(
+			curItems[py]->getNameTranslated(),
+			curItems[py]->fullFileURI,
+			tabType == ESelectionScreen::newGame && curItems[py]->mapHeader->creationDateTime ? TextOperations::getFormattedDateTimeLocal(curItems[py]->mapHeader->creationDateTime) : curItems[py]->date,
+			curItems[py]->mapHeader->author.toString() + (!curItems[py]->mapHeader->authorContact.toString().empty() ? (" <" + curItems[py]->mapHeader->authorContact.toString() + ">") : ""),
+			curItems[py]->mapHeader->mapVersion.toString(),
+			ResourcePath(curItems[py]->fileURI),
+			tabType
+		);
 	else
 		CRClickPopup::createAndPush(curItems[py]->folderName);
 }

+ 10 - 2
client/windows/CMapOverview.cpp

@@ -43,8 +43,8 @@
 #include "../../lib/rmg/CMapGenOptions.h"
 #include "../../lib/Languages.h"
 
-CMapOverview::CMapOverview(std::string mapName, std::string fileName, std::string date, ResourcePath resource, ESelectionScreen tabType)
-	: CWindowObject(BORDERED | RCLICK_POPUP), resource(resource), mapName(mapName), fileName(fileName), date(date), tabType(tabType)
+CMapOverview::CMapOverview(std::string mapName, std::string fileName, std::string date, std::string author, std::string version, ResourcePath resource, ESelectionScreen tabType)
+	: CWindowObject(BORDERED | RCLICK_POPUP), resource(resource), mapName(mapName), fileName(fileName), date(date), author(author), version(version), tabType(tabType)
 {
 
 	OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
@@ -204,6 +204,14 @@ CMapOverviewWidget::CMapOverviewWidget(CMapOverview& parent):
 		else
 			w->setText(p.date);
 	}
+	if(auto w = widget<CLabel>("author"))
+	{
+		w->setText(p.author.empty() ? "-" : p.author);
+	}
+	if(auto w = widget<CLabel>("version"))
+	{
+		w->setText(p.version);
+	}
 	if(auto w = widget<CLabel>("noUnderground"))
 	{
 		if(minimaps.size() == 0)

+ 3 - 1
client/windows/CMapOverview.h

@@ -53,7 +53,9 @@ public:
 	const std::string mapName;
 	const std::string fileName;
 	const std::string date;
+	const std::string author;
+	const std::string version;
 	const ESelectionScreen tabType;
 
-	CMapOverview(std::string mapName, std::string fileName, std::string date, ResourcePath resource, ESelectionScreen tabType);
+	CMapOverview(std::string mapName, std::string fileName, std::string date, std::string author, std::string version, ResourcePath resource, ESelectionScreen tabType);
 };

+ 38 - 4
config/widgets/mapOverview.json

@@ -7,7 +7,7 @@
 			"name": "background",
 			"type": "texture",
 			"image": "DIBOXBCK",
-			"rect": {"w": 428, "h": 379}
+			"rect": {"w": 428, "h": 429}
 		},
 		{
 			"type": "boxWithBackground",
@@ -34,6 +34,15 @@
 			"text": "",
 			"position": {"x": 214, "y": 40}
 		},
+		{
+			"type": "label",
+			"name": "version",
+			"font": "small",
+			"alignment": "right",
+			"color": "green",
+			"text": "",
+			"position": {"x": 418, "y": 40}
+		},
 		{
 			"type": "boxWithBackground",
 			"rect": {"x": 5, "y": 55, "w": 418, "h": 20}
@@ -115,12 +124,37 @@
 			"font": "medium",
 			"alignment": "center",
 			"color": "yellow",
-			"text": "vcmi.lobby.filepath",
+			"text": "vcmi.lobby.author",
 			"position": {"x": 214, "y": 314}
 		},
 		{
 			"type": "boxWithBackground",
-			"rect": {"x": 5, "y": 329, "w": 418, "h": 45}
+			"rect": {"x": 5, "y": 329, "w": 418, "h": 20}
+		},
+		{
+			"type": "label",
+			"name": "author",
+			"font": "small",
+			"alignment": "center",
+			"color": "white",
+			"text": "",
+			"position": {"x": 214, "y": 339}
+		},
+		{
+			"type": "boxWithBackground",
+			"rect": {"x": 5, "y": 354, "w": 418, "h": 20}
+		},
+		{
+			"type": "label",
+			"font": "medium",
+			"alignment": "center",
+			"color": "yellow",
+			"text": "vcmi.lobby.filepath",
+			"position": {"x": 214, "y": 364}
+		},
+		{
+			"type": "boxWithBackground",
+			"rect": {"x": 5, "y": 379, "w": 418, "h": 45}
 		},
 		{
 			"type": "textBox",
@@ -129,7 +163,7 @@
 			"alignment": "center",
 			"color": "white",
 			"text": "",
-			"rect": {"x": 10, "y": 334, "w": 408, "h": 35}
+			"rect": {"x": 10, "y": 384, "w": 408, "h": 35}
 		}
 	],
 

+ 8 - 0
lib/campaign/CampaignHandler.cpp

@@ -151,6 +151,10 @@ void CampaignHandler::readHeaderFromJson(CampaignHeader & ret, JsonNode & reader
 	ret.numberOfScenarios = reader["scenarios"].Vector().size();
 	ret.name.appendTextID(reader["name"].String());
 	ret.description.appendTextID(reader["description"].String());
+	ret.author.appendRawString(reader["author"].String());
+	ret.authorContact.appendRawString(reader["authorContact"].String());
+	ret.mapVersion.appendRawString(reader["mapVersion"].String());
+	ret.creationDateTime = reader["creationDateTime"].Integer();
 	ret.difficultyChosenByPlayer = reader["allowDifficultySelection"].Bool();
 	ret.music = AudioPath::fromJson(reader["music"]);
 	ret.filename = filename;
@@ -385,6 +389,10 @@ void CampaignHandler::readHeaderFromMemory( CampaignHeader & ret, CBinaryReader
 	ret.loadLegacyData(campId);
 	ret.name.appendTextID(readLocalizedString(ret, reader, filename, modName, encoding, "name"));
 	ret.description.appendTextID(readLocalizedString(ret, reader, filename, modName, encoding, "description"));
+	ret.author.appendRawString("");
+	ret.authorContact.appendRawString("");
+	ret.mapVersion.appendRawString("");
+	ret.creationDateTime = 0;
 	if (ret.version > CampaignVersion::RoE)
 		ret.difficultyChosenByPlayer = reader.readInt8();
 	else

+ 11 - 0
lib/campaign/CampaignState.h

@@ -81,6 +81,10 @@ class DLL_LINKAGE CampaignHeader : public boost::noncopyable
 	CampaignRegions campaignRegions;
 	MetaString name;
 	MetaString description;
+	MetaString author;
+	MetaString authorContact;
+	MetaString mapVersion;
+	std::time_t creationDateTime;
 	AudioPath music;
 	std::string filename;
 	std::string modName;
@@ -114,6 +118,13 @@ public:
 		h & numberOfScenarios;
 		h & name;
 		h & description;
+		if (h.version >= Handler::Version::MAP_FORMAT_ADDITIONAL_INFOS)
+		{
+			h & author;
+			h & authorContact;
+			h & mapVersion;
+			h & creationDateTime;
+		}
 		h & difficultyChosenByPlayer;
 		h & filename;
 		h & modName;

+ 11 - 0
lib/mapping/CMapHeader.h

@@ -230,6 +230,10 @@ public:
 	MetaString name;
 	MetaString description;
 	EMapDifficulty difficulty;
+	MetaString author;
+	MetaString authorContact;
+	MetaString mapVersion;
+	std::time_t creationDateTime;
 	/// Specifies the maximum level to reach for a hero. A value of 0 states that there is no
 	///	maximum level for heroes. This is the default value.
 	ui8 levelLimit;
@@ -263,6 +267,13 @@ public:
 		h & mods;
 		h & name;
 		h & description;
+		if (h.version >= Handler::Version::MAP_FORMAT_ADDITIONAL_INFOS)
+		{
+			h & author;
+			h & authorContact;
+			h & mapVersion;
+			h & creationDateTime;
+		}
 		h & width;
 		h & height;
 		h & twoLevel;

+ 4 - 0
lib/mapping/MapFormatH3M.cpp

@@ -219,6 +219,10 @@ void CMapLoaderH3M::readHeader()
 	mapHeader->twoLevel = reader->readBool();
 	mapHeader->name.appendTextID(readLocalizedString("header.name"));
 	mapHeader->description.appendTextID(readLocalizedString("header.description"));
+	mapHeader->author.appendRawString("");
+	mapHeader->authorContact.appendRawString("");
+	mapHeader->mapVersion.appendRawString("");
+	mapHeader->creationDateTime = 0;
 	mapHeader->difficulty = static_cast<EMapDifficulty>(reader->readInt8Checked(0, 4));
 
 	if(features.levelAB)

+ 4 - 1
lib/mapping/MapFormatJson.cpp

@@ -311,6 +311,10 @@ void CMapFormatJson::serializeHeader(JsonSerializeFormat & handler)
 {
 	handler.serializeStruct("name", mapHeader->name);
 	handler.serializeStruct("description", mapHeader->description);
+	handler.serializeStruct("author", mapHeader->author);
+	handler.serializeStruct("authorContact", mapHeader->authorContact);
+	handler.serializeStruct("mapVersion", mapHeader->mapVersion);
+	handler.serializeInt("creationDateTime", mapHeader->creationDateTime, 0);
 	handler.serializeInt("heroLevelLimit", mapHeader->levelLimit, 0);
 
 	//todo: support arbitrary percentage
@@ -855,7 +859,6 @@ void CMapLoaderJson::readHeader(const bool complete)
 	//todo: multilevel map load support
 	{
 		auto levels = handler.enterStruct("mapLevels");
-
 		{
 			auto surface = handler.enterStruct("surface");
 			handler.serializeInt("height", mapHeader->height);

+ 2 - 1
lib/serializer/ESerializationVersion.h

@@ -55,6 +55,7 @@ enum class ESerializationVersion : int32_t
 	COMPACT_INTEGER_SERIALIZATION, // 845 - serialize integers in forms similar to protobuf
 	REMOVE_FOG_OF_WAR_POINTER, // 846 - fog of war is serialized as reference instead of pointer
 	SIMPLE_TEXT_CONTAINER_SERIALIZATION, // 847 - text container is serialized using common routine instead of custom approach
+	MAP_FORMAT_ADDITIONAL_INFOS, // 848 - serialize new infos in map format
 
-	CURRENT = SIMPLE_TEXT_CONTAINER_SERIALIZATION
+	CURRENT = MAP_FORMAT_ADDITIONAL_INFOS
 };

+ 8 - 0
mapeditor/mapsettings/generalsettings.cpp

@@ -29,6 +29,10 @@ void GeneralSettings::initialize(MapController & c)
 	AbstractSettings::initialize(c);
 	ui->mapNameEdit->setText(QString::fromStdString(controller->map()->name.toString()));
 	ui->mapDescriptionEdit->setPlainText(QString::fromStdString(controller->map()->description.toString()));
+	ui->authorEdit->setText(QString::fromStdString(controller->map()->author.toString()));
+	ui->authorContactEdit->setText(QString::fromStdString(controller->map()->authorContact.toString()));
+	ui->mapCreationDateTimeEdit->setDateTime(QDateTime::fromTime_t(controller->map()->creationDateTime));
+	ui->mapVersionEdit->setText(QString::fromStdString(controller->map()->mapVersion.toString()));
 	ui->heroLevelLimit->setValue(controller->map()->levelLimit);
 	ui->heroLevelLimitCheck->setChecked(controller->map()->levelLimit);
 
@@ -61,6 +65,10 @@ void GeneralSettings::update()
 {
 	controller->map()->name = MetaString::createFromTextID(mapRegisterLocalizedString("map", *controller->map(), TextIdentifier("header", "name"),  ui->mapNameEdit->text().toStdString()));
 	controller->map()->description = MetaString::createFromTextID(mapRegisterLocalizedString("map", *controller->map(), TextIdentifier("header", "description"), ui->mapDescriptionEdit->toPlainText().toStdString()));
+	controller->map()->author = MetaString::createFromRawString(ui->authorEdit->toPlainText().toStdString());
+	controller->map()->authorContact = MetaString::createFromRawString(ui->authorContactEdit->toPlainText().toStdString());
+	controller->map()->creationDateTime = ui->mapCreationDateTimeEdit->dateTime().toTime_t();
+	controller->map()->mapVersion = MetaString::createFromRawString(ui->mapVersionEdit->toPlainText().toStdString());
 	if(ui->heroLevelLimitCheck->isChecked())
 		controller->map()->levelLimit = ui->heroLevelLimit->value();
 	else

+ 40 - 0
mapeditor/mapsettings/generalsettings.ui

@@ -46,6 +46,46 @@
    <item>
     <widget class="QPlainTextEdit" name="mapDescriptionEdit"/>
    </item>
+   <item>
+    <widget class="QLabel" name="label_3">
+     <property name="text">
+      <string>Author</string>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="QLineEdit" name="authorEdit"/>
+   </item>
+   <item>
+    <widget class="QLabel" name="label_4">
+     <property name="text">
+      <string>Author contact (e.g. email)</string>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="QLineEdit" name="authorContactEdit"/>
+   </item>
+   <item>
+    <widget class="QLabel" name="label_5">
+     <property name="text">
+      <string>Map Creation Time</string>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="QDateTimeEdit" name="mapCreationDateTimeEdit"/>
+   </item>
+   <item>
+    <widget class="QLabel" name="label_6">
+     <property name="text">
+      <string>Map Version</string>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="QLineEdit" name="mapVersionEdit"/>
+   </item>
    <item>
     <layout class="QHBoxLayout" name="horizontalLayout_4">
      <property name="topMargin">