diff --git a/client/CGameInfo.cpp b/client/CGameInfo.cpp index 2fe9f4fd6..3c8eb86fb 100644 --- a/client/CGameInfo.cpp +++ b/client/CGameInfo.cpp @@ -37,6 +37,7 @@ void CGameInfo::setFromLib() spellh = VLC->spellh; skillh = VLC->skillh; objtypeh = VLC->objtypeh; + battleFieldHandler = VLC->battlefieldsHandler; } const ArtifactService * CGameInfo::artifacts() const @@ -44,6 +45,11 @@ const ArtifactService * CGameInfo::artifacts() const return globalServices->artifacts(); } +const BattleFieldService * CGameInfo::battlefields() const +{ + return globalServices->battlefields(); +} + const CreatureService * CGameInfo::creatures() const { return globalServices->creatures(); diff --git a/client/CGameInfo.h b/client/CGameInfo.h index d9947c1d4..b93924873 100644 --- a/client/CGameInfo.h +++ b/client/CGameInfo.h @@ -31,6 +31,7 @@ class CCursorHandler; class CGameState; class IMainVideoPlayer; class CServerHandler; +class BattleFieldHandler; class CMap; @@ -60,6 +61,7 @@ public: const scripting::Service * scripts() const override; const spells::Service * spells() const override; const SkillService * skills() const override; + const BattleFieldService * battlefields() const override; void updateEntity(Metatype metatype, int32_t index, const JsonNode & data) override; @@ -68,6 +70,7 @@ public: ConstTransitivePtr modh; //public? + ConstTransitivePtr battleFieldHandler; ConstTransitivePtr heroh; ConstTransitivePtr creh; ConstTransitivePtr spellh; diff --git a/client/Graphics.cpp b/client/Graphics.cpp index fe64be0f9..41b4a4507 100644 --- a/client/Graphics.cpp +++ b/client/Graphics.cpp @@ -109,12 +109,6 @@ void Graphics::initializeBattleGraphics() const JsonNode config(mod, ResourceID("config/battles_graphics.json")); - if(!config["backgrounds"].isNull()) - for(auto & t : config["backgrounds"].Struct()) - { - battleBacks[t.first] = t.second.String(); - } - //initialization of AC->def name mapping if(!config["ac_mapping"].isNull()) for(const JsonNode &ac : config["ac_mapping"].Vector()) diff --git a/client/Graphics.h b/client/Graphics.h index d59ae3fb2..fbf41063a 100644 --- a/client/Graphics.h +++ b/client/Graphics.h @@ -87,7 +87,6 @@ public: //towns std::map ERMUtoPicture[GameConstants::F_NUMBER]; //maps building ID to it's picture's name for each town type //for battles - std::map battleBacks; //maps BattleField to it's picture's name std::map< int, std::vector < std::string > > battleACToDef; //maps AC format to vector of appropriate def names //functions diff --git a/client/battle/CBattleInterface.cpp b/client/battle/CBattleInterface.cpp index 0b91a9155..6bea98b7f 100644 --- a/client/battle/CBattleInterface.cpp +++ b/client/battle/CBattleInterface.cpp @@ -41,6 +41,7 @@ #include "../../lib/spells/ISpellMechanics.h" #include "../../lib/spells/Problem.h" #include "../../lib/CTownHandler.h" +#include "../../lib/BattleFieldHandler.h" #include "../../lib/CGameState.h" #include "../../lib/mapping/CMap.h" #include "../../lib/NetPacks.h" @@ -201,13 +202,14 @@ CBattleInterface::CBattleInterface(const CCreatureSet *army1, const CCreatureSet else { auto bfieldType = curInt->cb->battleGetBattlefieldType(); - if(!vstd::contains(graphics->battleBacks, bfieldType)) + + if(bfieldType == BattleField::NONE) { - logGlobal->error("%s is not valid battlefield type!", static_cast(bfieldType)); + logGlobal->error("Invalid battlefield returned for current battle"); } else { - background = BitmapHandler::loadBitmap(graphics->battleBacks[bfieldType], false); + background = BitmapHandler::loadBitmap(bfieldType.getInfo()->graphics, false); } } diff --git a/config/battlefields.json b/config/battlefields.json new file mode 100644 index 000000000..cffc0df4a --- /dev/null +++ b/config/battlefields.json @@ -0,0 +1,173 @@ +{ + "sand_shore": { "graphics" : "CMBKBCH.BMP" }, + "sand_mesas": { "graphics" : "CMBKDES.BMP" }, + "dirt_birches": { "graphics" : "CMBKDRTR.BMP" }, + "dirt_hills": { "graphics" : "CMBKDRMT.BMP" }, + "dirt_pines": { "graphics" : "CMBKDRDD.BMP" }, + "grass_hills": { "graphics" : "CMBKGRMT.BMP" }, + "grass_pines": { "graphics" : "CMBKGRTR.BMP" }, + "lava": { "graphics" :"CMBKLAVA.BMP" }, + "magic_plains": { + "graphics": "CMBKMAG.BMP", + "isSpecial" : true, + "bonuses": [ + { + "type" : "MAGIC_SCHOOL_SKILL", + "subtype" : 0, + "val" : 3, + "valueType" : "BASE_NUMBER" + } + ] + }, + "snow_mountains": { "graphics" : "CMBKSNMT.BMP" }, + "snow_trees": { "graphics" : "CMBKSNTR.BMP" }, + "subterranean": { "graphics" : "CMBKSUB.BMP", "isSpecial" : true }, + "swamp_trees": { "graphics" : "CMBKSWMP.BMP" }, + "fiery_fields": { + "graphics": "CMBKFF.BMP", + "isSpecial" : true, + "bonuses": [ + { + "type" : "MAGIC_SCHOOL_SKILL", + "subtype" : 2, + "val" : 3, + "valueType" : "BASE_NUMBER" + } + ] + }, + "rocklands": { + "graphics": "CMBKRK.BMP", + "isSpecial" : true, + "bonuses": [ + { + "type" : "MAGIC_SCHOOL_SKILL", + "subtype" : 8, + "val" : 3, + "valueType" : "BASE_NUMBER" + } + ] + }, + "magic_clouds": { + "graphics": "CMBKMC.BMP", + "isSpecial" : true, + "bonuses": [ + { + "type" : "MAGIC_SCHOOL_SKILL", + "subtype" : 1, + "val" : 3, + "valueType" : "BASE_NUMBER" + } + ] + }, + "lucid_pools": { + "graphics": "CMBKLP.BMP", + "isSpecial" : true, + "bonuses": [ + { + "type" : "MAGIC_SCHOOL_SKILL", + "subtype" : 4, + "val" : 3, + "valueType" : "BASE_NUMBER" + } + ] + }, + "holy_ground": { + "graphics": "CMBKHG.BMP", + "isSpecial" : true, + "bonuses": [ + { + "type" : "MORALE", + "val" : 1, + "valueType" : "BASE_NUMBER", + "description" : "Creatures of good town alignment on Holly Ground", + "limiters": [{ "type" : "CREATURE_ALIGNMENT_LIMITER", "parameters" : ["good"] }] + }, + { + "type" : "MORALE", + "val" : -1, + "valueType" : "BASE_NUMBER", + "description" : "Creatures of evil town alignment on Holly Ground", + "limiters": [{ "type" : "CREATURE_ALIGNMENT_LIMITER", "parameters" : ["evil"] }] + } + ] + }, + "clover_field": { + "graphics": "CMBKCF.BMP", + "isSpecial" : true, + "bonuses": [ + { + "type" : "LUCK", + "val" : 2, + "valueType" : "BASE_NUMBER", + "description" : "Creatures of neutral town alignment on Clover Field", + "limiters": [{ "type" : "CREATURE_ALIGNMENT_LIMITER", "parameters" : ["neutral"] }] + } + ] + }, + "evil_fog": { + "graphics": "CMBKEF.BMP", + "isSpecial" : true, + "bonuses": [ + { + "type" : "MORALE", + "val" : -1, + "valueType" : "BASE_NUMBER", + "description" : "Creatures of good town alignment on Evil Fog", + "limiters": [{ "type" : "CREATURE_ALIGNMENT_LIMITER", "parameters" : ["good"] }] + }, + { + "type" : "MORALE", + "val" : 1, + "valueType" : "BASE_NUMBER", + "description" : "Creatures of evil town alignment on Evil Fog", + "limiters": [{ "type" : "CREATURE_ALIGNMENT_LIMITER", "parameters" : ["evil"] }] + } + ] + }, + "favorable_winds": { + "graphics": "CMBKFW.BMP", + "isSpecial" : true + }, + "cursed_ground": { + "graphics": "CMBKCUR.BMP", + "isSpecial" : true, + "bonuses": [ + { + "type" : "NO_MORALE", + "subtype" : 0, + "val" : 0, + "valueType" : "INDEPENDENT_MIN", + "description" : "Creatures on Cursed Ground" + }, + { + "type" : "NO_LUCK", + "subtype" : 0, + "val" : 0, + "valueType" : "INDEPENDENT_MIN", + "description" : "Creatures on Cursed Ground" + }, + { + "type" : "BLOCK_MAGIC_ABOVE", + "subtype" : 0, + "val" : 1, + "valueType" : "INDEPENDENT_MIN" + } + ] + }, + "rough": { "graphics" : "CMBKRGH.BMP" }, + "ship_to_ship": + { + "graphics" : "CMBKBOAT.BMP" + "impassableHexes" : [ + 6, 7, 8, 9, + 24, 25, 26, + 58, 59, 60, + 75, 76, 77, + 92, 93, 94, + 109, 110, 111, + 126, 127, 128, + 159, 160, 161, 162, 163, + 176, 177, 178, 179, 180] + }, + "ship": { "graphics" : "CMBKDECK.BMP" } +} diff --git a/config/battles_graphics.json b/config/battles_graphics.json index 8e37ae7eb..ee68b9693 100644 --- a/config/battles_graphics.json +++ b/config/battles_graphics.json @@ -1,33 +1,4 @@ { - // backgrounds of terrains battles can be fought on - "backgrounds": { - "sand_shore": "CMBKBCH.BMP", - "sand_mesas": "CMBKDES.BMP", - "dirt_birches": "CMBKDRTR.BMP", - "dirt_hills": "CMBKDRMT.BMP", - "dirt_pines": "CMBKDRDD.BMP", - "grass_hills": "CMBKGRMT.BMP", - "grass_pines": "CMBKGRTR.BMP", - "lava": "CMBKLAVA.BMP", - "magic_plains": "CMBKMAG.BMP", - "snow_mountains": "CMBKSNMT.BMP", - "snow_trees": "CMBKSNTR.BMP", - "subterranean": "CMBKSUB.BMP", - "swamp_trees": "CMBKSWMP.BMP", - "fiery_fields": "CMBKFF.BMP", - "rocklands": "CMBKRK.BMP", - "magic_clouds": "CMBKMC.BMP", - "lucid_pools": "CMBKLP.BMP", - "holy_ground": "CMBKHG.BMP", - "clover_field": "CMBKCF.BMP", - "evil_fog": "CMBKEF.BMP", - "favorable_winds": "CMBKFW.BMP", - "cursed_ground": "CMBKCUR.BMP", - "rough": "CMBKRGH.BMP", - "ship_to_ship": "CMBKBOAT.BMP", - "ship": "CMBKDECK.BMP" - }, - // WoG_Ac_format_to_def_names_mapping "ac_mapping": [ { "id": 0, "defnames": [ "C10SPW.DEF" ] },//merged diff --git a/config/gameConfig.json b/config/gameConfig.json index 57872e9a4..186029a94 100644 --- a/config/gameConfig.json +++ b/config/gameConfig.json @@ -84,5 +84,9 @@ "terrains": [ "config/terrains.json" + ], + "battlefields": + [ + "config/battlefields.json" ] } diff --git a/config/schemas/battlefield.json b/config/schemas/battlefield.json new file mode 100644 index 000000000..02aa0dcfd --- /dev/null +++ b/config/schemas/battlefield.json @@ -0,0 +1,35 @@ +{ + "type":"object", + "$schema": "http://json-schema.org/draft-04/schema", + "title" : "VCMI battlefield format", + "description" : "Format used to define new artifacts in VCMI", + "required" : [ "graphics" ], + + "additionalProperties" : false, + "properties":{ + "bonuses": { + "type":"array", + "description": "Bonuses provided by this battleground using bonus system", + "items": { "$ref" : "bonus.json" } + }, + "name": { + "type":"string", + "description": "Name of the battleground" + }, + "graphics": { + "type":"string", + "description": "BMP battleground resource" + }, + "isSpecial": { + "type":"boolean", + "description": "Shows if this battleground has own obstacles" + }, + "impassableHexes": { + "type":"array", + "description": "Battle hexes always impassable for this type of battlefield (ship to ship for instance)", + "items": { + "type":"number" + } + } + } +} diff --git a/config/schemas/mod.json b/config/schemas/mod.json index e6e18167a..34aed68c3 100644 --- a/config/schemas/mod.json +++ b/config/schemas/mod.json @@ -112,6 +112,12 @@ "description": "List of configuration files for RMG templates", "items": { "type":"string", "format" : "textFile" } + }, + "battlefields":{ + "type":"array", + "description": "List of configuration files for battlefields", + "items": { "type":"string", "format" : "textFile" } + }, "changelog" : { diff --git a/include/vcmi/Services.h b/include/vcmi/Services.h index 46c2e58a9..1b567a824 100644 --- a/include/vcmi/Services.h +++ b/include/vcmi/Services.h @@ -19,6 +19,7 @@ class HeroClassService; class HeroTypeService; class SkillService; class JsonNode; +class BattleFieldService; namespace spells { @@ -48,6 +49,7 @@ public: virtual const scripting::Service * scripts() const = 0; virtual const spells::Service * spells() const = 0; virtual const SkillService * skills() const = 0; + virtual const BattleFieldService * battlefields() const = 0; virtual void updateEntity(Metatype metatype, int32_t index, const JsonNode & data) = 0; diff --git a/lib/BattleFieldHandler.cpp b/lib/BattleFieldHandler.cpp new file mode 100644 index 000000000..317afcad7 --- /dev/null +++ b/lib/BattleFieldHandler.cpp @@ -0,0 +1,108 @@ +/* + * BattleFieldHandler.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 +#include "BattleFieldHandler.h" + +BattleFieldInfo * BattleFieldHandler::loadFromJson(const std::string & scope, const JsonNode & json, const std::string & identifier, size_t index) +{ + BattleFieldInfo * info = new BattleFieldInfo(BattleField(index), identifier); + + if(json["graphics"].getType() == JsonNode::JsonType::DATA_STRING) + { + info->graphics = json["graphics"].String(); + } + + if(json["icon"].getType() == JsonNode::JsonType::DATA_STRING) + { + info->icon = json["icon"].String(); + } + + if(json["name"].getType() == JsonNode::JsonType::DATA_STRING) + { + info->name = json["name"].String(); + } + + if(json["bonuses"].getType() == JsonNode::JsonType::DATA_VECTOR) + { + for(auto b : json["bonuses"].Vector()) + { + auto bonus = JsonUtils::parseBonus(b); + + bonus->source = Bonus::TERRAIN_OVERLAY; + bonus->sid = info->getIndex(); + bonus->duration = Bonus::ONE_BATTLE; + + info->bonuses.push_back(bonus); + } + } + + if(json["isSpecial"].getType() == JsonNode::JsonType::DATA_BOOL) + { + info->isSpecial = json["isSpecial"].Bool(); + } + + if(json["impassableHexes"].getType() == JsonNode::JsonType::DATA_VECTOR) + { + for(auto node : json["impassableHexes"].Vector()) + info->impassableHexes.push_back(BattleHex(node.Integer())); + } + + + return info; +} + +std::vector BattleFieldHandler::loadLegacyData(size_t dataSize) +{ + return std::vector(); +} + +const std::vector & BattleFieldHandler::getTypeNames() const +{ + static const std::vector types = std::vector { "battlefield" }; + + return types; +} + +std::vector BattleFieldHandler::getDefaultAllowed() const +{ + return std::vector(); +} + +int32_t BattleFieldInfo::getIndex() const +{ + return battlefield.getNum(); +} + +int32_t BattleFieldInfo::getIconIndex() const +{ + return iconIndex; +} + +const std::string & BattleFieldInfo::getName() const +{ + return name; +} + +const std::string & BattleFieldInfo::getJsonKey() const +{ + return identifier; +} + +void BattleFieldInfo::registerIcons(const IconRegistar & cb) const +{ + //cb(getIconIndex(), "BATTLEFIELD", icon); +} + +BattleField BattleFieldInfo::getId() const +{ + return battlefield; +} \ No newline at end of file diff --git a/lib/BattleFieldHandler.h b/lib/BattleFieldHandler.h new file mode 100644 index 000000000..ab5ab2742 --- /dev/null +++ b/lib/BattleFieldHandler.h @@ -0,0 +1,85 @@ +/* + * BattleFieldHandler.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 +#include "HeroBonus.h" +#include "GameConstants.h" +#include "IHandlerBase.h" +#include "Terrain.h" +#include "battle/BattleHex.h" + +class BattleFieldInfo : public EntityT +{ +public: + BattleField battlefield; + std::vector> bonuses; + bool isSpecial; + std::string graphics; + std::string name; + std::string identifier; + std::string icon; + si32 iconIndex; + std::vector impassableHexes; + + BattleFieldInfo() + : BattleFieldInfo(BattleField::NONE, "") + { + } + + BattleFieldInfo(BattleField battlefield, std::string identifier) + :bonuses(), isSpecial(false), battlefield(battlefield), identifier(identifier), graphics(), icon(), iconIndex(battlefield.getNum()), impassableHexes(), name(identifier) + { + } + + int32_t getIndex() const override; + int32_t getIconIndex() const override; + const std::string & getName() const override; + const std::string & getJsonKey() const override; + void registerIcons(const IconRegistar & cb) const override; + BattleField getId() const override; + + template void serialize(Handler & h, const int version) + { + h & name; + h & identifier; + h & isSpecial; + h & graphics; + h & icon; + h & iconIndex; + h & battlefield; + h & impassableHexes; + + } +}; + +class DLL_LINKAGE BattleFieldService : public EntityServiceT +{ +public: +}; + +class BattleFieldHandler : public CHandlerBase +{ +public: + virtual BattleFieldInfo * loadFromJson( + const std::string & scope, + const JsonNode & json, + const std::string & identifier, + size_t index) override; + + virtual const std::vector & getTypeNames() const override; + virtual std::vector loadLegacyData(size_t dataSize) override; + virtual std::vector getDefaultAllowed() const override; + + template void serialize(Handler & h, const int version) + { + h & objects; + } +}; diff --git a/lib/CGameState.cpp b/lib/CGameState.cpp index 83196ce72..cb20200c3 100644 --- a/lib/CGameState.cpp +++ b/lib/CGameState.cpp @@ -1922,9 +1922,10 @@ BattleField CGameState::battleGetBattlefieldType(int3 tile, CRandomGenerator & r } if(map->isCoastalTile(tile)) //coastal tile is always ground - return BattleField("sand_shore"); + return BattleField::fromString("sand_shore"); - return *RandomGeneratorUtil::nextItem(Terrain::Manager::getInfo(t.terType).battleFields, rand); + return BattleField::fromString( + *RandomGeneratorUtil::nextItem(Terrain::Manager::getInfo(t.terType).battleFields, rand)); } UpgradeInfo CGameState::getUpgradeInfo(const CStackInstance &stack) diff --git a/lib/CHeroHandler.cpp b/lib/CHeroHandler.cpp index 7fae83f7e..bfb00fed3 100644 --- a/lib/CHeroHandler.cpp +++ b/lib/CHeroHandler.cpp @@ -25,6 +25,7 @@ #include #include "mapObjects/CObjectClassesHandler.h" +#include "BattleFieldHandler.h" CHero::CHero() = default; CHero::~CHero() = default; @@ -179,8 +180,10 @@ std::vector CObstacleInfo::getBlocked(BattleHex hex) const bool CObstacleInfo::isAppropriate(const Terrain & terrainType, const BattleField & battlefield) const { - if(battlefield.isSpecial()) - return vstd::contains(allowedSpecialBfields, battlefield); + auto bgInfo = battlefield.getInfo(); + + if(bgInfo->isSpecial) + return vstd::contains(allowedSpecialBfields, bgInfo->identifier); return vstd::contains(allowedTerrains, terrainType); } diff --git a/lib/CHeroHandler.h b/lib/CHeroHandler.h index 674dabb0a..e690fcfa9 100644 --- a/lib/CHeroHandler.h +++ b/lib/CHeroHandler.h @@ -227,7 +227,7 @@ struct DLL_LINKAGE CObstacleInfo { std::string defName; std::vector allowedTerrains; - std::vector allowedSpecialBfields; + std::vector allowedSpecialBfields; ui8 isAbsoluteObstacle; //there may only one such obstacle in battle and its position is always the same si32 width, height; //how much space to the right and up is needed to place obstacle (affects only placement algorithm) diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 9f42388dd..f49cd00c9 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -129,7 +129,8 @@ set(lib_SRCS spells/effects/Sacrifice.cpp vstd/StringUtils.cpp - + + BattleFieldHandler.cpp CAndroidVMHelper.cpp CArtHandler.cpp CBonusTypeHandler.cpp @@ -338,6 +339,7 @@ set(lib_HEADERS spells/effects/Sacrifice.h AI_Base.h + BattleFieldHandler.h CAndroidVMHelper.h CArtHandler.h CBonusTypeHandler.h diff --git a/lib/CModHandler.cpp b/lib/CModHandler.cpp index c3d5185ae..a7b2ace73 100644 --- a/lib/CModHandler.cpp +++ b/lib/CModHandler.cpp @@ -25,6 +25,7 @@ #include "spells/CSpellHandler.h" #include "CSkillHandler.h" #include "ScriptHandler.h" +#include "BattleFieldHandler.h" #include @@ -433,6 +434,7 @@ void CContentHandler::init() handlers.insert(std::make_pair("skills", ContentTypeHandler(VLC->skillh, "skill"))); handlers.insert(std::make_pair("templates", ContentTypeHandler((IHandlerBase *)VLC->tplh, "template"))); handlers.insert(std::make_pair("scripts", ContentTypeHandler(VLC->scriptHandler, "script"))); + handlers.insert(std::make_pair("battlefields", ContentTypeHandler(VLC->battlefieldsHandler, "battlefield"))); //TODO: any other types of moddables? } diff --git a/lib/GameConstants.cpp b/lib/GameConstants.cpp index ed73ef621..f9a5d84a2 100644 --- a/lib/GameConstants.cpp +++ b/lib/GameConstants.cpp @@ -34,6 +34,7 @@ #include "StringConstants.h" #include "CGeneralTextHandler.h" #include "CModHandler.h"//todo: remove +#include "BattleFieldHandler.h" const SlotID SlotID::COMMANDER_SLOT_PLACEHOLDER = SlotID(-2); const SlotID SlotID::SUMMONED_SLOT_PLACEHOLDER = SlotID(-3); @@ -255,3 +256,40 @@ std::ostream & operator<<(std::ostream & os, const EPathfindingLayer pathfinding if (it == pathfinderLayerToString.end()) return os << ""; else return os << it->second; } + +const BattleField BattleField::NONE; + +bool operator==(const BattleField & l, const BattleField & r) +{ + return l.num == r.num; +} + +bool operator!=(const BattleField & l, const BattleField & r) +{ + return l.num != r.num; +} + +bool operator<(const BattleField & l, const BattleField & r) +{ + return l.num < r.num; +} + +BattleField::operator std::string() const +{ + return getInfo()->identifier; +} + +const BattleFieldInfo * BattleField::getInfo() const +{ + return VLC->battlefields()->getById(*this); +} + +BattleField BattleField::fromString(std::string identifier) +{ + auto rawId = VLC->modh->identifiers.getIdentifier("core", "battlefield", identifier); + + if(rawId) + return BattleField(rawId.get()); + else + return BattleField::NONE; +} \ No newline at end of file diff --git a/lib/GameConstants.h b/lib/GameConstants.h index 636766ed9..7dfbde61a 100644 --- a/lib/GameConstants.h +++ b/lib/GameConstants.h @@ -1109,6 +1109,23 @@ public: ID_LIKE_OPERATORS(SpellID, SpellID::ESpellID) +class BattleFieldInfo; +class BattleField : public BaseForID +{ + INSTID_LIKE_CLASS_COMMON(BattleField, si32) + + DLL_LINKAGE static const BattleField NONE; + + DLL_LINKAGE friend bool operator==(const BattleField & l, const BattleField & r); + DLL_LINKAGE friend bool operator!=(const BattleField & l, const BattleField & r); + DLL_LINKAGE friend bool operator<(const BattleField & l, const BattleField & r); + + DLL_LINKAGE operator std::string() const; + DLL_LINKAGE const BattleFieldInfo * getInfo() const; + + DLL_LINKAGE static BattleField fromString(std::string identifier); +}; + enum class ESpellSchool: ui8 { AIR = 0, diff --git a/lib/IGameCallback.cpp b/lib/IGameCallback.cpp index cc75b45d0..b5d6f37b0 100644 --- a/lib/IGameCallback.cpp +++ b/lib/IGameCallback.cpp @@ -16,6 +16,7 @@ #include "NetPacks.h" #include "CBonusTypeHandler.h" #include "CModHandler.h" +#include "BattleFieldHandler.h" #include "serializer/CSerializer.h" // for SAVEGAME_MAGIC #include "serializer/BinaryDeserializer.h" diff --git a/lib/Terrain.cpp b/lib/Terrain.cpp index 4a669fc86..24afad471 100644 --- a/lib/Terrain.cpp +++ b/lib/Terrain.cpp @@ -18,8 +18,6 @@ const Terrain Terrain::ANY("ANY"); -const BattleField BattleField::NONE(""); - Terrain Terrain::createTerrainTypeH3M(int tId) { static std::array terrainsH3M @@ -227,27 +225,3 @@ bool Terrain::isTransitionRequired() const { return Terrain::Manager::getInfo(*this).transitionRequired; } - -bool operator==(const BattleField & l, const BattleField & r) -{ - return l.name == r.name; -} - -bool operator!=(const BattleField & l, const BattleField & r) -{ - return l.name != r.name; -} - -bool operator<(const BattleField & l, const BattleField & r) -{ - return l.name < r.name; -} -BattleField::operator std::string() const -{ - return name; -} - -int BattleField::hash() const -{ - return std::hash{}(name); -} diff --git a/lib/Terrain.h b/lib/Terrain.h index 63dac1b6f..40d2e0778 100644 --- a/lib/Terrain.h +++ b/lib/Terrain.h @@ -11,47 +11,9 @@ #pragma once #include "ConstTransitivePtr.h" +#include "GameConstants.h" #include "JsonNode.h" -class DLL_LINKAGE BattleField -{ -public: - // 1. sand/shore 2. sand/mesas 3. dirt/birches 4. dirt/hills 5. dirt/pines 6. grass/hills 7. grass/pines - //8. lava 9. magic plains 10. snow/mountains 11. snow/trees 12. subterranean 13. swamp/trees 14. fiery fields - //15. rock lands 16. magic clouds 17. lucid pools 18. holy ground 19. clover field 20. evil fog - //21. "favorable winds" text on magic plains background 22. cursed ground 23. rough 24. ship to ship 25. ship - /*enum EBFieldType {NONE = -1, NONE2, SAND_SHORE, SAND_MESAS, DIRT_BIRCHES, DIRT_HILLS, DIRT_PINES, GRASS_HILLS, - GRASS_PINES, LAVA, MAGIC_PLAINS, SNOW_MOUNTAINS, SNOW_TREES, SUBTERRANEAN, SWAMP_TREES, FIERY_FIELDS, - ROCKLANDS, MAGIC_CLOUDS, LUCID_POOLS, HOLY_GROUND, CLOVER_FIELD, EVIL_FOG, FAVORABLE_WINDS, CURSED_GROUND, - ROUGH, SHIP_TO_SHIP, SHIP - };*/ - - BattleField(const std::string & type = "") : name(type) - {} - - static const BattleField NONE; - - DLL_LINKAGE friend bool operator==(const BattleField & l, const BattleField & r); - DLL_LINKAGE friend bool operator!=(const BattleField & l, const BattleField & r); - DLL_LINKAGE friend bool operator<(const BattleField & l, const BattleField & r); - - operator std::string() const; - int hash() const; - - template void serialize(Handler &h, const int version) - { - h & name; - } - - bool isSpecial() const - { - return name.find('_') >= 0; // hack for special battlefields, move to JSON - } - -protected: - - std::string name; -}; class DLL_LINKAGE Terrain { @@ -77,7 +39,7 @@ public: std::string terrainViewPatterns; int horseSoundId; Type type; - std::vector battleFields; + std::vector battleFields; }; class DLL_LINKAGE Manager diff --git a/lib/VCMI_Lib.cpp b/lib/VCMI_Lib.cpp index 30ba67e41..ff8d35eec 100644 --- a/lib/VCMI_Lib.cpp +++ b/lib/VCMI_Lib.cpp @@ -32,6 +32,7 @@ #include "rmg/CRmgTemplateStorage.h" #include "mapping/CMapEditManager.h" #include "ScriptHandler.h" +#include "BattleFieldHandler.h" LibClasses * VLC = nullptr; @@ -110,6 +111,11 @@ spells::effects::Registry * LibClasses::spellEffects() return spells::effects::GlobalRegistry::get(); } +const BattleFieldService * LibClasses::battlefields() const +{ + return battlefieldsHandler; +} + void LibClasses::updateEntity(Metatype metatype, int32_t index, const JsonNode & data) { switch(metatype) @@ -205,6 +211,8 @@ void LibClasses::init(bool onlyEssential) createHandler(scriptHandler, "Script", pomtime); + createHandler(battlefieldsHandler, "Battlefields", pomtime); + logGlobal->info("\tInitializing handlers: %d ms", totalTime.getDiff()); modh->load(); @@ -231,6 +239,7 @@ void LibClasses::clear() delete tplh; delete terviewh; delete scriptHandler; + delete battlefieldsHandler; makeNull(); } @@ -250,6 +259,7 @@ void LibClasses::makeNull() tplh = nullptr; terviewh = nullptr; scriptHandler = nullptr; + battlefieldsHandler = nullptr; } LibClasses::LibClasses() diff --git a/lib/VCMI_Lib.h b/lib/VCMI_Lib.h index e982e3cfe..4ab7c126c 100644 --- a/lib/VCMI_Lib.h +++ b/lib/VCMI_Lib.h @@ -24,6 +24,7 @@ class CTownHandler; class CGeneralTextHandler; class CModHandler; class CContentHandler; +class BattleFieldHandler; class IBonusTypeHandler; class CBonusTypeHandler; class CTerrainViewPatternConfig; @@ -56,6 +57,7 @@ public: const scripting::Service * scripts() const override; const spells::Service * spells() const override; const SkillService * skills() const override; + const BattleFieldService * battlefields() const override; void updateEntity(Metatype metatype, int32_t index, const JsonNode & data) override; @@ -76,6 +78,7 @@ public: CModHandler * modh; CTerrainViewPatternConfig * terviewh; CRmgTemplateStorage * tplh; + BattleFieldHandler * battlefieldsHandler; scripting::ScriptHandler * scriptHandler; LibClasses(); //c-tor, loads .lods and NULLs handlers @@ -104,6 +107,8 @@ public: h & objtypeh; h & spellh; h & skillh; + h & battlefieldsHandler; + if(!h.saving) { //modh will be changed and modh->content will be empty after deserialization diff --git a/lib/battle/BattleInfo.cpp b/lib/battle/BattleInfo.cpp index 1f4587901..59925927e 100644 --- a/lib/battle/BattleInfo.cpp +++ b/lib/battle/BattleInfo.cpp @@ -16,6 +16,7 @@ #include "../mapObjects/CGTownInstance.h" #include "../CGeneralTextHandler.h" #include "../Terrain.h" +#include "../BattleFieldHandler.h" //TODO: remove #include "../IGameCallback.h" @@ -458,64 +459,12 @@ BattleInfo * BattleInfo::setupBattle(const int3 & tile, const Terrain & terrain, auto good = std::make_shared(EAlignment::GOOD); auto evil = std::make_shared(EAlignment::EVIL); - //giving terrain overlay premies - int bonusSubtype = -1; + auto bgInfo = VLC->battlefields()->getById(battlefieldType); - if(battlefieldType == BattleField("magic_plains")) + for(const std::shared_ptr & bonus : bgInfo->bonuses) { - bonusSubtype = 0; + curB->addNewBonus(bonus); } - if(battlefieldType == BattleField("fiery_fields")) - { - if(bonusSubtype == -1) bonusSubtype = 2; - } - if(battlefieldType == BattleField("rocklands")) - { - if(bonusSubtype == -1) bonusSubtype = 8; - } - if(battlefieldType == BattleField("magic_clouds")) - { - if(bonusSubtype == -1) bonusSubtype = 1; - } - if(battlefieldType == BattleField("lucid_pools")) - { - if(bonusSubtype == -1) bonusSubtype = 4; - } - if(bonusSubtype != -1) - { //common part for cases 9, 14, 15, 16, 17 - curB->addNewBonus(std::make_shared(Bonus::ONE_BATTLE, Bonus::MAGIC_SCHOOL_SKILL,Bonus::TERRAIN_OVERLAY, 3, battlefieldType.hash(), bonusSubtype)); - } - else if(battlefieldType == BattleField("holy_ground")) - { - std::string goodArmyDesc = VLC->generaltexth->arraytxt[123]; - goodArmyDesc.erase(goodArmyDesc.size() - 2, 2); //omitting hardcoded +1 in description - std::string evilArmyDesc = VLC->generaltexth->arraytxt[124]; - evilArmyDesc.erase(evilArmyDesc.size() - 2, 2); - curB->addNewBonus(std::make_shared(Bonus::ONE_BATTLE, Bonus::MORALE, Bonus::TERRAIN_OVERLAY, +1, battlefieldType.hash(), goodArmyDesc, 0)->addLimiter(good)); - curB->addNewBonus(std::make_shared(Bonus::ONE_BATTLE, Bonus::MORALE, Bonus::TERRAIN_OVERLAY, -1, battlefieldType.hash(), evilArmyDesc, 0)->addLimiter(evil)); - } - else if(battlefieldType == BattleField("clover_field")) - { //+2 luck bonus for neutral creatures - std::string desc = VLC->generaltexth->arraytxt[83]; - desc.erase(desc.size() - 2, 2); - curB->addNewBonus(std::make_shared(Bonus::ONE_BATTLE, Bonus::LUCK, Bonus::TERRAIN_OVERLAY, +2, battlefieldType.hash(), desc, 0)->addLimiter(neutral)); - } - else if(battlefieldType == BattleField("evil_fog")) - { - std::string goodArmyDesc = VLC->generaltexth->arraytxt[126]; - goodArmyDesc.erase(goodArmyDesc.size() - 2, 2); - std::string evilArmyDesc = VLC->generaltexth->arraytxt[125]; - evilArmyDesc.erase(evilArmyDesc.size() - 2, 2); - curB->addNewBonus(std::make_shared(Bonus::ONE_BATTLE, Bonus::MORALE, Bonus::TERRAIN_OVERLAY, -1, battlefieldType.hash(), goodArmyDesc, 0)->addLimiter(good)); - curB->addNewBonus(std::make_shared(Bonus::ONE_BATTLE, Bonus::MORALE, Bonus::TERRAIN_OVERLAY, +1, battlefieldType.hash(), evilArmyDesc, 0)->addLimiter(evil)); - } - else if(battlefieldType == BattleField("cursed_ground")) - { - curB->addNewBonus(std::make_shared(Bonus::ONE_BATTLE, Bonus::NO_MORALE, Bonus::TERRAIN_OVERLAY, 0, battlefieldType.hash(), VLC->generaltexth->arraytxt[112], 0)); - curB->addNewBonus(std::make_shared(Bonus::ONE_BATTLE, Bonus::NO_LUCK, Bonus::TERRAIN_OVERLAY, 0, battlefieldType.hash(), VLC->generaltexth->arraytxt[81], 0)); - curB->addNewBonus(std::make_shared(Bonus::ONE_BATTLE, Bonus::BLOCK_MAGIC_ABOVE, Bonus::TERRAIN_OVERLAY, 1, battlefieldType.hash(), 0, Bonus::INDEPENDENT_MIN)); - } - //overlay premies given //native terrain bonuses static auto nativeTerrain = std::make_shared(); diff --git a/lib/battle/CBattleInfoCallback.cpp b/lib/battle/CBattleInfoCallback.cpp index a2122bcbb..fbeb7d20a 100644 --- a/lib/battle/CBattleInfoCallback.cpp +++ b/lib/battle/CBattleInfoCallback.cpp @@ -17,6 +17,7 @@ #include "../NetPacks.h" #include "../spells/CSpellHandler.h" #include "../mapObjects/CGTownInstance.h" +#include "../BattleFieldHandler.h" namespace SiegeStuffThatShouldBeMovedToHandlers // <=== TODO { @@ -1058,24 +1059,15 @@ AccessibilityInfo CBattleInfoCallback::getAccesibility() const } //special battlefields with logically unavailable tiles - std::vector impassableHexes; - if(battleGetBattlefieldType() == BattleField("ship_to_ship")) + auto bFieldType = battleGetBattlefieldType(); + + if(bFieldType != BattleField::NONE) { - impassableHexes = - { - 6, 7, 8, 9, - 24, 25, 26, - 58, 59, 60, - 75, 76, 77, - 92, 93, 94, - 109, 110, 111, - 126, 127, 128, - 159, 160, 161, 162, 163, - 176, 177, 178, 179, 180 - }; + std::vector impassableHexes = bFieldType.getInfo()->impassableHexes; + + for(auto hex : impassableHexes) + ret[hex] = EAccessibility::UNAVAILABLE; } - for(auto hex : impassableHexes) - ret[hex] = EAccessibility::UNAVAILABLE; //gate -> should be before stacks if(battleGetSiegeLevel() > 0) diff --git a/lib/mapObjects/CObjectClassesHandler.cpp b/lib/mapObjects/CObjectClassesHandler.cpp index 638ad7cea..b9525cb0a 100644 --- a/lib/mapObjects/CObjectClassesHandler.cpp +++ b/lib/mapObjects/CObjectClassesHandler.cpp @@ -515,10 +515,10 @@ void AObjectTypeHandler::init(const JsonNode & input, boost::optional>(input["aiValue"].Integer()); - if(input["battleground"].isNull()) - battlefield = BattleField::NONE; + if(input["battleground"].getType() == JsonNode::JsonType::DATA_STRING) + battlefield = input["battleground"].String(); else - battlefield = BattleField(input["battleground"].String()); + battlefield = boost::none; initTypeData(input); } @@ -577,7 +577,7 @@ std::vector AObjectTypeHandler::getTemplates() const BattleField AObjectTypeHandler::getBattlefield() const { - return battlefield; + return battlefield ? BattleField::fromString(battlefield.get()) : BattleField::NONE; } std::vector AObjectTypeHandler::getTemplates(const Terrain & terrainType) const diff --git a/lib/mapObjects/CObjectClassesHandler.h b/lib/mapObjects/CObjectClassesHandler.h index c46275261..e578f585c 100644 --- a/lib/mapObjects/CObjectClassesHandler.h +++ b/lib/mapObjects/CObjectClassesHandler.h @@ -150,7 +150,7 @@ class DLL_LINKAGE AObjectTypeHandler : public boost::noncopyable boost::optional aiValue; - BattleField battlefield; + boost::optional battlefield; protected: void preInitObject(CGObjectInstance * obj) const; @@ -219,6 +219,7 @@ public: h & subTypeName; h & sounds; h & aiValue; + h & battlefield; } }; diff --git a/lib/mapping/CMapEditManager.cpp b/lib/mapping/CMapEditManager.cpp index e514084c9..50ddd52af 100644 --- a/lib/mapping/CMapEditManager.cpp +++ b/lib/mapping/CMapEditManager.cpp @@ -461,12 +461,15 @@ const std::vector & CTerrainViewPatternCon return iter->second; } -boost::optional CTerrainViewPatternConfig::getTerrainViewPatternById(const Terrain & terrain, const std::string & id) const +boost::optional CTerrainViewPatternConfig::getTerrainViewPatternById(std::string patternId, const std::string & id) const { - const std::vector & groupPatterns = getTerrainViewPatterns(terrain); + auto iter = terrainViewPatterns.find(patternId); + const std::vector & groupPatterns = (iter == terrainViewPatterns.end()) ? terrainViewPatterns.at("normal") : iter->second; + for (const TVPVector & patternFlips : groupPatterns) { const TerrainViewPattern & pattern = patternFlips.front(); + if(id == pattern.id) { return boost::optional(pattern); diff --git a/lib/mapping/CMapEditManager.h b/lib/mapping/CMapEditManager.h index 2bd42e26d..041be905e 100644 --- a/lib/mapping/CMapEditManager.h +++ b/lib/mapping/CMapEditManager.h @@ -327,7 +327,7 @@ public: ~CTerrainViewPatternConfig(); const std::vector & getTerrainViewPatterns(const Terrain & terrain) const; - boost::optional getTerrainViewPatternById(const Terrain & terrain, const std::string & id) const; + boost::optional getTerrainViewPatternById(std::string patternId, const std::string & id) const; boost::optional getTerrainViewPatternsById(const Terrain & terrain, const std::string & id) const; const TVPVector * getTerrainTypePatternById(const std::string & id) const; void flipPattern(TerrainViewPattern & pattern, int flip) const; diff --git a/lib/registerTypes/RegisterTypes.h b/lib/registerTypes/RegisterTypes.h index a20235254..6bf06044b 100644 --- a/lib/registerTypes/RegisterTypes.h +++ b/lib/registerTypes/RegisterTypes.h @@ -55,6 +55,7 @@ void registerTypesMapObjects1(Serializer &s) s.template registerType(); s.template registerType(); s.template registerType(); s.template registerType(); + s.template registerType(); s.template registerType(); s.template registerType(); s.template registerType(); s.template registerType(); @@ -108,6 +109,7 @@ void registerTypesMapObjectTypes(Serializer &s) REGISTER_GENERIC_HANDLER(CGHeroInstance); REGISTER_GENERIC_HANDLER(CGKeymasterTent); REGISTER_GENERIC_HANDLER(CGLighthouse); + REGISTER_GENERIC_HANDLER(CGTerrainPatch); REGISTER_GENERIC_HANDLER(CGMagi); REGISTER_GENERIC_HANDLER(CGMagicSpring); REGISTER_GENERIC_HANDLER(CGMagicWell); diff --git a/lib/rmg/CMapGenOptions.cpp b/lib/rmg/CMapGenOptions.cpp index ba71ace26..55802f2a2 100644 --- a/lib/rmg/CMapGenOptions.cpp +++ b/lib/rmg/CMapGenOptions.cpp @@ -246,8 +246,18 @@ void CMapGenOptions::finalize(CRandomGenerator & rand) if(waterContent == EWaterContent::RANDOM) { - waterContent = *RandomGeneratorUtil::nextItem(mapTemplate->getWaterContentAllowed(), rand); + auto allowedContent = mapTemplate->getWaterContentAllowed(); + + if(allowedContent.size()) + { + waterContent = *RandomGeneratorUtil::nextItem(mapTemplate->getWaterContentAllowed(), rand); + } + else + { + waterContent = EWaterContent::NONE; + } } + if(monsterStrength == EMonsterStrength::RANDOM) { monsterStrength = static_cast(rand.nextInt(EMonsterStrength::GLOBAL_WEAK, EMonsterStrength::GLOBAL_STRONG)); diff --git a/lib/rmg/CRmgTemplateZone.cpp b/lib/rmg/CRmgTemplateZone.cpp index 2600fdc55..eefbd3495 100644 --- a/lib/rmg/CRmgTemplateZone.cpp +++ b/lib/rmg/CRmgTemplateZone.cpp @@ -2019,6 +2019,10 @@ int3 CRmgTemplateZone::makeBoat(TRmgTemplateZoneId land, const std::set & { std::set lakeCoast; std::set_intersection(gen->getZones()[land]->getCoastTiles().begin(), gen->getZones()[land]->getCoastTiles().end(), lake.begin(), lake.end(), std::inserter(lakeCoast, lakeCoast.begin())); + + if(lakeCoast.empty()) + return int3(-1, -1, -1); + for(int randomAttempts = 0; randomAttempts<5; ++randomAttempts) { auto coastTile = *RandomGeneratorUtil::nextItem(lakeCoast, gen->rand); diff --git a/lib/spells/ISpellMechanics.cpp b/lib/spells/ISpellMechanics.cpp index 1aa00f649..a80475041 100644 --- a/lib/spells/ISpellMechanics.cpp +++ b/lib/spells/ISpellMechanics.cpp @@ -12,6 +12,7 @@ #include "ISpellMechanics.h" #include "../CRandomGenerator.h" +#include "../VCMI_Lib.h" #include "../HeroBonus.h" #include "../battle/CBattleInfoCallback.h" @@ -39,6 +40,7 @@ #include "../CHeroHandler.h"//todo: remove #include "../IGameCallback.h"//todo: remove +#include "../BattleFieldHandler.h" namespace spells { @@ -520,7 +522,7 @@ bool BaseMechanics::adaptProblem(ESpellCastProblem::ESpellCastProblem source, Pr caster->getCasterName(text); target.add(std::move(text), spells::Problem::NORMAL); } - else if(b && b->source == Bonus::TERRAIN_OVERLAY && b->sid == BattleField("cursed_ground").hash()) + else if(b && b->source == Bonus::TERRAIN_OVERLAY && VLC->battlefields()->getByIndex(b->sid)->identifier == "cursed_ground") { text.addTxt(MetaString::GENERAL_TXT, 537); target.add(std::move(text), spells::Problem::NORMAL); diff --git a/scripting/lua/api/BattleCb.cpp b/scripting/lua/api/BattleCb.cpp index 6f4f2a81a..0f8127797 100644 --- a/scripting/lua/api/BattleCb.cpp +++ b/scripting/lua/api/BattleCb.cpp @@ -10,12 +10,14 @@ #include "StdInc.h" #include "BattleCb.h" +#include #include "../LuaStack.h" #include "../LuaCallWrapper.h" #include "../../../lib/GameConstants.h" #include "../../../lib/battle/Unit.h" +#include "../../../lib/BattleFieldHandler.h" namespace scripting { @@ -73,7 +75,7 @@ int BattleCbProxy::getBattlefieldType(lua_State * L) auto ret = object->battleGetBattlefieldType(); - return LuaStack::quickRetStr(L, ret); + return LuaStack::quickRetStr(L, ret.getInfo()->identifier); } int BattleCbProxy::getTerrainType(lua_State * L) diff --git a/scripts/lib/erm/BU.lua b/scripts/lib/erm/BU.lua index ee4ee6b9f..0aabe23aa 100644 --- a/scripts/lib/erm/BU.lua +++ b/scripts/lib/erm/BU.lua @@ -55,16 +55,16 @@ end local SPECIAL_FIELDS = {} -SPECIAL_FIELDS[0] = 0 -SPECIAL_FIELDS[22] = 1 -SPECIAL_FIELDS[9] = 2 -SPECIAL_FIELDS[18] = 3 -SPECIAL_FIELDS[20] = 4 -SPECIAL_FIELDS[19] = 5 -SPECIAL_FIELDS[17] = 6 -SPECIAL_FIELDS[14] = 7 -SPECIAL_FIELDS[15] = 8 -SPECIAL_FIELDS[16] = 9 +SPECIAL_FIELDS['sand_shore'] = 0 +SPECIAL_FIELDS['cursed_ground'] = 1 +SPECIAL_FIELDS['magic_plains'] = 2 +SPECIAL_FIELDS['holy_ground'] = 3 +SPECIAL_FIELDS['evil_fog'] = 4 +SPECIAL_FIELDS['clover_field'] = 5 +SPECIAL_FIELDS['lucid_pools'] = 6 +SPECIAL_FIELDS['fiery_fields'] = 7 +SPECIAL_FIELDS['rocklands'] = 8 +SPECIAL_FIELDS['magic_clouds'] = 9 function BU:G(x, p1) diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 71754f0ea..383e7b205 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -2221,7 +2221,7 @@ void CGameHandler::setupBattle(int3 tile, const CArmedInstance *armies[2], const BattleField terType = gs->battleGetBattlefieldType(tile, getRandomGenerator()); if (heroes[0] && heroes[0]->boat && heroes[1] && heroes[1]->boat) - terType = BattleField("ship_to_ship"); + terType = BattleField::fromString("ship_to_ship"); //send info about battles BattleStart bs; diff --git a/test/battle/CBattleInfoCallbackTest.cpp b/test/battle/CBattleInfoCallbackTest.cpp index b552304f4..4504180f8 100644 --- a/test/battle/CBattleInfoCallbackTest.cpp +++ b/test/battle/CBattleInfoCallbackTest.cpp @@ -10,6 +10,7 @@ #include "StdInc.h" #include "../../lib/battle/CBattleInfoCallback.h" +#include "../../lib/battle/CUnitState.h" #include @@ -25,7 +26,15 @@ using namespace testing; class UnitFake : public UnitMock { +private: + std::shared_ptr state; + public: + UnitFake() + { + state.reset(new CUnitStateDetached(this, this)); + } + void addNewBonus(const std::shared_ptr & b) { bonusFake.addNewBonus(b); @@ -58,6 +67,13 @@ public: EXPECT_CALL(*this, unitSlot()).WillRepeatedly(Return(SlotID(0))); EXPECT_CALL(*this, creatureIndex()).WillRepeatedly(Return(0)); } + + void setDefaultState() + { + EXPECT_CALL(*this, isClone()).WillRepeatedly(Return(false)); + EXPECT_CALL(*this, isCaster()).WillRepeatedly(Return(false)); + EXPECT_CALL(*this, acquireState()).WillRepeatedly(Return(state)); + } private: BonusBearerMock bonusFake; }; @@ -207,6 +223,7 @@ TEST_F(BattleFinishedTest, LastAliveUnitWins) { UnitFake & unit = unitsFake.add(1); unit.makeAlive(); + unit.setDefaultState(); setDefaultExpectations(); startBattle(); @@ -232,6 +249,7 @@ TEST_F(BattleFinishedTest, LastWarMachineNotWins) UnitFake & unit = unitsFake.add(0); unit.makeAlive(); unit.makeWarMachine(); + unit.setDefaultState(); setDefaultExpectations(); startBattle(); @@ -241,17 +259,26 @@ TEST_F(BattleFinishedTest, LastWarMachineNotWins) TEST_F(BattleFinishedTest, LastWarMachineLoose) { - UnitFake & unit1 = unitsFake.add(0); - unit1.makeAlive(); + try + { + UnitFake & unit1 = unitsFake.add(0); + unit1.makeAlive(); + unit1.setDefaultState(); - UnitFake & unit2 = unitsFake.add(1); - unit2.makeAlive(); - unit2.makeWarMachine(); + UnitFake & unit2 = unitsFake.add(1); + unit2.makeAlive(); + unit2.makeWarMachine(); + unit2.setDefaultState(); - setDefaultExpectations(); - startBattle(); + setDefaultExpectations(); + startBattle(); - expectBattleWinner(0); + expectBattleWinner(0); + } + catch(std::exception e) + { + logGlobal->error(e.what()); + } } class BattleMatchOwnerTest : public CBattleInfoCallbackTest diff --git a/test/erm/ERM_BU.cpp b/test/erm/ERM_BU.cpp index 87273b2e9..4db1c3bc7 100644 --- a/test/erm/ERM_BU.cpp +++ b/test/erm/ERM_BU.cpp @@ -155,7 +155,7 @@ TEST_F(ERM_BU_G, Get) source << "!?PI;" << std::endl; source << "!!BU:G?v1;" << std::endl; - EXPECT_CALL(binfoMock, battleGetBattlefieldType()).WillOnce(Return(BattleField("snow_trees"))); + EXPECT_CALL(binfoMock, battleGetBattlefieldType()).WillOnce(Return(BattleField::fromString("snow_trees"))); loadScript(VLC->scriptHandler->erm, source.str()); @@ -169,12 +169,14 @@ TEST_F(ERM_BU_G, Get) TEST_F(ERM_BU_G, Get2) { + const int EXPECTED_ERM_FOG_CODE = 4; + std::stringstream source; source << "VERM" << std::endl; source << "!?PI;" << std::endl; source << "!!BU:G?v1;" << std::endl; - EXPECT_CALL(binfoMock, battleGetBattlefieldType()).WillOnce(Return(BattleField("evil_fog"))); + EXPECT_CALL(binfoMock, battleGetBattlefieldType()).WillOnce(Return(BattleField::fromString("evil_fog"))); loadScript(VLC->scriptHandler->erm, source.str()); runServer(); @@ -182,7 +184,7 @@ TEST_F(ERM_BU_G, Get2) JsonNode actualState = context->saveState(); - EXPECT_EQ(actualState["ERM"]["v"]["1"], JsonUtils::floatNode(4)) << actualState.toJson(true); + EXPECT_EQ(actualState["ERM"]["v"]["1"], JsonUtils::floatNode(EXPECTED_ERM_FOG_CODE)) << actualState.toJson(true); } //TODO: ERM_BU_G Set diff --git a/test/game/CGameStateTest.cpp b/test/game/CGameStateTest.cpp index aebb82783..984cd6c02 100644 --- a/test/game/CGameStateTest.cpp +++ b/test/game/CGameStateTest.cpp @@ -194,7 +194,7 @@ public: const auto t = gameCallback->getTile(tile); Terrain terrain = t->terType; - BattleField terType = BattleField("grass_hills"); + BattleField terType = BattleField::fromString("grass_hills"); //send info about battles diff --git a/test/map/CMapEditManagerTest.cpp b/test/map/CMapEditManagerTest.cpp index f33ff4d9d..d028ac51f 100644 --- a/test/map/CMapEditManagerTest.cpp +++ b/test/map/CMapEditManagerTest.cpp @@ -132,7 +132,7 @@ TEST(MapManager, DrawTerrain_View) const auto & id = patternParts[1]; // Get mapping range - const auto & pattern = VLC->terviewh->getTerrainViewPatternById(groupStr, id); + const auto & pattern = VLC->terviewh->getTerrainViewPatternById(groupStr, id); const auto & mapping = (*pattern).mapping; const auto & positionsNode = node["pos"].Vector(); diff --git a/test/map/CMapFormatTest.cpp b/test/map/CMapFormatTest.cpp index bd497f6ac..e1d88f7a4 100644 --- a/test/map/CMapFormatTest.cpp +++ b/test/map/CMapFormatTest.cpp @@ -23,6 +23,7 @@ #include "MapComparer.h" #include "../JsonComparer.h" +#include "mock/ZoneOptionsFake.h" static const int TEST_RANDOM_SEED = 1337; @@ -43,10 +44,12 @@ TEST(MapFormat, Random) CMapGenOptions opt; CRmgTemplate tmpl; + std::shared_ptr zoneOptions = std::make_shared(); const_cast(tmpl.getCpuPlayers()).addRange(1, 4); - const_cast(tmpl.getZones())[0] = std::make_shared(); + const_cast(tmpl.getZones())[0] = zoneOptions; + zoneOptions->setOwner(1); opt.setMapTemplate(&tmpl); opt.setHeight(CMapHeader::MAP_SIZE_MIDDLE); diff --git a/test/mock/BattleFake.cpp b/test/mock/BattleFake.cpp index 23ce30f2f..f7bf0d595 100644 --- a/test/mock/BattleFake.cpp +++ b/test/mock/BattleFake.cpp @@ -92,7 +92,7 @@ void BattleFake::setupEmptyBattlefield() { EXPECT_CALL(*this, getDefendedTown()).WillRepeatedly(Return(nullptr)); EXPECT_CALL(*this, getAllObstacles()).WillRepeatedly(Return(IBattleInfo::ObstacleCList())); - EXPECT_CALL(*this, getBattlefieldType()).WillRepeatedly(Return(BattleField::NONE)); + EXPECT_CALL(*this, getBattlefieldType()).WillRepeatedly(Return(BattleField::fromString("grass_hills"))); } diff --git a/test/mock/ZoneOptionsFake.h b/test/mock/ZoneOptionsFake.h new file mode 100644 index 000000000..e371b2519 --- /dev/null +++ b/test/mock/ZoneOptionsFake.h @@ -0,0 +1,23 @@ +/* + * BattleFake.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 "../../../lib/mapping/CMap.h" +#include "../../../lib/rmg/CMapGenOptions.h" +#include "../../../lib/rmg/CMapGenerator.h" + +#pragma once + +class ZoneOptionsFake : public rmg::ZoneOptions +{ +public: + void setOwner(int ow) + { + this->owner = ow; + } +}; \ No newline at end of file diff --git a/test/mock/mock_Services.h b/test/mock/mock_Services.h index 00f6ffff0..7c6587159 100644 --- a/test/mock/mock_Services.h +++ b/test/mock/mock_Services.h @@ -23,7 +23,8 @@ public: MOCK_CONST_METHOD0(heroTypes, const HeroTypeService *()); MOCK_CONST_METHOD0(scripts, const scripting::Service *()); MOCK_CONST_METHOD0(spells, const spells::Service *()); - MOCK_CONST_METHOD0(skills, const SkillService *()); + MOCK_CONST_METHOD0(skills, const SkillService * ()); + MOCK_CONST_METHOD0(battlefields, const BattleFieldService *()); MOCK_METHOD3(updateEntity, void(Metatype, int32_t, const JsonNode &)); diff --git a/test/spells/effects/CloneTest.cpp b/test/spells/effects/CloneTest.cpp index 81391ec63..60cfa4631 100644 --- a/test/spells/effects/CloneTest.cpp +++ b/test/spells/effects/CloneTest.cpp @@ -232,7 +232,7 @@ TEST_F(CloneApplyTest, SetsLifetimeMarker) { setDefaultExpectations(); - EXPECT_CALL(*battleFake, addUnitBonus(_,_)).WillOnce(Invoke(this, &CloneApplyTest::checkCloneLifetimeMarker)); + EXPECT_CALL(*battleFake, addUnitBonus(_, _)).WillOnce(Invoke(this, &CloneApplyTest::checkCloneLifetimeMarker)); subject->apply(&serverMock, &mechanicsMock, target); }