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

battlefields in VLC and custom bonuses for terrain patches

This commit is contained in:
Andrii Danylchenko 2022-06-28 11:05:30 +03:00
parent 8b8e837495
commit 232010c5de
47 changed files with 645 additions and 221 deletions

View File

@ -37,6 +37,7 @@ void CGameInfo::setFromLib()
spellh = VLC->spellh; spellh = VLC->spellh;
skillh = VLC->skillh; skillh = VLC->skillh;
objtypeh = VLC->objtypeh; objtypeh = VLC->objtypeh;
battleFieldHandler = VLC->battlefieldsHandler;
} }
const ArtifactService * CGameInfo::artifacts() const const ArtifactService * CGameInfo::artifacts() const
@ -44,6 +45,11 @@ const ArtifactService * CGameInfo::artifacts() const
return globalServices->artifacts(); return globalServices->artifacts();
} }
const BattleFieldService * CGameInfo::battlefields() const
{
return globalServices->battlefields();
}
const CreatureService * CGameInfo::creatures() const const CreatureService * CGameInfo::creatures() const
{ {
return globalServices->creatures(); return globalServices->creatures();

View File

@ -31,6 +31,7 @@ class CCursorHandler;
class CGameState; class CGameState;
class IMainVideoPlayer; class IMainVideoPlayer;
class CServerHandler; class CServerHandler;
class BattleFieldHandler;
class CMap; class CMap;
@ -60,6 +61,7 @@ public:
const scripting::Service * scripts() const override; const scripting::Service * scripts() const override;
const spells::Service * spells() const override; const spells::Service * spells() const override;
const SkillService * skills() const override; const SkillService * skills() const override;
const BattleFieldService * battlefields() const override;
void updateEntity(Metatype metatype, int32_t index, const JsonNode & data) override; void updateEntity(Metatype metatype, int32_t index, const JsonNode & data) override;
@ -68,6 +70,7 @@ public:
ConstTransitivePtr<CModHandler> modh; //public? ConstTransitivePtr<CModHandler> modh; //public?
ConstTransitivePtr<BattleFieldHandler> battleFieldHandler;
ConstTransitivePtr<CHeroHandler> heroh; ConstTransitivePtr<CHeroHandler> heroh;
ConstTransitivePtr<CCreatureHandler> creh; ConstTransitivePtr<CCreatureHandler> creh;
ConstTransitivePtr<CSpellHandler> spellh; ConstTransitivePtr<CSpellHandler> spellh;

View File

@ -109,12 +109,6 @@ void Graphics::initializeBattleGraphics()
const JsonNode config(mod, ResourceID("config/battles_graphics.json")); 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 //initialization of AC->def name mapping
if(!config["ac_mapping"].isNull()) if(!config["ac_mapping"].isNull())
for(const JsonNode &ac : config["ac_mapping"].Vector()) for(const JsonNode &ac : config["ac_mapping"].Vector())

View File

@ -87,7 +87,6 @@ public:
//towns //towns
std::map<int, std::string> ERMUtoPicture[GameConstants::F_NUMBER]; //maps building ID to it's picture's name for each town type std::map<int, std::string> ERMUtoPicture[GameConstants::F_NUMBER]; //maps building ID to it's picture's name for each town type
//for battles //for battles
std::map<std::string, std::string> 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 std::map< int, std::vector < std::string > > battleACToDef; //maps AC format to vector of appropriate def names
//functions //functions

View File

@ -41,6 +41,7 @@
#include "../../lib/spells/ISpellMechanics.h" #include "../../lib/spells/ISpellMechanics.h"
#include "../../lib/spells/Problem.h" #include "../../lib/spells/Problem.h"
#include "../../lib/CTownHandler.h" #include "../../lib/CTownHandler.h"
#include "../../lib/BattleFieldHandler.h"
#include "../../lib/CGameState.h" #include "../../lib/CGameState.h"
#include "../../lib/mapping/CMap.h" #include "../../lib/mapping/CMap.h"
#include "../../lib/NetPacks.h" #include "../../lib/NetPacks.h"
@ -201,13 +202,14 @@ CBattleInterface::CBattleInterface(const CCreatureSet *army1, const CCreatureSet
else else
{ {
auto bfieldType = curInt->cb->battleGetBattlefieldType(); 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<std::string>(bfieldType)); logGlobal->error("Invalid battlefield returned for current battle");
} }
else else
{ {
background = BitmapHandler::loadBitmap(graphics->battleBacks[bfieldType], false); background = BitmapHandler::loadBitmap(bfieldType.getInfo()->graphics, false);
} }
} }

173
config/battlefields.json Normal file
View File

@ -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" }
}

View File

@ -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 // WoG_Ac_format_to_def_names_mapping
"ac_mapping": [ "ac_mapping": [
{ "id": 0, "defnames": [ "C10SPW.DEF" ] },//merged { "id": 0, "defnames": [ "C10SPW.DEF" ] },//merged

View File

@ -84,5 +84,9 @@
"terrains": "terrains":
[ [
"config/terrains.json" "config/terrains.json"
],
"battlefields":
[
"config/battlefields.json"
] ]
} }

View File

@ -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"
}
}
}
}

View File

@ -112,6 +112,12 @@
"description": "List of configuration files for RMG templates", "description": "List of configuration files for RMG templates",
"items": { "type":"string", "format" : "textFile" } "items": { "type":"string", "format" : "textFile" }
},
"battlefields":{
"type":"array",
"description": "List of configuration files for battlefields",
"items": { "type":"string", "format" : "textFile" }
}, },
"changelog" : { "changelog" : {

View File

@ -19,6 +19,7 @@ class HeroClassService;
class HeroTypeService; class HeroTypeService;
class SkillService; class SkillService;
class JsonNode; class JsonNode;
class BattleFieldService;
namespace spells namespace spells
{ {
@ -48,6 +49,7 @@ public:
virtual const scripting::Service * scripts() const = 0; virtual const scripting::Service * scripts() const = 0;
virtual const spells::Service * spells() const = 0; virtual const spells::Service * spells() const = 0;
virtual const SkillService * skills() 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; virtual void updateEntity(Metatype metatype, int32_t index, const JsonNode & data) = 0;

108
lib/BattleFieldHandler.cpp Normal file
View File

@ -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 <vcmi/Entity.h>
#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<JsonNode> BattleFieldHandler::loadLegacyData(size_t dataSize)
{
return std::vector<JsonNode>();
}
const std::vector<std::string> & BattleFieldHandler::getTypeNames() const
{
static const std::vector<std::string> types = std::vector<std::string> { "battlefield" };
return types;
}
std::vector<bool> BattleFieldHandler::getDefaultAllowed() const
{
return std::vector<bool>();
}
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;
}

85
lib/BattleFieldHandler.h Normal file
View File

@ -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 <vcmi/EntityService.h>
#include "HeroBonus.h"
#include "GameConstants.h"
#include "IHandlerBase.h"
#include "Terrain.h"
#include "battle/BattleHex.h"
class BattleFieldInfo : public EntityT<BattleField>
{
public:
BattleField battlefield;
std::vector<std::shared_ptr<Bonus>> bonuses;
bool isSpecial;
std::string graphics;
std::string name;
std::string identifier;
std::string icon;
si32 iconIndex;
std::vector<BattleHex> 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 <typename Handler> 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<BattleField, BattleFieldInfo>
{
public:
};
class BattleFieldHandler : public CHandlerBase<BattleField, BattleFieldInfo, BattleFieldInfo, BattleFieldService>
{
public:
virtual BattleFieldInfo * loadFromJson(
const std::string & scope,
const JsonNode & json,
const std::string & identifier,
size_t index) override;
virtual const std::vector<std::string> & getTypeNames() const override;
virtual std::vector<JsonNode> loadLegacyData(size_t dataSize) override;
virtual std::vector<bool> getDefaultAllowed() const override;
template <typename Handler> void serialize(Handler & h, const int version)
{
h & objects;
}
};

View File

@ -1922,9 +1922,10 @@ BattleField CGameState::battleGetBattlefieldType(int3 tile, CRandomGenerator & r
} }
if(map->isCoastalTile(tile)) //coastal tile is always ground 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) UpgradeInfo CGameState::getUpgradeInfo(const CStackInstance &stack)

View File

@ -25,6 +25,7 @@
#include <math.h> #include <math.h>
#include "mapObjects/CObjectClassesHandler.h" #include "mapObjects/CObjectClassesHandler.h"
#include "BattleFieldHandler.h"
CHero::CHero() = default; CHero::CHero() = default;
CHero::~CHero() = default; CHero::~CHero() = default;
@ -179,8 +180,10 @@ std::vector<BattleHex> CObstacleInfo::getBlocked(BattleHex hex) const
bool CObstacleInfo::isAppropriate(const Terrain & terrainType, const BattleField & battlefield) const bool CObstacleInfo::isAppropriate(const Terrain & terrainType, const BattleField & battlefield) const
{ {
if(battlefield.isSpecial()) auto bgInfo = battlefield.getInfo();
return vstd::contains(allowedSpecialBfields, battlefield);
if(bgInfo->isSpecial)
return vstd::contains(allowedSpecialBfields, bgInfo->identifier);
return vstd::contains(allowedTerrains, terrainType); return vstd::contains(allowedTerrains, terrainType);
} }

View File

@ -227,7 +227,7 @@ struct DLL_LINKAGE CObstacleInfo
{ {
std::string defName; std::string defName;
std::vector<Terrain> allowedTerrains; std::vector<Terrain> allowedTerrains;
std::vector<BattleField> allowedSpecialBfields; std::vector<std::string> allowedSpecialBfields;
ui8 isAbsoluteObstacle; //there may only one such obstacle in battle and its position is always the same 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) si32 width, height; //how much space to the right and up is needed to place obstacle (affects only placement algorithm)

View File

@ -130,6 +130,7 @@ set(lib_SRCS
vstd/StringUtils.cpp vstd/StringUtils.cpp
BattleFieldHandler.cpp
CAndroidVMHelper.cpp CAndroidVMHelper.cpp
CArtHandler.cpp CArtHandler.cpp
CBonusTypeHandler.cpp CBonusTypeHandler.cpp
@ -338,6 +339,7 @@ set(lib_HEADERS
spells/effects/Sacrifice.h spells/effects/Sacrifice.h
AI_Base.h AI_Base.h
BattleFieldHandler.h
CAndroidVMHelper.h CAndroidVMHelper.h
CArtHandler.h CArtHandler.h
CBonusTypeHandler.h CBonusTypeHandler.h

View File

@ -25,6 +25,7 @@
#include "spells/CSpellHandler.h" #include "spells/CSpellHandler.h"
#include "CSkillHandler.h" #include "CSkillHandler.h"
#include "ScriptHandler.h" #include "ScriptHandler.h"
#include "BattleFieldHandler.h"
#include <vstd/StringUtils.h> #include <vstd/StringUtils.h>
@ -433,6 +434,7 @@ void CContentHandler::init()
handlers.insert(std::make_pair("skills", ContentTypeHandler(VLC->skillh, "skill"))); 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("templates", ContentTypeHandler((IHandlerBase *)VLC->tplh, "template")));
handlers.insert(std::make_pair("scripts", ContentTypeHandler(VLC->scriptHandler, "script"))); 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? //TODO: any other types of moddables?
} }

View File

@ -34,6 +34,7 @@
#include "StringConstants.h" #include "StringConstants.h"
#include "CGeneralTextHandler.h" #include "CGeneralTextHandler.h"
#include "CModHandler.h"//todo: remove #include "CModHandler.h"//todo: remove
#include "BattleFieldHandler.h"
const SlotID SlotID::COMMANDER_SLOT_PLACEHOLDER = SlotID(-2); const SlotID SlotID::COMMANDER_SLOT_PLACEHOLDER = SlotID(-2);
const SlotID SlotID::SUMMONED_SLOT_PLACEHOLDER = SlotID(-3); 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 << "<Unknown type>"; if (it == pathfinderLayerToString.end()) return os << "<Unknown type>";
else return os << it->second; 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;
}

View File

@ -1109,6 +1109,23 @@ public:
ID_LIKE_OPERATORS(SpellID, SpellID::ESpellID) ID_LIKE_OPERATORS(SpellID, SpellID::ESpellID)
class BattleFieldInfo;
class BattleField : public BaseForID<BattleField, si32>
{
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 enum class ESpellSchool: ui8
{ {
AIR = 0, AIR = 0,

View File

@ -16,6 +16,7 @@
#include "NetPacks.h" #include "NetPacks.h"
#include "CBonusTypeHandler.h" #include "CBonusTypeHandler.h"
#include "CModHandler.h" #include "CModHandler.h"
#include "BattleFieldHandler.h"
#include "serializer/CSerializer.h" // for SAVEGAME_MAGIC #include "serializer/CSerializer.h" // for SAVEGAME_MAGIC
#include "serializer/BinaryDeserializer.h" #include "serializer/BinaryDeserializer.h"

View File

@ -18,8 +18,6 @@
const Terrain Terrain::ANY("ANY"); const Terrain Terrain::ANY("ANY");
const BattleField BattleField::NONE("");
Terrain Terrain::createTerrainTypeH3M(int tId) Terrain Terrain::createTerrainTypeH3M(int tId)
{ {
static std::array<std::string, 10> terrainsH3M static std::array<std::string, 10> terrainsH3M
@ -227,27 +225,3 @@ bool Terrain::isTransitionRequired() const
{ {
return Terrain::Manager::getInfo(*this).transitionRequired; 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<std::string>{}(name);
}

View File

@ -11,47 +11,9 @@
#pragma once #pragma once
#include "ConstTransitivePtr.h" #include "ConstTransitivePtr.h"
#include "GameConstants.h"
#include "JsonNode.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 <typename Handler> 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 class DLL_LINKAGE Terrain
{ {
@ -77,7 +39,7 @@ public:
std::string terrainViewPatterns; std::string terrainViewPatterns;
int horseSoundId; int horseSoundId;
Type type; Type type;
std::vector<BattleField> battleFields; std::vector<std::string> battleFields;
}; };
class DLL_LINKAGE Manager class DLL_LINKAGE Manager

View File

@ -32,6 +32,7 @@
#include "rmg/CRmgTemplateStorage.h" #include "rmg/CRmgTemplateStorage.h"
#include "mapping/CMapEditManager.h" #include "mapping/CMapEditManager.h"
#include "ScriptHandler.h" #include "ScriptHandler.h"
#include "BattleFieldHandler.h"
LibClasses * VLC = nullptr; LibClasses * VLC = nullptr;
@ -110,6 +111,11 @@ spells::effects::Registry * LibClasses::spellEffects()
return spells::effects::GlobalRegistry::get(); return spells::effects::GlobalRegistry::get();
} }
const BattleFieldService * LibClasses::battlefields() const
{
return battlefieldsHandler;
}
void LibClasses::updateEntity(Metatype metatype, int32_t index, const JsonNode & data) void LibClasses::updateEntity(Metatype metatype, int32_t index, const JsonNode & data)
{ {
switch(metatype) switch(metatype)
@ -205,6 +211,8 @@ void LibClasses::init(bool onlyEssential)
createHandler(scriptHandler, "Script", pomtime); createHandler(scriptHandler, "Script", pomtime);
createHandler(battlefieldsHandler, "Battlefields", pomtime);
logGlobal->info("\tInitializing handlers: %d ms", totalTime.getDiff()); logGlobal->info("\tInitializing handlers: %d ms", totalTime.getDiff());
modh->load(); modh->load();
@ -231,6 +239,7 @@ void LibClasses::clear()
delete tplh; delete tplh;
delete terviewh; delete terviewh;
delete scriptHandler; delete scriptHandler;
delete battlefieldsHandler;
makeNull(); makeNull();
} }
@ -250,6 +259,7 @@ void LibClasses::makeNull()
tplh = nullptr; tplh = nullptr;
terviewh = nullptr; terviewh = nullptr;
scriptHandler = nullptr; scriptHandler = nullptr;
battlefieldsHandler = nullptr;
} }
LibClasses::LibClasses() LibClasses::LibClasses()

View File

@ -24,6 +24,7 @@ class CTownHandler;
class CGeneralTextHandler; class CGeneralTextHandler;
class CModHandler; class CModHandler;
class CContentHandler; class CContentHandler;
class BattleFieldHandler;
class IBonusTypeHandler; class IBonusTypeHandler;
class CBonusTypeHandler; class CBonusTypeHandler;
class CTerrainViewPatternConfig; class CTerrainViewPatternConfig;
@ -56,6 +57,7 @@ public:
const scripting::Service * scripts() const override; const scripting::Service * scripts() const override;
const spells::Service * spells() const override; const spells::Service * spells() const override;
const SkillService * skills() const override; const SkillService * skills() const override;
const BattleFieldService * battlefields() const override;
void updateEntity(Metatype metatype, int32_t index, const JsonNode & data) override; void updateEntity(Metatype metatype, int32_t index, const JsonNode & data) override;
@ -76,6 +78,7 @@ public:
CModHandler * modh; CModHandler * modh;
CTerrainViewPatternConfig * terviewh; CTerrainViewPatternConfig * terviewh;
CRmgTemplateStorage * tplh; CRmgTemplateStorage * tplh;
BattleFieldHandler * battlefieldsHandler;
scripting::ScriptHandler * scriptHandler; scripting::ScriptHandler * scriptHandler;
LibClasses(); //c-tor, loads .lods and NULLs handlers LibClasses(); //c-tor, loads .lods and NULLs handlers
@ -104,6 +107,8 @@ public:
h & objtypeh; h & objtypeh;
h & spellh; h & spellh;
h & skillh; h & skillh;
h & battlefieldsHandler;
if(!h.saving) if(!h.saving)
{ {
//modh will be changed and modh->content will be empty after deserialization //modh will be changed and modh->content will be empty after deserialization

View File

@ -16,6 +16,7 @@
#include "../mapObjects/CGTownInstance.h" #include "../mapObjects/CGTownInstance.h"
#include "../CGeneralTextHandler.h" #include "../CGeneralTextHandler.h"
#include "../Terrain.h" #include "../Terrain.h"
#include "../BattleFieldHandler.h"
//TODO: remove //TODO: remove
#include "../IGameCallback.h" #include "../IGameCallback.h"
@ -458,64 +459,12 @@ BattleInfo * BattleInfo::setupBattle(const int3 & tile, const Terrain & terrain,
auto good = std::make_shared<CreatureAlignmentLimiter>(EAlignment::GOOD); auto good = std::make_shared<CreatureAlignmentLimiter>(EAlignment::GOOD);
auto evil = std::make_shared<CreatureAlignmentLimiter>(EAlignment::EVIL); auto evil = std::make_shared<CreatureAlignmentLimiter>(EAlignment::EVIL);
//giving terrain overlay premies auto bgInfo = VLC->battlefields()->getById(battlefieldType);
int bonusSubtype = -1;
if(battlefieldType == BattleField("magic_plains")) for(const std::shared_ptr<Bonus> & 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>(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>(Bonus::ONE_BATTLE, Bonus::MORALE, Bonus::TERRAIN_OVERLAY, +1, battlefieldType.hash(), goodArmyDesc, 0)->addLimiter(good));
curB->addNewBonus(std::make_shared<Bonus>(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>(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>(Bonus::ONE_BATTLE, Bonus::MORALE, Bonus::TERRAIN_OVERLAY, -1, battlefieldType.hash(), goodArmyDesc, 0)->addLimiter(good));
curB->addNewBonus(std::make_shared<Bonus>(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>(Bonus::ONE_BATTLE, Bonus::NO_MORALE, Bonus::TERRAIN_OVERLAY, 0, battlefieldType.hash(), VLC->generaltexth->arraytxt[112], 0));
curB->addNewBonus(std::make_shared<Bonus>(Bonus::ONE_BATTLE, Bonus::NO_LUCK, Bonus::TERRAIN_OVERLAY, 0, battlefieldType.hash(), VLC->generaltexth->arraytxt[81], 0));
curB->addNewBonus(std::make_shared<Bonus>(Bonus::ONE_BATTLE, Bonus::BLOCK_MAGIC_ABOVE, Bonus::TERRAIN_OVERLAY, 1, battlefieldType.hash(), 0, Bonus::INDEPENDENT_MIN));
}
//overlay premies given
//native terrain bonuses //native terrain bonuses
static auto nativeTerrain = std::make_shared<CreatureTerrainLimiter>(); static auto nativeTerrain = std::make_shared<CreatureTerrainLimiter>();

View File

@ -17,6 +17,7 @@
#include "../NetPacks.h" #include "../NetPacks.h"
#include "../spells/CSpellHandler.h" #include "../spells/CSpellHandler.h"
#include "../mapObjects/CGTownInstance.h" #include "../mapObjects/CGTownInstance.h"
#include "../BattleFieldHandler.h"
namespace SiegeStuffThatShouldBeMovedToHandlers // <=== TODO namespace SiegeStuffThatShouldBeMovedToHandlers // <=== TODO
{ {
@ -1058,24 +1059,15 @@ AccessibilityInfo CBattleInfoCallback::getAccesibility() const
} }
//special battlefields with logically unavailable tiles //special battlefields with logically unavailable tiles
std::vector<BattleHex> impassableHexes; auto bFieldType = battleGetBattlefieldType();
if(battleGetBattlefieldType() == BattleField("ship_to_ship"))
if(bFieldType != BattleField::NONE)
{ {
impassableHexes = std::vector<BattleHex> impassableHexes = bFieldType.getInfo()->impassableHexes;
{
6, 7, 8, 9, for(auto hex : impassableHexes)
24, 25, 26, ret[hex] = EAccessibility::UNAVAILABLE;
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
};
} }
for(auto hex : impassableHexes)
ret[hex] = EAccessibility::UNAVAILABLE;
//gate -> should be before stacks //gate -> should be before stacks
if(battleGetSiegeLevel() > 0) if(battleGetSiegeLevel() > 0)

View File

@ -515,10 +515,10 @@ void AObjectTypeHandler::init(const JsonNode & input, boost::optional<std::strin
else else
aiValue = static_cast<boost::optional<si32>>(input["aiValue"].Integer()); aiValue = static_cast<boost::optional<si32>>(input["aiValue"].Integer());
if(input["battleground"].isNull()) if(input["battleground"].getType() == JsonNode::JsonType::DATA_STRING)
battlefield = BattleField::NONE; battlefield = input["battleground"].String();
else else
battlefield = BattleField(input["battleground"].String()); battlefield = boost::none;
initTypeData(input); initTypeData(input);
} }
@ -577,7 +577,7 @@ std::vector<ObjectTemplate> AObjectTypeHandler::getTemplates() const
BattleField AObjectTypeHandler::getBattlefield() const BattleField AObjectTypeHandler::getBattlefield() const
{ {
return battlefield; return battlefield ? BattleField::fromString(battlefield.get()) : BattleField::NONE;
} }
std::vector<ObjectTemplate> AObjectTypeHandler::getTemplates(const Terrain & terrainType) const std::vector<ObjectTemplate> AObjectTypeHandler::getTemplates(const Terrain & terrainType) const

View File

@ -150,7 +150,7 @@ class DLL_LINKAGE AObjectTypeHandler : public boost::noncopyable
boost::optional<si32> aiValue; boost::optional<si32> aiValue;
BattleField battlefield; boost::optional<std::string> battlefield;
protected: protected:
void preInitObject(CGObjectInstance * obj) const; void preInitObject(CGObjectInstance * obj) const;
@ -219,6 +219,7 @@ public:
h & subTypeName; h & subTypeName;
h & sounds; h & sounds;
h & aiValue; h & aiValue;
h & battlefield;
} }
}; };

View File

@ -461,12 +461,15 @@ const std::vector<CTerrainViewPatternConfig::TVPVector> & CTerrainViewPatternCon
return iter->second; return iter->second;
} }
boost::optional<const TerrainViewPattern &> CTerrainViewPatternConfig::getTerrainViewPatternById(const Terrain & terrain, const std::string & id) const boost::optional<const TerrainViewPattern &> CTerrainViewPatternConfig::getTerrainViewPatternById(std::string patternId, const std::string & id) const
{ {
const std::vector<TVPVector> & groupPatterns = getTerrainViewPatterns(terrain); auto iter = terrainViewPatterns.find(patternId);
const std::vector<TVPVector> & groupPatterns = (iter == terrainViewPatterns.end()) ? terrainViewPatterns.at("normal") : iter->second;
for (const TVPVector & patternFlips : groupPatterns) for (const TVPVector & patternFlips : groupPatterns)
{ {
const TerrainViewPattern & pattern = patternFlips.front(); const TerrainViewPattern & pattern = patternFlips.front();
if(id == pattern.id) if(id == pattern.id)
{ {
return boost::optional<const TerrainViewPattern &>(pattern); return boost::optional<const TerrainViewPattern &>(pattern);

View File

@ -327,7 +327,7 @@ public:
~CTerrainViewPatternConfig(); ~CTerrainViewPatternConfig();
const std::vector<TVPVector> & getTerrainViewPatterns(const Terrain & terrain) const; const std::vector<TVPVector> & getTerrainViewPatterns(const Terrain & terrain) const;
boost::optional<const TerrainViewPattern &> getTerrainViewPatternById(const Terrain & terrain, const std::string & id) const; boost::optional<const TerrainViewPattern &> getTerrainViewPatternById(std::string patternId, const std::string & id) const;
boost::optional<const TVPVector &> getTerrainViewPatternsById(const Terrain & terrain, const std::string & id) const; boost::optional<const TVPVector &> getTerrainViewPatternsById(const Terrain & terrain, const std::string & id) const;
const TVPVector * getTerrainTypePatternById(const std::string & id) const; const TVPVector * getTerrainTypePatternById(const std::string & id) const;
void flipPattern(TerrainViewPattern & pattern, int flip) const; void flipPattern(TerrainViewPattern & pattern, int flip) const;

View File

@ -55,6 +55,7 @@ void registerTypesMapObjects1(Serializer &s)
s.template registerType<CGObjectInstance, CGShipyard>(); s.template registerType<IShipyard, CGShipyard>(); s.template registerType<CGObjectInstance, CGShipyard>(); s.template registerType<IShipyard, CGShipyard>();
s.template registerType<CGObjectInstance, CGDenOfthieves>(); s.template registerType<CGObjectInstance, CGDenOfthieves>();
s.template registerType<CGObjectInstance, CGLighthouse>(); s.template registerType<CGObjectInstance, CGLighthouse>();
s.template registerType<CGObjectInstance, CGTerrainPatch>();
s.template registerType<CGObjectInstance, CGMarket>(); s.template registerType<IMarket, CGMarket>(); s.template registerType<CGObjectInstance, CGMarket>(); s.template registerType<IMarket, CGMarket>();
s.template registerType<CGMarket, CGBlackMarket>(); s.template registerType<CGMarket, CGBlackMarket>();
s.template registerType<CGMarket, CGUniversity>(); s.template registerType<CGMarket, CGUniversity>();
@ -108,6 +109,7 @@ void registerTypesMapObjectTypes(Serializer &s)
REGISTER_GENERIC_HANDLER(CGHeroInstance); REGISTER_GENERIC_HANDLER(CGHeroInstance);
REGISTER_GENERIC_HANDLER(CGKeymasterTent); REGISTER_GENERIC_HANDLER(CGKeymasterTent);
REGISTER_GENERIC_HANDLER(CGLighthouse); REGISTER_GENERIC_HANDLER(CGLighthouse);
REGISTER_GENERIC_HANDLER(CGTerrainPatch);
REGISTER_GENERIC_HANDLER(CGMagi); REGISTER_GENERIC_HANDLER(CGMagi);
REGISTER_GENERIC_HANDLER(CGMagicSpring); REGISTER_GENERIC_HANDLER(CGMagicSpring);
REGISTER_GENERIC_HANDLER(CGMagicWell); REGISTER_GENERIC_HANDLER(CGMagicWell);

View File

@ -246,8 +246,18 @@ void CMapGenOptions::finalize(CRandomGenerator & rand)
if(waterContent == EWaterContent::RANDOM) 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) if(monsterStrength == EMonsterStrength::RANDOM)
{ {
monsterStrength = static_cast<EMonsterStrength::EMonsterStrength>(rand.nextInt(EMonsterStrength::GLOBAL_WEAK, EMonsterStrength::GLOBAL_STRONG)); monsterStrength = static_cast<EMonsterStrength::EMonsterStrength>(rand.nextInt(EMonsterStrength::GLOBAL_WEAK, EMonsterStrength::GLOBAL_STRONG));

View File

@ -2019,6 +2019,10 @@ int3 CRmgTemplateZone::makeBoat(TRmgTemplateZoneId land, const std::set<int3> &
{ {
std::set<int3> lakeCoast; std::set<int3> lakeCoast;
std::set_intersection(gen->getZones()[land]->getCoastTiles().begin(), gen->getZones()[land]->getCoastTiles().end(), lake.begin(), lake.end(), std::inserter(lakeCoast, lakeCoast.begin())); 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) for(int randomAttempts = 0; randomAttempts<5; ++randomAttempts)
{ {
auto coastTile = *RandomGeneratorUtil::nextItem(lakeCoast, gen->rand); auto coastTile = *RandomGeneratorUtil::nextItem(lakeCoast, gen->rand);

View File

@ -12,6 +12,7 @@
#include "ISpellMechanics.h" #include "ISpellMechanics.h"
#include "../CRandomGenerator.h" #include "../CRandomGenerator.h"
#include "../VCMI_Lib.h"
#include "../HeroBonus.h" #include "../HeroBonus.h"
#include "../battle/CBattleInfoCallback.h" #include "../battle/CBattleInfoCallback.h"
@ -39,6 +40,7 @@
#include "../CHeroHandler.h"//todo: remove #include "../CHeroHandler.h"//todo: remove
#include "../IGameCallback.h"//todo: remove #include "../IGameCallback.h"//todo: remove
#include "../BattleFieldHandler.h"
namespace spells namespace spells
{ {
@ -520,7 +522,7 @@ bool BaseMechanics::adaptProblem(ESpellCastProblem::ESpellCastProblem source, Pr
caster->getCasterName(text); caster->getCasterName(text);
target.add(std::move(text), spells::Problem::NORMAL); 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); text.addTxt(MetaString::GENERAL_TXT, 537);
target.add(std::move(text), spells::Problem::NORMAL); target.add(std::move(text), spells::Problem::NORMAL);

View File

@ -10,12 +10,14 @@
#include "StdInc.h" #include "StdInc.h"
#include "BattleCb.h" #include "BattleCb.h"
#include <vcmi/Entity.h>
#include "../LuaStack.h" #include "../LuaStack.h"
#include "../LuaCallWrapper.h" #include "../LuaCallWrapper.h"
#include "../../../lib/GameConstants.h" #include "../../../lib/GameConstants.h"
#include "../../../lib/battle/Unit.h" #include "../../../lib/battle/Unit.h"
#include "../../../lib/BattleFieldHandler.h"
namespace scripting namespace scripting
{ {
@ -73,7 +75,7 @@ int BattleCbProxy::getBattlefieldType(lua_State * L)
auto ret = object->battleGetBattlefieldType(); auto ret = object->battleGetBattlefieldType();
return LuaStack::quickRetStr(L, ret); return LuaStack::quickRetStr(L, ret.getInfo()->identifier);
} }
int BattleCbProxy::getTerrainType(lua_State * L) int BattleCbProxy::getTerrainType(lua_State * L)

View File

@ -55,16 +55,16 @@ end
local SPECIAL_FIELDS = {} local SPECIAL_FIELDS = {}
SPECIAL_FIELDS[0] = 0 SPECIAL_FIELDS['sand_shore'] = 0
SPECIAL_FIELDS[22] = 1 SPECIAL_FIELDS['cursed_ground'] = 1
SPECIAL_FIELDS[9] = 2 SPECIAL_FIELDS['magic_plains'] = 2
SPECIAL_FIELDS[18] = 3 SPECIAL_FIELDS['holy_ground'] = 3
SPECIAL_FIELDS[20] = 4 SPECIAL_FIELDS['evil_fog'] = 4
SPECIAL_FIELDS[19] = 5 SPECIAL_FIELDS['clover_field'] = 5
SPECIAL_FIELDS[17] = 6 SPECIAL_FIELDS['lucid_pools'] = 6
SPECIAL_FIELDS[14] = 7 SPECIAL_FIELDS['fiery_fields'] = 7
SPECIAL_FIELDS[15] = 8 SPECIAL_FIELDS['rocklands'] = 8
SPECIAL_FIELDS[16] = 9 SPECIAL_FIELDS['magic_clouds'] = 9
function BU:G(x, p1) function BU:G(x, p1)

View File

@ -2221,7 +2221,7 @@ void CGameHandler::setupBattle(int3 tile, const CArmedInstance *armies[2], const
BattleField terType = gs->battleGetBattlefieldType(tile, getRandomGenerator()); BattleField terType = gs->battleGetBattlefieldType(tile, getRandomGenerator());
if (heroes[0] && heroes[0]->boat && heroes[1] && heroes[1]->boat) 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 //send info about battles
BattleStart bs; BattleStart bs;

View File

@ -10,6 +10,7 @@
#include "StdInc.h" #include "StdInc.h"
#include "../../lib/battle/CBattleInfoCallback.h" #include "../../lib/battle/CBattleInfoCallback.h"
#include "../../lib/battle/CUnitState.h"
#include <vstd/RNG.h> #include <vstd/RNG.h>
@ -25,7 +26,15 @@ using namespace testing;
class UnitFake : public UnitMock class UnitFake : public UnitMock
{ {
private:
std::shared_ptr<CUnitState> state;
public: public:
UnitFake()
{
state.reset(new CUnitStateDetached(this, this));
}
void addNewBonus(const std::shared_ptr<Bonus> & b) void addNewBonus(const std::shared_ptr<Bonus> & b)
{ {
bonusFake.addNewBonus(b); bonusFake.addNewBonus(b);
@ -58,6 +67,13 @@ public:
EXPECT_CALL(*this, unitSlot()).WillRepeatedly(Return(SlotID(0))); EXPECT_CALL(*this, unitSlot()).WillRepeatedly(Return(SlotID(0)));
EXPECT_CALL(*this, creatureIndex()).WillRepeatedly(Return(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: private:
BonusBearerMock bonusFake; BonusBearerMock bonusFake;
}; };
@ -207,6 +223,7 @@ TEST_F(BattleFinishedTest, LastAliveUnitWins)
{ {
UnitFake & unit = unitsFake.add(1); UnitFake & unit = unitsFake.add(1);
unit.makeAlive(); unit.makeAlive();
unit.setDefaultState();
setDefaultExpectations(); setDefaultExpectations();
startBattle(); startBattle();
@ -232,6 +249,7 @@ TEST_F(BattleFinishedTest, LastWarMachineNotWins)
UnitFake & unit = unitsFake.add(0); UnitFake & unit = unitsFake.add(0);
unit.makeAlive(); unit.makeAlive();
unit.makeWarMachine(); unit.makeWarMachine();
unit.setDefaultState();
setDefaultExpectations(); setDefaultExpectations();
startBattle(); startBattle();
@ -241,17 +259,26 @@ TEST_F(BattleFinishedTest, LastWarMachineNotWins)
TEST_F(BattleFinishedTest, LastWarMachineLoose) TEST_F(BattleFinishedTest, LastWarMachineLoose)
{ {
UnitFake & unit1 = unitsFake.add(0); try
unit1.makeAlive(); {
UnitFake & unit1 = unitsFake.add(0);
unit1.makeAlive();
unit1.setDefaultState();
UnitFake & unit2 = unitsFake.add(1); UnitFake & unit2 = unitsFake.add(1);
unit2.makeAlive(); unit2.makeAlive();
unit2.makeWarMachine(); unit2.makeWarMachine();
unit2.setDefaultState();
setDefaultExpectations(); setDefaultExpectations();
startBattle(); startBattle();
expectBattleWinner(0); expectBattleWinner(0);
}
catch(std::exception e)
{
logGlobal->error(e.what());
}
} }
class BattleMatchOwnerTest : public CBattleInfoCallbackTest class BattleMatchOwnerTest : public CBattleInfoCallbackTest

View File

@ -155,7 +155,7 @@ TEST_F(ERM_BU_G, Get)
source << "!?PI;" << std::endl; source << "!?PI;" << std::endl;
source << "!!BU:G?v1;" << 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()); loadScript(VLC->scriptHandler->erm, source.str());
@ -169,12 +169,14 @@ TEST_F(ERM_BU_G, Get)
TEST_F(ERM_BU_G, Get2) TEST_F(ERM_BU_G, Get2)
{ {
const int EXPECTED_ERM_FOG_CODE = 4;
std::stringstream source; std::stringstream source;
source << "VERM" << std::endl; source << "VERM" << std::endl;
source << "!?PI;" << std::endl; source << "!?PI;" << std::endl;
source << "!!BU:G?v1;" << 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()); loadScript(VLC->scriptHandler->erm, source.str());
runServer(); runServer();
@ -182,7 +184,7 @@ TEST_F(ERM_BU_G, Get2)
JsonNode actualState = context->saveState(); 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 //TODO: ERM_BU_G Set

View File

@ -194,7 +194,7 @@ public:
const auto t = gameCallback->getTile(tile); const auto t = gameCallback->getTile(tile);
Terrain terrain = t->terType; Terrain terrain = t->terType;
BattleField terType = BattleField("grass_hills"); BattleField terType = BattleField::fromString("grass_hills");
//send info about battles //send info about battles

View File

@ -23,6 +23,7 @@
#include "MapComparer.h" #include "MapComparer.h"
#include "../JsonComparer.h" #include "../JsonComparer.h"
#include "mock/ZoneOptionsFake.h"
static const int TEST_RANDOM_SEED = 1337; static const int TEST_RANDOM_SEED = 1337;
@ -43,10 +44,12 @@ TEST(MapFormat, Random)
CMapGenOptions opt; CMapGenOptions opt;
CRmgTemplate tmpl; CRmgTemplate tmpl;
std::shared_ptr<ZoneOptionsFake> zoneOptions = std::make_shared<ZoneOptionsFake>();
const_cast<CRmgTemplate::CPlayerCountRange &>(tmpl.getCpuPlayers()).addRange(1, 4); const_cast<CRmgTemplate::CPlayerCountRange &>(tmpl.getCpuPlayers()).addRange(1, 4);
const_cast<CRmgTemplate::Zones &>(tmpl.getZones())[0] = std::make_shared<rmg::ZoneOptions>(); const_cast<CRmgTemplate::Zones &>(tmpl.getZones())[0] = zoneOptions;
zoneOptions->setOwner(1);
opt.setMapTemplate(&tmpl); opt.setMapTemplate(&tmpl);
opt.setHeight(CMapHeader::MAP_SIZE_MIDDLE); opt.setHeight(CMapHeader::MAP_SIZE_MIDDLE);

View File

@ -92,7 +92,7 @@ void BattleFake::setupEmptyBattlefield()
{ {
EXPECT_CALL(*this, getDefendedTown()).WillRepeatedly(Return(nullptr)); EXPECT_CALL(*this, getDefendedTown()).WillRepeatedly(Return(nullptr));
EXPECT_CALL(*this, getAllObstacles()).WillRepeatedly(Return(IBattleInfo::ObstacleCList())); 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")));
} }

View File

@ -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;
}
};

View File

@ -23,7 +23,8 @@ public:
MOCK_CONST_METHOD0(heroTypes, const HeroTypeService *()); MOCK_CONST_METHOD0(heroTypes, const HeroTypeService *());
MOCK_CONST_METHOD0(scripts, const scripting::Service *()); MOCK_CONST_METHOD0(scripts, const scripting::Service *());
MOCK_CONST_METHOD0(spells, const spells::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 &)); MOCK_METHOD3(updateEntity, void(Metatype, int32_t, const JsonNode &));

View File

@ -232,7 +232,7 @@ TEST_F(CloneApplyTest, SetsLifetimeMarker)
{ {
setDefaultExpectations(); setDefaultExpectations();
EXPECT_CALL(*battleFake, addUnitBonus(_,_)).WillOnce(Invoke(this, &CloneApplyTest::checkCloneLifetimeMarker)); EXPECT_CALL(*battleFake, addUnitBonus(_, _)).WillOnce(Invoke(this, &CloneApplyTest::checkCloneLifetimeMarker));
subject->apply(&serverMock, &mechanicsMock, target); subject->apply(&serverMock, &mechanicsMock, target);
} }