diff --git a/client/lobby/RandomMapTab.cpp b/client/lobby/RandomMapTab.cpp index 42e772b89..eac1d79dd 100644 --- a/client/lobby/RandomMapTab.cpp +++ b/client/lobby/RandomMapTab.cpp @@ -28,6 +28,7 @@ #include "../../lib/CGeneralTextHandler.h" #include "../../lib/mapping/CMapInfo.h" #include "../../lib/mapping/CMap.h" +#include "../../lib/mapping/MapFormat.h" #include "../../lib/rmg/CMapGenOptions.h" #include "../../lib/CModHandler.h" #include "../../lib/rmg/CRmgTemplateStorage.h" diff --git a/client/lobby/SelectionTab.cpp b/client/lobby/SelectionTab.cpp index 3e42998b6..c07b366bd 100644 --- a/client/lobby/SelectionTab.cpp +++ b/client/lobby/SelectionTab.cpp @@ -37,6 +37,7 @@ #include "../../lib/filesystem/Filesystem.h" #include "../../lib/mapping/CMapInfo.h" #include "../../lib/mapping/CMap.h" +#include "../../lib/mapping/MapFormat.h" #include "../../lib/mapping/CCampaignHandler.h" #include "../../lib/serializer/Connection.h" diff --git a/cmake_modules/VCMI_lib.cmake b/cmake_modules/VCMI_lib.cmake index 3fd3997d9..640e264aa 100644 --- a/cmake_modules/VCMI_lib.cmake +++ b/cmake_modules/VCMI_lib.cmake @@ -85,6 +85,7 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE) ${MAIN_LIB_DIR}/mapping/CCampaignHandler.cpp ${MAIN_LIB_DIR}/mapping/CDrawRoadsOperation.cpp ${MAIN_LIB_DIR}/mapping/CMap.cpp + ${MAIN_LIB_DIR}/mapping/CMapHeader.cpp ${MAIN_LIB_DIR}/mapping/CMapEditManager.cpp ${MAIN_LIB_DIR}/mapping/CMapInfo.cpp ${MAIN_LIB_DIR}/mapping/CMapOperation.cpp @@ -206,6 +207,7 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE) ${MAIN_LIB_DIR}/CGeneralTextHandler.cpp ${MAIN_LIB_DIR}/CHeroHandler.cpp ${MAIN_LIB_DIR}/CModHandler.cpp + ${MAIN_LIB_DIR}/CModVersion.cpp ${MAIN_LIB_DIR}/CPathfinder.cpp ${MAIN_LIB_DIR}/CPlayerState.cpp ${MAIN_LIB_DIR}/CRandomGenerator.cpp @@ -382,6 +384,7 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE) ${MAIN_LIB_DIR}/mapping/CDrawRoadsOperation.h ${MAIN_LIB_DIR}/mapping/CMapDefines.h ${MAIN_LIB_DIR}/mapping/CMapEditManager.h + ${MAIN_LIB_DIR}/mapping/CMapHeader.h ${MAIN_LIB_DIR}/mapping/CMap.h ${MAIN_LIB_DIR}/mapping/CMapInfo.h ${MAIN_LIB_DIR}/mapping/CMapOperation.h @@ -390,6 +393,7 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE) ${MAIN_LIB_DIR}/mapping/MapIdentifiersH3M.h ${MAIN_LIB_DIR}/mapping/MapFeaturesH3M.h ${MAIN_LIB_DIR}/mapping/MapFormatH3M.h + ${MAIN_LIB_DIR}/mapping/MapFormat.h ${MAIN_LIB_DIR}/mapping/MapReaderH3M.h ${MAIN_LIB_DIR}/mapping/MapFormatJson.h ${MAIN_LIB_DIR}/mapping/ObstacleProxy.h @@ -499,6 +503,7 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE) ${MAIN_LIB_DIR}/CGeneralTextHandler.h ${MAIN_LIB_DIR}/CHeroHandler.h ${MAIN_LIB_DIR}/CModHandler.h + ${MAIN_LIB_DIR}/CModVersion.h ${MAIN_LIB_DIR}/CondSh.h ${MAIN_LIB_DIR}/ConstTransitivePtr.h ${MAIN_LIB_DIR}/Color.h diff --git a/lib/CModHandler.cpp b/lib/CModHandler.cpp index 976ea2660..4e2837d60 100644 --- a/lib/CModHandler.cpp +++ b/lib/CModHandler.cpp @@ -582,53 +582,6 @@ JsonNode addMeta(JsonNode config, const std::string & meta) return config; } -CModInfo::Version CModInfo::Version::GameVersion() -{ - return Version(VCMI_VERSION_MAJOR, VCMI_VERSION_MINOR, VCMI_VERSION_PATCH); -} - -CModInfo::Version CModInfo::Version::fromString(std::string from) -{ - int major = 0; - int minor = 0; - int patch = 0; - try - { - auto pointPos = from.find('.'); - major = std::stoi(from.substr(0, pointPos)); - if(pointPos != std::string::npos) - { - from = from.substr(pointPos + 1); - pointPos = from.find('.'); - minor = std::stoi(from.substr(0, pointPos)); - if(pointPos != std::string::npos) - patch = std::stoi(from.substr(pointPos + 1)); - } - } - catch(const std::invalid_argument &) - { - return Version(); - } - return Version(major, minor, patch); -} - -std::string CModInfo::Version::toString() const -{ - return std::to_string(major) + '.' + std::to_string(minor) + '.' + std::to_string(patch); -} - -bool CModInfo::Version::compatible(const Version & other, bool checkMinor, bool checkPatch) const -{ - return (major == other.major && - (!checkMinor || minor >= other.minor) && - (!checkPatch || minor > other.minor || (minor == other.minor && patch >= other.patch))); -} - -bool CModInfo::Version::isNull() const -{ - return major == 0 && minor == 0 && patch == 0; -} - CModInfo::CModInfo(): checksum(0), explicitlyEnabled(false), @@ -650,11 +603,11 @@ CModInfo::CModInfo(const std::string & identifier, const JsonNode & local, const validation(PENDING), config(addMeta(config, identifier)) { - version = Version::fromString(config["version"].String()); + version = CModVersion::fromString(config["version"].String()); if(!config["compatibility"].isNull()) { - vcmiCompatibleMin = Version::fromString(config["compatibility"]["min"].String()); - vcmiCompatibleMax = Version::fromString(config["compatibility"]["max"].String()); + vcmiCompatibleMin = CModVersion::fromString(config["compatibility"]["min"].String()); + vcmiCompatibleMax = CModVersion::fromString(config["compatibility"]["max"].String()); } if (!config["language"].isNull()) @@ -715,8 +668,8 @@ void CModInfo::loadLocalData(const JsonNode & data) } //check compatibility - implicitlyEnabled &= (vcmiCompatibleMin.isNull() || Version::GameVersion().compatible(vcmiCompatibleMin)); - implicitlyEnabled &= (vcmiCompatibleMax.isNull() || vcmiCompatibleMax.compatible(Version::GameVersion())); + implicitlyEnabled &= (vcmiCompatibleMin.isNull() || CModVersion::GameVersion().compatible(vcmiCompatibleMin)); + implicitlyEnabled &= (vcmiCompatibleMax.isNull() || vcmiCompatibleMax.compatible(CModVersion::GameVersion())); if(!implicitlyEnabled) logGlobal->warn("Mod %s is incompatible with current version of VCMI and cannot be enabled", name); diff --git a/lib/CModHandler.h b/lib/CModHandler.h index 04eb645cb..30325f246 100644 --- a/lib/CModHandler.h +++ b/lib/CModHandler.h @@ -10,6 +10,7 @@ #pragma once #include "JsonNode.h" +#include "CModVersion.h" #ifdef __UCLIBC__ #undef major @@ -185,30 +186,6 @@ public: FAILED, PASSED }; - - struct DLL_LINKAGE Version - { - int major = 0; - int minor = 0; - int patch = 0; - - Version() = default; - Version(int mj, int mi, int p): major(mj), minor(mi), patch(p) {} - - static Version GameVersion(); - static Version fromString(std::string from); - std::string toString() const; - - bool compatible(const Version & other, bool checkMinor = false, bool checkPatch = false) const; - bool isNull() const; - - template void serialize(Handler &h, const int version) - { - h & major; - h & minor; - h & patch; - } - }; /// identifier, identical to name of folder with mod std::string identifier; @@ -218,14 +195,14 @@ public: std::string description; /// version of the mod - Version version; + CModVersion version; /// Base language of mod, all mod strings are assumed to be in this language std::string baseLanguage; /// vcmi versions compatible with the mod - Version vcmiCompatibleMin, vcmiCompatibleMax; + CModVersion vcmiCompatibleMin, vcmiCompatibleMax; /// list of mods that should be loaded before this one std::set dependencies; @@ -381,7 +358,7 @@ public: for(const auto & m : newActiveMods) { - CModInfo::Version mver; + CModVersion mver; h & mver; if(allMods.count(m) && (allMods[m].version.isNull() || mver.isNull() || allMods[m].version.compatible(mver))) diff --git a/lib/CModVersion.cpp b/lib/CModVersion.cpp new file mode 100644 index 000000000..0624dc1e7 --- /dev/null +++ b/lib/CModVersion.cpp @@ -0,0 +1,63 @@ +/* + * CModVersion.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 + * + */ + +#include "StdInc.h" +#include "CModVersion.h" + +VCMI_LIB_NAMESPACE_BEGIN + +CModVersion CModVersion::GameVersion() +{ + return CModVersion(VCMI_VERSION_MAJOR, VCMI_VERSION_MINOR, VCMI_VERSION_PATCH); +} + +CModVersion CModVersion::fromString(std::string from) +{ + int major = 0; + int minor = 0; + int patch = 0; + try + { + auto pointPos = from.find('.'); + major = std::stoi(from.substr(0, pointPos)); + if(pointPos != std::string::npos) + { + from = from.substr(pointPos + 1); + pointPos = from.find('.'); + minor = std::stoi(from.substr(0, pointPos)); + if(pointPos != std::string::npos) + patch = std::stoi(from.substr(pointPos + 1)); + } + } + catch(const std::invalid_argument &) + { + return CModVersion(); + } + return CModVersion(major, minor, patch); +} + +std::string CModVersion::toString() const +{ + return std::to_string(major) + '.' + std::to_string(minor) + '.' + std::to_string(patch); +} + +bool CModVersion::compatible(const CModVersion & other, bool checkMinor, bool checkPatch) const +{ + return (major == other.major && + (!checkMinor || minor >= other.minor) && + (!checkPatch || minor > other.minor || (minor == other.minor && patch >= other.patch))); +} + +bool CModVersion::isNull() const +{ + return major == 0 && minor == 0 && patch == 0; +} + +VCMI_LIB_NAMESPACE_END diff --git a/lib/CModVersion.h b/lib/CModVersion.h new file mode 100644 index 000000000..877615f27 --- /dev/null +++ b/lib/CModVersion.h @@ -0,0 +1,39 @@ +/* + * CModVersion.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 + +VCMI_LIB_NAMESPACE_BEGIN + +struct DLL_LINKAGE CModVersion +{ + int major = 0; + int minor = 0; + int patch = 0; + + CModVersion() = default; + CModVersion(int mj, int mi, int p): major(mj), minor(mi), patch(p) {} + + static CModVersion GameVersion(); + static CModVersion fromString(std::string from); + std::string toString() const; + + bool compatible(const CModVersion & other, bool checkMinor = false, bool checkPatch = false) const; + bool isNull() const; + + template void serialize(Handler &h, const int version) + { + h & major; + h & minor; + h & patch; + } +}; + +VCMI_LIB_NAMESPACE_END diff --git a/lib/StartInfo.cpp b/lib/StartInfo.cpp index 57c35f4f2..071c8dc14 100644 --- a/lib/StartInfo.cpp +++ b/lib/StartInfo.cpp @@ -11,6 +11,7 @@ #include "StartInfo.h" #include "CGeneralTextHandler.h" +#include "CModHandler.h" #include "rmg/CMapGenOptions.h" #include "mapping/CMapInfo.h" #include "mapping/CCampaignHandler.h" diff --git a/lib/mapping/CMap.cpp b/lib/mapping/CMap.cpp index 7cf16ef0c..b9554ebab 100644 --- a/lib/mapping/CMap.cpp +++ b/lib/mapping/CMap.cpp @@ -29,74 +29,6 @@ VCMI_LIB_NAMESPACE_BEGIN -SHeroName::SHeroName() : heroId(-1) -{ - -} - -PlayerInfo::PlayerInfo(): canHumanPlay(false), canComputerPlay(false), - aiTactic(EAiTactic::RANDOM), isFactionRandom(false), hasRandomHero(false), mainCustomHeroPortrait(-1), mainCustomHeroId(-1), hasMainTown(false), - generateHeroAtMainTown(false), posOfMainTown(-1), team(TeamID::NO_TEAM) -{ - allowedFactions = VLC->townh->getAllowedFactions(); -} - -si8 PlayerInfo::defaultCastle() const -{ - //if random allowed set it as default - if(isFactionRandom) - return -1; - - if(!allowedFactions.empty()) - return *allowedFactions.begin(); - - // fall back to random - return -1; -} - -si8 PlayerInfo::defaultHero() const -{ - // we will generate hero in front of main town - if((generateHeroAtMainTown && hasMainTown) || hasRandomHero) - { - //random hero - return -1; - } - - return -2; -} - -bool PlayerInfo::canAnyonePlay() const -{ - return canHumanPlay || canComputerPlay; -} - -bool PlayerInfo::hasCustomMainHero() const -{ - return !mainCustomHeroName.empty() && mainCustomHeroPortrait != -1; -} - -EventCondition::EventCondition(EWinLoseType condition): - object(nullptr), - metaType(EMetaclass::INVALID), - value(-1), - objectType(-1), - objectSubtype(-1), - position(-1, -1, -1), - condition(condition) -{ -} - -EventCondition::EventCondition(EWinLoseType condition, si32 value, si32 objectType, const int3 & position): - object(nullptr), - metaType(EMetaclass::INVALID), - value(value), - objectType(objectType), - objectSubtype(-1), - position(position), - condition(condition) -{} - void Rumor::serializeJson(JsonSerializeFormat & handler) { handler.serializeString("name", name); @@ -196,53 +128,6 @@ bool TerrainTile::isWater() const return terType->isWater(); } -void CMapHeader::setupEvents() -{ - EventCondition victoryCondition(EventCondition::STANDARD_WIN); - EventCondition defeatCondition(EventCondition::DAYS_WITHOUT_TOWN); - defeatCondition.value = 7; - - //Victory condition - defeat all - TriggeredEvent standardVictory; - standardVictory.effect.type = EventEffect::VICTORY; - standardVictory.effect.toOtherMessage = VLC->generaltexth->allTexts[5]; - standardVictory.identifier = "standardVictory"; - standardVictory.description.clear(); // TODO: display in quest window - standardVictory.onFulfill = VLC->generaltexth->allTexts[659]; - standardVictory.trigger = EventExpression(victoryCondition); - - //Loss condition - 7 days without town - TriggeredEvent standardDefeat; - standardDefeat.effect.type = EventEffect::DEFEAT; - standardDefeat.effect.toOtherMessage = VLC->generaltexth->allTexts[8]; - standardDefeat.identifier = "standardDefeat"; - standardDefeat.description.clear(); // TODO: display in quest window - standardDefeat.onFulfill = VLC->generaltexth->allTexts[7]; - standardDefeat.trigger = EventExpression(defeatCondition); - - triggeredEvents.push_back(standardVictory); - triggeredEvents.push_back(standardDefeat); - - victoryIconIndex = 11; - victoryMessage = VLC->generaltexth->victoryConditions[0]; - - defeatIconIndex = 3; - defeatMessage = VLC->generaltexth->lossCondtions[0]; -} - -CMapHeader::CMapHeader() : version(EMapFormat::VCMI), height(72), width(72), - twoLevel(true), difficulty(1), levelLimit(0), howManyTeams(0), areAnyPlayers(false) -{ - setupEvents(); - allowedHeroes = VLC->heroh->getDefaultAllowed(); - players.resize(PlayerColor::PLAYER_LIMIT_I); -} - -ui8 CMapHeader::levels() const -{ - return (twoLevel ? 2 : 1); -} - CMap::CMap() : checksum(0), grailPos(-1, -1, -1), grailRadius(0), terrain(nullptr), guardingCreaturePositions(nullptr), diff --git a/lib/mapping/CMap.h b/lib/mapping/CMap.h index fc217401d..1638ff3b3 100644 --- a/lib/mapping/CMap.h +++ b/lib/mapping/CMap.h @@ -10,15 +10,10 @@ #pragma once -#include "../ConstTransitivePtr.h" +#include "CMapHeader.h" #include "../mapObjects/MiscObjects.h" // To serialize static props #include "../mapObjects/CQuest.h" // To serialize static props #include "../mapObjects/CGTownInstance.h" // To serialize static props -#include "../ResourceSet.h" -#include "../int3.h" -#include "../GameConstants.h" -#include "../LogicalExpression.h" -#include "../CModHandler.h" #include "CMapDefines.h" VCMI_LIB_NAMESPACE_BEGIN @@ -34,183 +29,8 @@ class IModableArt; class IQuestObject; class CInputStream; class CMapEditManager; - -/// The hero name struct consists of the hero id and the hero name. -struct DLL_LINKAGE SHeroName -{ - SHeroName(); - - int heroId; - std::string heroName; - - template - void serialize(Handler & h, const int version) - { - h & heroId; - h & heroName; - } -}; - -/// The player info constains data about which factions are allowed, AI tactical settings, -/// the main hero name, where to generate the hero, whether the faction should be selected randomly,... -struct DLL_LINKAGE PlayerInfo -{ - PlayerInfo(); - - /// Gets the default faction id or -1 for a random faction. - si8 defaultCastle() const; - /// Gets the default hero id or -1 for a random hero. - si8 defaultHero() const; - bool canAnyonePlay() const; - bool hasCustomMainHero() const; - - bool canHumanPlay; - bool canComputerPlay; - EAiTactic::EAiTactic aiTactic; /// The default value is EAiTactic::RANDOM. - - std::set allowedFactions; - bool isFactionRandom; - - ///main hero instance (VCMI maps only) - std::string mainHeroInstance; - /// Player has a random main hero - bool hasRandomHero; - /// The default value is -1. - si32 mainCustomHeroPortrait; - std::string mainCustomHeroName; - /// ID of custom hero (only if portrait and hero name are set, otherwise unpredicted value), -1 if none (not always -1) - si32 mainCustomHeroId; - - std::vector heroesNames; /// list of placed heroes on the map - bool hasMainTown; /// The default value is false. - bool generateHeroAtMainTown; /// The default value is false. - int3 posOfMainTown; - TeamID team; /// The default value NO_TEAM - - template - void serialize(Handler & h, const int version) - { - h & hasRandomHero; - h & mainCustomHeroId; - h & canHumanPlay; - h & canComputerPlay; - h & aiTactic; - h & allowedFactions; - h & isFactionRandom; - h & mainCustomHeroPortrait; - h & mainCustomHeroName; - h & heroesNames; - h & hasMainTown; - h & generateHeroAtMainTown; - h & posOfMainTown; - h & team; - h & mainHeroInstance; - } -}; - -/// The loss condition describes the condition to lose the game. (e.g. lose all own heroes/castles) -struct DLL_LINKAGE EventCondition -{ - enum EWinLoseType { - //internal use, deprecated - HAVE_ARTIFACT, // type - required artifact - HAVE_CREATURES, // type - creatures to collect, value - amount to collect - HAVE_RESOURCES, // type - resource ID, value - amount to collect - HAVE_BUILDING, // position - town, optional, type - building to build - CONTROL, // position - position of object, optional, type - type of object - DESTROY, // position - position of object, optional, type - type of object - TRANSPORT, // position - where artifact should be transported, type - type of artifact - - //map format version pre 1.0 - DAYS_PASSED, // value - number of days from start of the game - IS_HUMAN, // value - 0 = player is AI, 1 = player is human - DAYS_WITHOUT_TOWN, // value - how long player can live without town, 0=instakill - STANDARD_WIN, // normal defeat all enemies condition - CONST_VALUE, // condition that always evaluates to "value" (0 = false, 1 = true) - - //map format version 1.0+ - HAVE_0, - HAVE_BUILDING_0, - DESTROY_0 - }; - - EventCondition(EWinLoseType condition = STANDARD_WIN); - EventCondition(EWinLoseType condition, si32 value, si32 objectType, const int3 & position = int3(-1, -1, -1)); - - const CGObjectInstance * object; // object that was at specified position or with instance name on start - EMetaclass metaType; - si32 value; - si32 objectType; - si32 objectSubtype; - std::string objectInstanceName; - int3 position; - EWinLoseType condition; - - template - void serialize(Handler & h, const int version) - { - h & object; - h & value; - h & objectType; - h & position; - h & condition; - h & objectSubtype; - h & objectInstanceName; - h & metaType; - } -}; - -using EventExpression = LogicalExpression; - -struct DLL_LINKAGE EventEffect -{ - enum EType - { - VICTORY, - DEFEAT - }; - - /// effect type, using EType enum - si8 type; - - /// message that will be sent to other players - std::string toOtherMessage; - - template - void serialize(Handler & h, const int version) - { - h & type; - h & toOtherMessage; - } -}; - -struct DLL_LINKAGE TriggeredEvent -{ - /// base condition that must be evaluated - EventExpression trigger; - - /// string identifier read from config file (e.g. captureKreelah) - std::string identifier; - - /// string-description, for use in UI (capture town to win) - std::string description; - - /// Message that will be displayed when this event is triggered (You captured town. You won!) - std::string onFulfill; - - /// Effect of this event. TODO: refactor into something more flexible - EventEffect effect; - - template - void serialize(Handler & h, const int version) - { - h & identifier; - h & trigger; - h & description; - h & onFulfill; - h & effect; - } -}; +class JsonSerializeFormat; +struct TeleportChannel; /// The rumor struct consists of a rumor name and text. struct DLL_LINKAGE Rumor @@ -251,94 +71,6 @@ struct DLL_LINKAGE DisposedHero } }; -enum class EMapFormat: uint8_t -{ - INVALID = 0, - // HEX DEC - ROE = 0x0e, // 14 - AB = 0x15, // 21 - SOD = 0x1c, // 28 -// CHR = 0x1d, // 29 Heroes Chronicles, presumably - identical to SoD, untested - HOTA = 0x20, // 32 - WOG = 0x33, // 51 - VCMI = 0x64 -}; - -// Inherit from container to enable forward declaration -class ModCompatibilityInfo: public std::map -{}; - -/// The map header holds information about loss/victory condition,map format, version, players, height, width,... -class DLL_LINKAGE CMapHeader -{ - void setupEvents(); -public: - - static const int MAP_SIZE_SMALL = 36; - 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() = default; - - ui8 levels() const; - - EMapFormat version; /// The default value is EMapFormat::SOD. - ModCompatibilityInfo mods; /// set of mods required to play a map - - si32 height; /// The default value is 72. - si32 width; /// The default value is 72. - bool twoLevel; /// The default value is true. - std::string name; - std::string description; - ui8 difficulty; /// The default value is 1 representing a normal map difficulty. - /// 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; - - std::string victoryMessage; - std::string defeatMessage; - ui16 victoryIconIndex; - ui16 defeatIconIndex; - - std::vector players; /// The default size of the vector is PlayerColor::PLAYER_LIMIT. - ui8 howManyTeams; - std::vector allowedHeroes; - - bool areAnyPlayers; /// Unused. True if there are any playable players on the map. - - /// "main quests" of the map that describe victory and loss conditions - std::vector triggeredEvents; - - template - void serialize(Handler & h, const int Version) - { - h & version; - if(Version >= 821) - h & mods; - h & name; - h & description; - h & width; - h & height; - h & twoLevel; - h & difficulty; - h & levelLimit; - h & areAnyPlayers; - h & players; - h & howManyTeams; - h & allowedHeroes; - //Do not serialize triggeredEvents in header as they can contain information about heroes and armies - h & victoryMessage; - h & victoryIconIndex; - h & defeatMessage; - h & defeatIconIndex; - } -}; - /// The map contains the map header, the tiles of the terrain, objects, heroes, towns, rumors... class DLL_LINKAGE CMap : public CMapHeader { diff --git a/lib/mapping/CMapDefines.h b/lib/mapping/CMapDefines.h index 52d6885d5..14c7512b9 100644 --- a/lib/mapping/CMapDefines.h +++ b/lib/mapping/CMapDefines.h @@ -12,9 +12,13 @@ VCMI_LIB_NAMESPACE_BEGIN +#include "../ResourceSet.h" + class TerrainType; class RiverType; class RoadType; +class CGObjectInstance; +class CGTownInstance; /// The map event is an event which e.g. gives or takes resources of a specific /// amount to/from players and can appear regularly or once a time. diff --git a/lib/mapping/CMapHeader.cpp b/lib/mapping/CMapHeader.cpp new file mode 100644 index 000000000..88741fc87 --- /dev/null +++ b/lib/mapping/CMapHeader.cpp @@ -0,0 +1,133 @@ +/* + * CMapHeader.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 "CMapHeader.h" + +#include "MapFormat.h" + +#include "../VCMI_Lib.h" +#include "../CTownHandler.h" +#include "../CGeneralTextHandler.h" +#include "../CHeroHandler.h" + +SHeroName::SHeroName() : heroId(-1) +{ +} + +PlayerInfo::PlayerInfo(): canHumanPlay(false), canComputerPlay(false), + aiTactic(EAiTactic::RANDOM), isFactionRandom(false), hasRandomHero(false), mainCustomHeroPortrait(-1), mainCustomHeroId(-1), hasMainTown(false), + generateHeroAtMainTown(false), posOfMainTown(-1), team(TeamID::NO_TEAM) +{ + allowedFactions = VLC->townh->getAllowedFactions(); +} + +si8 PlayerInfo::defaultCastle() const +{ + //if random allowed set it as default + if(isFactionRandom) + return -1; + + if(!allowedFactions.empty()) + return *allowedFactions.begin(); + + // fall back to random + return -1; +} + +si8 PlayerInfo::defaultHero() const +{ + // we will generate hero in front of main town + if((generateHeroAtMainTown && hasMainTown) || hasRandomHero) + { + //random hero + return -1; + } + + return -2; +} + +bool PlayerInfo::canAnyonePlay() const +{ + return canHumanPlay || canComputerPlay; +} + +bool PlayerInfo::hasCustomMainHero() const +{ + return !mainCustomHeroName.empty() && mainCustomHeroPortrait != -1; +} + +EventCondition::EventCondition(EWinLoseType condition): + object(nullptr), + metaType(EMetaclass::INVALID), + value(-1), + objectType(-1), + objectSubtype(-1), + position(-1, -1, -1), + condition(condition) +{ +} + +EventCondition::EventCondition(EWinLoseType condition, si32 value, si32 objectType, const int3 & position): + object(nullptr), + metaType(EMetaclass::INVALID), + value(value), + objectType(objectType), + objectSubtype(-1), + position(position), + condition(condition) +{} + + +void CMapHeader::setupEvents() +{ + EventCondition victoryCondition(EventCondition::STANDARD_WIN); + EventCondition defeatCondition(EventCondition::DAYS_WITHOUT_TOWN); + defeatCondition.value = 7; + + //Victory condition - defeat all + TriggeredEvent standardVictory; + standardVictory.effect.type = EventEffect::VICTORY; + standardVictory.effect.toOtherMessage = VLC->generaltexth->allTexts[5]; + standardVictory.identifier = "standardVictory"; + standardVictory.description.clear(); // TODO: display in quest window + standardVictory.onFulfill = VLC->generaltexth->allTexts[659]; + standardVictory.trigger = EventExpression(victoryCondition); + + //Loss condition - 7 days without town + TriggeredEvent standardDefeat; + standardDefeat.effect.type = EventEffect::DEFEAT; + standardDefeat.effect.toOtherMessage = VLC->generaltexth->allTexts[8]; + standardDefeat.identifier = "standardDefeat"; + standardDefeat.description.clear(); // TODO: display in quest window + standardDefeat.onFulfill = VLC->generaltexth->allTexts[7]; + standardDefeat.trigger = EventExpression(defeatCondition); + + triggeredEvents.push_back(standardVictory); + triggeredEvents.push_back(standardDefeat); + + victoryIconIndex = 11; + victoryMessage = VLC->generaltexth->victoryConditions[0]; + + defeatIconIndex = 3; + defeatMessage = VLC->generaltexth->lossCondtions[0]; +} + +CMapHeader::CMapHeader() : version(EMapFormat::VCMI), height(72), width(72), + twoLevel(true), difficulty(1), levelLimit(0), howManyTeams(0), areAnyPlayers(false) +{ + setupEvents(); + allowedHeroes = VLC->heroh->getDefaultAllowed(); + players.resize(PlayerColor::PLAYER_LIMIT_I); +} + +ui8 CMapHeader::levels() const +{ + return (twoLevel ? 2 : 1); +} diff --git a/lib/mapping/CMapHeader.h b/lib/mapping/CMapHeader.h new file mode 100644 index 000000000..1b33c6618 --- /dev/null +++ b/lib/mapping/CMapHeader.h @@ -0,0 +1,271 @@ +/* + * CMapHeader.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 "../CModVersion.h" +#include "../LogicalExpression.h" +#include "../int3.h" + +VCMI_LIB_NAMESPACE_BEGIN + +class CGObjectInstance; +enum class EMapFormat : uint8_t; +using ModCompatibilityInfo = std::map; + +/// The hero name struct consists of the hero id and the hero name. +struct DLL_LINKAGE SHeroName +{ + SHeroName(); + + int heroId; + std::string heroName; + + template + void serialize(Handler & h, const int version) + { + h & heroId; + h & heroName; + } +}; + +/// The player info constains data about which factions are allowed, AI tactical settings, +/// the main hero name, where to generate the hero, whether the faction should be selected randomly,... +struct DLL_LINKAGE PlayerInfo +{ + PlayerInfo(); + + /// Gets the default faction id or -1 for a random faction. + si8 defaultCastle() const; + /// Gets the default hero id or -1 for a random hero. + si8 defaultHero() const; + bool canAnyonePlay() const; + bool hasCustomMainHero() const; + + bool canHumanPlay; + bool canComputerPlay; + EAiTactic::EAiTactic aiTactic; /// The default value is EAiTactic::RANDOM. + + std::set allowedFactions; + bool isFactionRandom; + + ///main hero instance (VCMI maps only) + std::string mainHeroInstance; + /// Player has a random main hero + bool hasRandomHero; + /// The default value is -1. + si32 mainCustomHeroPortrait; + std::string mainCustomHeroName; + /// ID of custom hero (only if portrait and hero name are set, otherwise unpredicted value), -1 if none (not always -1) + si32 mainCustomHeroId; + + std::vector heroesNames; /// list of placed heroes on the map + bool hasMainTown; /// The default value is false. + bool generateHeroAtMainTown; /// The default value is false. + int3 posOfMainTown; + TeamID team; /// The default value NO_TEAM + + template + void serialize(Handler & h, const int version) + { + h & hasRandomHero; + h & mainCustomHeroId; + h & canHumanPlay; + h & canComputerPlay; + h & aiTactic; + h & allowedFactions; + h & isFactionRandom; + h & mainCustomHeroPortrait; + h & mainCustomHeroName; + h & heroesNames; + h & hasMainTown; + h & generateHeroAtMainTown; + h & posOfMainTown; + h & team; + h & mainHeroInstance; + } +}; + +/// The loss condition describes the condition to lose the game. (e.g. lose all own heroes/castles) +struct DLL_LINKAGE EventCondition +{ + enum EWinLoseType { + //internal use, deprecated + HAVE_ARTIFACT, // type - required artifact + HAVE_CREATURES, // type - creatures to collect, value - amount to collect + HAVE_RESOURCES, // type - resource ID, value - amount to collect + HAVE_BUILDING, // position - town, optional, type - building to build + CONTROL, // position - position of object, optional, type - type of object + DESTROY, // position - position of object, optional, type - type of object + TRANSPORT, // position - where artifact should be transported, type - type of artifact + + //map format version pre 1.0 + DAYS_PASSED, // value - number of days from start of the game + IS_HUMAN, // value - 0 = player is AI, 1 = player is human + DAYS_WITHOUT_TOWN, // value - how long player can live without town, 0=instakill + STANDARD_WIN, // normal defeat all enemies condition + CONST_VALUE, // condition that always evaluates to "value" (0 = false, 1 = true) + + //map format version 1.0+ + HAVE_0, + HAVE_BUILDING_0, + DESTROY_0 + }; + + EventCondition(EWinLoseType condition = STANDARD_WIN); + EventCondition(EWinLoseType condition, si32 value, si32 objectType, const int3 & position = int3(-1, -1, -1)); + + const CGObjectInstance * object; // object that was at specified position or with instance name on start + EMetaclass metaType; + si32 value; + si32 objectType; + si32 objectSubtype; + std::string objectInstanceName; + int3 position; + EWinLoseType condition; + + template + void serialize(Handler & h, const int version) + { + h & object; + h & value; + h & objectType; + h & position; + h & condition; + h & objectSubtype; + h & objectInstanceName; + h & metaType; + } +}; + +using EventExpression = LogicalExpression; + +struct DLL_LINKAGE EventEffect +{ + enum EType + { + VICTORY, + DEFEAT + }; + + /// effect type, using EType enum + si8 type; + + /// message that will be sent to other players + std::string toOtherMessage; + + template + void serialize(Handler & h, const int version) + { + h & type; + h & toOtherMessage; + } +}; + +struct DLL_LINKAGE TriggeredEvent +{ + /// base condition that must be evaluated + EventExpression trigger; + + /// string identifier read from config file (e.g. captureKreelah) + std::string identifier; + + /// string-description, for use in UI (capture town to win) + std::string description; + + /// Message that will be displayed when this event is triggered (You captured town. You won!) + std::string onFulfill; + + /// Effect of this event. TODO: refactor into something more flexible + EventEffect effect; + + template + void serialize(Handler & h, const int version) + { + h & identifier; + h & trigger; + h & description; + h & onFulfill; + h & effect; + } +}; + +/// The map header holds information about loss/victory condition,map format, version, players, height, width,... +class DLL_LINKAGE CMapHeader +{ + void setupEvents(); +public: + + static const int MAP_SIZE_SMALL = 36; + 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() = default; + + ui8 levels() const; + + EMapFormat version; /// The default value is EMapFormat::SOD. + ModCompatibilityInfo mods; /// set of mods required to play a map + + si32 height; /// The default value is 72. + si32 width; /// The default value is 72. + bool twoLevel; /// The default value is true. + std::string name; + std::string description; + ui8 difficulty; /// The default value is 1 representing a normal map difficulty. + /// 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; + + std::string victoryMessage; + std::string defeatMessage; + ui16 victoryIconIndex; + ui16 defeatIconIndex; + + std::vector players; /// The default size of the vector is PlayerColor::PLAYER_LIMIT. + ui8 howManyTeams; + std::vector allowedHeroes; + + bool areAnyPlayers; /// Unused. True if there are any playable players on the map. + + /// "main quests" of the map that describe victory and loss conditions + std::vector triggeredEvents; + + template + void serialize(Handler & h, const int Version) + { + h & version; + if(Version >= 821) + h & mods; + h & name; + h & description; + h & width; + h & height; + h & twoLevel; + h & difficulty; + h & levelLimit; + h & areAnyPlayers; + h & players; + h & howManyTeams; + h & allowedHeroes; + //Do not serialize triggeredEvents in header as they can contain information about heroes and armies + h & victoryMessage; + h & victoryIconIndex; + h & defeatMessage; + h & defeatIconIndex; + } +}; + +VCMI_LIB_NAMESPACE_END diff --git a/lib/mapping/CMapInfo.cpp b/lib/mapping/CMapInfo.cpp index 8eacf860e..67cf3ebc7 100644 --- a/lib/mapping/CMapInfo.cpp +++ b/lib/mapping/CMapInfo.cpp @@ -16,6 +16,7 @@ #include "CMapService.h" #include "CMap.h" #include "CCampaignHandler.h" +#include "MapFormat.h" #include "../filesystem/Filesystem.h" #include "../serializer/CMemorySerializer.h" diff --git a/lib/mapping/CMapService.cpp b/lib/mapping/CMapService.cpp index 487c302ab..91cb833c3 100644 --- a/lib/mapping/CMapService.cpp +++ b/lib/mapping/CMapService.cpp @@ -19,6 +19,7 @@ #include "../Languages.h" #include "CMap.h" +#include "MapFormat.h" #include "MapFormatH3M.h" #include "MapFormatJson.h" diff --git a/lib/mapping/CMapService.h b/lib/mapping/CMapService.h index a04b0d942..b6e62299d 100644 --- a/lib/mapping/CMapService.h +++ b/lib/mapping/CMapService.h @@ -17,11 +17,12 @@ class ResourceID; class CMap; class CMapHeader; class CInputStream; +struct CModVersion; class IMapLoader; class IMapPatcher; -class ModCompatibilityInfo; +using ModCompatibilityInfo = std::map; /** * The map service provides loading of VCMI/H3 map files. It can diff --git a/lib/mapping/MapFeaturesH3M.cpp b/lib/mapping/MapFeaturesH3M.cpp index 6aecfd6ee..4ec517d8d 100644 --- a/lib/mapping/MapFeaturesH3M.cpp +++ b/lib/mapping/MapFeaturesH3M.cpp @@ -12,6 +12,7 @@ #include "MapFeaturesH3M.h" #include "CMap.h" +#include "MapFormat.h" VCMI_LIB_NAMESPACE_BEGIN diff --git a/lib/mapping/MapFormat.h b/lib/mapping/MapFormat.h new file mode 100644 index 000000000..01e91a05b --- /dev/null +++ b/lib/mapping/MapFormat.h @@ -0,0 +1,24 @@ +/* + * MapFormat.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 + +enum class EMapFormat : uint8_t +{ + INVALID = 0, + // HEX DEC + ROE = 0x0e, // 14 + AB = 0x15, // 21 + SOD = 0x1c, // 28 +// CHR = 0x1d, // 29 Heroes Chronicles, presumably - identical to SoD, untested + HOTA = 0x20, // 32 + WOG = 0x33, // 51 + VCMI = 0x64 +}; diff --git a/lib/mapping/MapFormatH3M.cpp b/lib/mapping/MapFormatH3M.cpp index 379da8c6b..ad997b521 100644 --- a/lib/mapping/MapFormatH3M.cpp +++ b/lib/mapping/MapFormatH3M.cpp @@ -13,12 +13,14 @@ #include "CMap.h" #include "MapReaderH3M.h" +#include "MapFormat.h" #include "../CCreatureHandler.h" #include "../CGeneralTextHandler.h" #include "../CHeroHandler.h" #include "../CSkillHandler.h" #include "../CStopWatch.h" +#include "../CModHandler.h" #include "../GameSettings.h" #include "../RiverHandler.h" #include "../RoadHandler.h" @@ -107,7 +109,7 @@ void CMapLoaderH3M::init() readRumors(); readPredefinedHeroes(); readTerrain(); - readDefInfo(); + readObjectTemplates(); readObjects(); readEvents(); @@ -911,7 +913,7 @@ void CMapLoaderH3M::readTerrain() } } -void CMapLoaderH3M::readDefInfo() +void CMapLoaderH3M::readObjectTemplates() { uint32_t defAmount = reader->readUInt32(); diff --git a/lib/mapping/MapFormatH3M.h b/lib/mapping/MapFormatH3M.h index ed3d35227..82d9fe284 100644 --- a/lib/mapping/MapFormatH3M.h +++ b/lib/mapping/MapFormatH3M.h @@ -148,7 +148,7 @@ private: /** * Reads custom(map) def information. */ - void readDefInfo(); + void readObjectTemplates(); /** * Reads objects(towns, mines,...). diff --git a/lib/mapping/MapFormatJson.cpp b/lib/mapping/MapFormatJson.cpp index cdd8f3122..67d3d6549 100644 --- a/lib/mapping/MapFormatJson.cpp +++ b/lib/mapping/MapFormatJson.cpp @@ -15,6 +15,7 @@ #include "../filesystem/COutputStream.h" #include "../JsonDetail.h" #include "CMap.h" +#include "MapFormat.h" #include "../CModHandler.h" #include "../CHeroHandler.h" #include "../CTownHandler.h" @@ -952,7 +953,7 @@ void CMapLoaderJson::readHeader(const bool complete) if(!header["mods"].isNull()) { for(auto & mod : header["mods"].Vector()) - mapHeader->mods[mod["name"].String()] = CModInfo::Version::fromString(mod["version"].String()); + mapHeader->mods[mod["name"].String()] = CModVersion::fromString(mod["version"].String()); } //todo: multilevel map load support diff --git a/lib/rmg/CMapGenerator.cpp b/lib/rmg/CMapGenerator.cpp index 86dc1051c..2dafe736b 100644 --- a/lib/rmg/CMapGenerator.cpp +++ b/lib/rmg/CMapGenerator.cpp @@ -11,6 +11,7 @@ #include "CMapGenerator.h" #include "../mapping/CMap.h" +#include "../mapping/MapFormat.h" #include "../VCMI_Lib.h" #include "../CGeneralTextHandler.h" #include "../mapping/CMapEditManager.h" diff --git a/mapeditor/mainwindow.cpp b/mapeditor/mainwindow.cpp index e222b56ab..b10c51fe7 100644 --- a/mapeditor/mainwindow.cpp +++ b/mapeditor/mainwindow.cpp @@ -27,6 +27,7 @@ #include "../lib/mapping/CMapService.h" #include "../lib/mapping/CMap.h" #include "../lib/mapping/CMapEditManager.h" +#include "../lib/mapping/MapFormat.h" #include "../lib/RoadHandler.h" #include "../lib/RiverHandler.h" #include "../lib/TerrainHandler.h" diff --git a/mapeditor/mapcontroller.cpp b/mapeditor/mapcontroller.cpp index 1ecec6a70..7551abe39 100644 --- a/mapeditor/mapcontroller.cpp +++ b/mapeditor/mapcontroller.cpp @@ -19,6 +19,7 @@ #include "../lib/CSkillHandler.h" #include "../lib/spells/CSpellHandler.h" #include "../lib/CHeroHandler.h" +#include "../lib/CModHandler.h" #include "../lib/serializer/CMemorySerializer.h" #include "mapview.h" #include "scenelayer.h" diff --git a/mapeditor/validator.cpp b/mapeditor/validator.cpp index c1b5892a1..6ac82994e 100644 --- a/mapeditor/validator.cpp +++ b/mapeditor/validator.cpp @@ -14,6 +14,7 @@ #include "ui_validator.h" #include "../lib/mapObjects/MapObjects.h" #include "../lib/CHeroHandler.h" +#include "../lib/CModHandler.h" Validator::Validator(const CMap * map, QWidget *parent) : QDialog(parent), diff --git a/mapeditor/windownewmap.cpp b/mapeditor/windownewmap.cpp index e320bf899..0ebe8d806 100644 --- a/mapeditor/windownewmap.cpp +++ b/mapeditor/windownewmap.cpp @@ -15,6 +15,7 @@ #include "../lib/rmg/CMapGenerator.h" #include "../lib/VCMI_Lib.h" #include "../lib/mapping/CMapEditManager.h" +#include "../lib/mapping/MapFormat.h" #include "../lib/CGeneralTextHandler.h" #include "windownewmap.h"