1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-01-26 03:52:01 +02:00

Merge pull request #146 from vcmi/feature/VCMIMapFormat1

Ok let's try it.
This commit is contained in:
DjWarmonger 2016-03-01 07:49:54 +01:00
commit 8f1fba9551
89 changed files with 4536 additions and 582 deletions

View File

@ -19,7 +19,7 @@ set(VCMI_VERSION_PATCH 0)
option(ENABLE_ERM "Enable compilation of ERM scripting module" OFF)
option(ENABLE_EDITOR "Enable compilation of map editor" OFF)
option(ENABLE_LAUNCHER "Enable compilation of launcher" ON)
option(ENABLE_TEST "Enable compilation of unit tests" OFF)
option(ENABLE_TEST "Enable compilation of unit tests" ON)
option(ENABLE_PCH "Enable compilation using precompiled headers" ON)
############################################

View File

@ -0,0 +1,7 @@
{
"basepath" : "mapFormatIcons/",
"images" :
[
{ "group" : 1, "frame" : 0, "file" : "vcmi1.png"}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -1117,8 +1117,9 @@ void SelectionTab::parseMaps(const std::unordered_set<ResourceID> &files)
CMapInfo mapInfo;
mapInfo.mapInit(file.getName());
// ignore unsupported map versions (e.g. WoG maps without WoG
if (mapInfo.mapHeader->version <= CGI->modh->settings.data["textData"]["mapVersion"].Float())
// ignore unsupported map versions (e.g. WoG maps without WoG)
// but accept VCMI maps
if((mapInfo.mapHeader->version >= EMapFormat::VCMI) || (mapInfo.mapHeader->version <= CGI->modh->settings.data["textData"]["mapVersion"].Float()))
allItems.push_back(std::move(mapInfo));
}
catch(std::exception & e)
@ -1283,7 +1284,9 @@ SelectionTab::SelectionTab(CMenuScreen::EState Type, const std::function<void(CM
slider = new CSlider(Point(372, 86), tabType != CMenuScreen::saveGame ? 480 : 430, std::bind(&SelectionTab::sliderMove, this, _1), positions, curItems.size(), 0, false, CSlider::BLUE);
slider->addUsedEvents(WHEEL);
format = CDefHandler::giveDef("SCSELC.DEF");
formatIcons = new CAnimation("SCSELC.DEF");
formatIcons->load();
sortingBy = _format;
ascending = true;
@ -1312,7 +1315,7 @@ SelectionTab::SelectionTab(CMenuScreen::EState Type, const std::function<void(CM
SelectionTab::~SelectionTab()
{
delete format;
delete formatIcons;
}
void SelectionTab::sortBy( int criteria )
@ -1437,27 +1440,34 @@ void SelectionTab::printMaps(SDL_Surface *to)
}
printAtMiddleLoc(temp2, 70, 128 + line * 25, FONT_SMALL, itemColor, to);
int temp=-1;
int frame = -1, group = 0;
switch (currentItem->mapHeader->version)
{
case EMapFormat::ROE:
temp=0;
frame = 0;
break;
case EMapFormat::AB:
temp=1;
frame = 1;
break;
case EMapFormat::SOD:
temp=2;
frame = 2;
break;
case EMapFormat::WOG:
temp=3;
frame = 3;
break;
case EMapFormat::VCMI:
frame = 0;
group = 1;
break;
default:
// Unknown version. Be safe and ignore that map
logGlobal->warnStream() << "Warning: " << currentItem->fileURI << " has wrong version!";
continue;
}
blitAtLoc(format->ourImages[temp].bitmap, 88, 117 + line * 25, to);
IImage * icon = formatIcons->getImage(frame,group);
if(icon)
icon->draw(to, pos.x + 88, pos.y + 117 + line * 25);
//victory conditions
blitAtLoc(CGP->victory->ourImages[currentItem->mapHeader->victoryIconIndex].bitmap, 306, 117 + line * 25, to);

View File

@ -149,7 +149,7 @@ public:
class SelectionTab : public CIntObject
{
private:
CDefHandler *format; //map size
CAnimation * formatIcons;
void parseMaps(const std::unordered_set<ResourceID> &files);
void parseGames(const std::unordered_set<ResourceID> &files, bool multi);

View File

@ -19,9 +19,11 @@
#include "mapObjects/MapObjects.h"
#include "NetPacksBase.h"
#include "GameConstants.h"
#include "StringConstants.h"
#include "CRandomGenerator.h"
#include "mapObjects/CObjectClassesHandler.h"
#include "mapping/CMap.h"
// Note: list must match entries in ArtTraits.txt
#define ART_POS_LIST \
@ -201,28 +203,71 @@ std::vector<JsonNode> CArtHandler::loadLegacyData(size_t dataSize)
void CArtHandler::loadObject(std::string scope, std::string name, const JsonNode & data)
{
auto object = loadFromJson(data);
auto object = loadFromJson(data, normalizeIdentifier(scope, "core", name));
object->id = ArtifactID(artifacts.size());
object->iconIndex = object->id + 5;
artifacts.push_back(object);
VLC->modh->identifiers.registerObject(scope, "artifact", name, object->id);
VLC->modh->identifiers.requestIdentifier(scope, "object", "artifact", [=](si32 index)
{
JsonNode conf;
conf.setMeta(scope);
VLC->objtypeh->loadSubObject(object->identifier, conf, Obj::ARTIFACT, object->id.num);
if (!object->advMapDef.empty())
{
JsonNode templ;
templ.setMeta(scope);
templ["animation"].String() = object->advMapDef;
// add new template.
// Necessary for objects added via mods that don't have any templates in H3
VLC->objtypeh->getHandlerFor(Obj::ARTIFACT, object->id)->addTemplate(templ);
}
// object does not have any templates - this is not usable object (e.g. pseudo-art like lock)
if (VLC->objtypeh->getHandlerFor(Obj::ARTIFACT, object->id)->getTemplates().empty())
VLC->objtypeh->removeSubObject(Obj::ARTIFACT, object->id);
});
registerObject(scope, "artifact", name, object->id);
}
void CArtHandler::loadObject(std::string scope, std::string name, const JsonNode & data, size_t index)
{
auto object = loadFromJson(data);
auto object = loadFromJson(data, normalizeIdentifier(scope, "core", name));
object->id = ArtifactID(index);
object->iconIndex = object->id;
assert(artifacts[index] == nullptr); // ensure that this id was not loaded before
artifacts[index] = object;
VLC->modh->identifiers.registerObject(scope, "artifact", name, object->id);
VLC->modh->identifiers.requestIdentifier(scope, "object", "artifact", [=](si32 index)
{
JsonNode conf;
conf.setMeta(scope);
VLC->objtypeh->loadSubObject(object->identifier, conf, Obj::ARTIFACT, object->id.num);
if (!object->advMapDef.empty())
{
JsonNode templ;
templ.setMeta(scope);
templ["animation"].String() = object->advMapDef;
// add new template.
// Necessary for objects added via mods that don't have any templates in H3
VLC->objtypeh->getHandlerFor(Obj::ARTIFACT, object->id)->addTemplate(templ);
}
// object does not have any templates - this is not usable object (e.g. pseudo-art like lock)
if (VLC->objtypeh->getHandlerFor(Obj::ARTIFACT, object->id)->getTemplates().empty())
VLC->objtypeh->removeSubObject(Obj::ARTIFACT, object->id);
});
registerObject(scope, "artifact", name, object->id);
}
CArtifact * CArtHandler::loadFromJson(const JsonNode & node)
CArtifact * CArtHandler::loadFromJson(const JsonNode & node, const std::string & identifier)
{
CArtifact * art;
@ -234,7 +279,7 @@ CArtifact * CArtHandler::loadFromJson(const JsonNode & node)
loadGrowingArt(growing, node);
art = growing;
}
art->identifier = identifier;
const JsonNode & text = node["text"];
art->name = text["name"].String();
art->description = text["description"].String();
@ -629,7 +674,7 @@ std::vector<bool> CArtHandler::getDefaultAllowed() const
std::vector<bool> allowedArtifacts;
allowedArtifacts.resize(127, true);
allowedArtifacts.resize(141, false);
allowedArtifacts.resize(GameConstants::ARTIFACTS_QUANTITY, true);
allowedArtifacts.resize(artifacts.size(), true);
return allowedArtifacts;
}
@ -695,24 +740,20 @@ void CArtHandler::afterLoadFinalization()
}
}
CBonusSystemNode::treeHasChanged();
}
for (CArtifact * art : artifacts)
{
VLC->objtypeh->loadSubObject(art->Name(), JsonNode(), Obj::ARTIFACT, art->id.num);
si32 CArtHandler::decodeArfifact(const std::string& identifier)
{
auto rawId = VLC->modh->identifiers.getIdentifier("core", "artifact", identifier);
if(rawId)
return rawId.get();
else
return -1;
}
if (!art->advMapDef.empty())
{
JsonNode templ;
templ["animation"].String() = art->advMapDef;
// add new template.
// Necessary for objects added via mods that don't have any templates in H3
VLC->objtypeh->getHandlerFor(Obj::ARTIFACT, art->id)->addTemplate(templ);
}
// object does not have any templates - this is not usable object (e.g. pseudo-art like lock)
if (VLC->objtypeh->getHandlerFor(Obj::ARTIFACT, art->id)->getTemplates().empty())
VLC->objtypeh->removeSubObject(Obj::ARTIFACT, art->id);
}
std::string CArtHandler::encodeArtifact(const si32 index)
{
return VLC->arth->artifacts[index]->identifier;
}
CArtifactInstance::CArtifactInstance()
@ -937,6 +978,40 @@ CArtifactInstance * CArtifactInstance::createNewArtifactInstance(int aid)
return createNewArtifactInstance(VLC->arth->artifacts[aid]);
}
CArtifactInstance * CArtifactInstance::createArtifact(CMap * map, int aid, int spellID)
{
CArtifactInstance * a = nullptr;
if(aid >= 0)
{
if(spellID < 0)
{
a = CArtifactInstance::createNewArtifactInstance(aid);
}
else
{
a = CArtifactInstance::createScroll(SpellID(spellID).toSpell());
}
}
else //FIXME: create combined artifact instance for random combined artifacts, just in case
{
a = new CArtifactInstance(); //random, empty
}
map->addNewArtifactInstance(a);
//TODO make it nicer
if(a->artType && (!!a->artType->constituents))
{
CCombinedArtifactInstance * comb = dynamic_cast<CCombinedArtifactInstance *>(a);
for(CCombinedArtifactInstance::ConstituentInfo & ci : comb->constituentsInfo)
{
map->addNewArtifactInstance(ci.art);
}
}
return a;
}
void CArtifactInstance::deserializationFix()
{
setType(artType);
@ -1313,3 +1388,13 @@ void CArtifactSet::artDeserializationFix(CBonusSystemNode *node)
if(elem.second.artifact && !elem.second.locked)
node->attachTo(elem.second.artifact);
}
void CArtifactSet::writeJson(JsonNode& json) const
{
}
void CArtifactSet::readJson(const JsonNode& json)
{
}

View File

@ -25,6 +25,7 @@ struct ArtifactLocation;
class CArtifactSet;
class CArtifactInstance;
class CRandomGenerator;
class CMap;
#define ART_BEARER_LIST \
ART_BEARER(HERO)\
@ -49,6 +50,7 @@ protected:
public:
enum EartClass {ART_SPECIAL=1, ART_TREASURE=2, ART_MINOR=4, ART_MAJOR=8, ART_RELIC=16}; //artifact classes
std::string identifier;
std::string image;
std::string large; // big image for cutom artifacts, used in drag & drop
std::string advMapDef; //used for adventure map object
@ -79,6 +81,10 @@ public:
h & static_cast<CBonusSystemNode&>(*this);
h & name & description & eventText & image & large & advMapDef & iconIndex &
price & possibleSlots & constituents & constituentOf & aClass & id;
if(version>=759)
{
h & identifier;
}
}
CArtifact();
@ -147,6 +153,15 @@ public:
static CArtifactInstance *createScroll(SpellID sid);
static CArtifactInstance *createNewArtifactInstance(CArtifact *Art);
static CArtifactInstance *createNewArtifactInstance(int aid);
/**
* Creates an artifact instance.
*
* @param aid the id of the artifact
* @param spellID optional. the id of a spell if a spell scroll object should be created
* @return the created artifact instance
*/
static CArtifactInstance * createArtifact(CMap * map, int aid, int spellID = -1);
};
class DLL_LINKAGE CCombinedArtifactInstance : public CArtifactInstance
@ -240,6 +255,12 @@ public:
std::vector<bool> getDefaultAllowed() const override;
///json serialization helper
static si32 decodeArfifact(const std::string & identifier);
///json serialization helper
static std::string encodeArtifact(const si32 index);
template <typename Handler> void serialize(Handler &h, const int version)
{
h & artifacts & allowedArtifacts & treasures & minors & majors & relics
@ -248,7 +269,7 @@ public:
}
private:
CArtifact * loadFromJson(const JsonNode & node);
CArtifact * loadFromJson(const JsonNode & node, const std::string & identifier);
void addSlot(CArtifact * art, const std::string & slotID);
void loadSlots(CArtifact * art, const JsonNode & node);
@ -313,6 +334,10 @@ public:
void artDeserializationFix(CBonusSystemNode *node);
protected:
void writeJson(JsonNode & json) const;
void readJson(const JsonNode & json);
protected:
std::pair<const CCombinedArtifactInstance *, const CArtifactInstance *> searchForConstituent(int aid) const;
};

View File

@ -167,12 +167,12 @@ static void AddAbility(CCreature *cre, const JsonVector &ability_vec)
}
nsf->type = it->second;
JsonUtils::parseTypedBonusShort(ability_vec,nsf);
nsf->source = Bonus::CREATURE_ABILITY;
nsf->sid = cre->idNumber;
cre->addNewBonus(nsf);
}
@ -188,6 +188,16 @@ CCreatureHandler::CCreatureHandler()
loadCommanders();
}
const CCreature * CCreatureHandler::getCreature(const std::string & scope, const std::string & identifier) const
{
boost::optional<si32> index = VLC->modh->identifiers.getIdentifier(scope, "creature", identifier);
if(!index)
throw std::runtime_error("Creature not found "+identifier);
return creatures[*index];
}
void CCreatureHandler::loadCommanders()
{
JsonNode data(ResourceID("config/commanders.json"));
@ -358,23 +368,41 @@ std::vector<JsonNode> CCreatureHandler::loadLegacyData(size_t dataSize)
void CCreatureHandler::loadObject(std::string scope, std::string name, const JsonNode & data)
{
auto object = loadFromJson(data);
auto object = loadFromJson(data, normalizeIdentifier(scope, "core", name));
object->setId(CreatureID(creatures.size()));
object->iconIndex = object->idNumber + 2;
creatures.push_back(object);
VLC->modh->identifiers.registerObject(scope, "creature", name, object->idNumber);
VLC->modh->identifiers.requestIdentifier(scope, "object", "monster", [=](si32 index)
{
JsonNode conf;
conf.setMeta(scope);
VLC->objtypeh->loadSubObject(object->identifier, conf, Obj::MONSTER, object->idNumber.num);
if (!object->advMapDef.empty())
{
JsonNode templ;
templ["animation"].String() = object->advMapDef;
VLC->objtypeh->getHandlerFor(Obj::MONSTER, object->idNumber.num)->addTemplate(templ);
}
// object does not have any templates - this is not usable object (e.g. pseudo-creature like Arrow Tower)
if (VLC->objtypeh->getHandlerFor(Obj::MONSTER, object->idNumber.num)->getTemplates().empty())
VLC->objtypeh->removeSubObject(Obj::MONSTER, object->idNumber.num);
});
registerObject(scope, "creature", name, object->idNumber);
for(auto node : data["extraNames"].Vector())
{
VLC->modh->identifiers.registerObject(scope, "creature", node.String(), object->idNumber);
registerObject(scope, "creature", node.String(), object->idNumber);
}
}
void CCreatureHandler::loadObject(std::string scope, std::string name, const JsonNode & data, size_t index)
{
auto object = loadFromJson(data);
auto object = loadFromJson(data, normalizeIdentifier(scope, "core", name));
object->setId(CreatureID(index));
object->iconIndex = object->idNumber + 2;
@ -386,10 +414,28 @@ void CCreatureHandler::loadObject(std::string scope, std::string name, const Jso
assert(creatures[index] == nullptr); // ensure that this id was not loaded before
creatures[index] = object;
VLC->modh->identifiers.registerObject(scope, "creature", name, object->idNumber);
VLC->modh->identifiers.requestIdentifier(scope, "object", "monster", [=](si32 index)
{
JsonNode conf;
conf.setMeta(scope);
VLC->objtypeh->loadSubObject(object->identifier, conf, Obj::MONSTER, object->idNumber.num);
if (!object->advMapDef.empty())
{
JsonNode templ;
templ["animation"].String() = object->advMapDef;
VLC->objtypeh->getHandlerFor(Obj::MONSTER, object->idNumber.num)->addTemplate(templ);
}
// object does not have any templates - this is not usable object (e.g. pseudo-creature like Arrow Tower)
if (VLC->objtypeh->getHandlerFor(Obj::MONSTER, object->idNumber.num)->getTemplates().empty())
VLC->objtypeh->removeSubObject(Obj::MONSTER, object->idNumber.num);
});
registerObject(scope, "creature", name, object->idNumber);
for(auto & node : data["extraNames"].Vector())
{
VLC->modh->identifiers.registerObject(scope, "creature", node.String(), object->idNumber);
registerObject(scope, "creature", node.String(), object->idNumber);
}
}
@ -568,11 +614,12 @@ void CCreatureHandler::loadUnitAnimInfo(JsonNode & graphics, CLegacyConfigParser
graphics.Struct().erase("missile");
}
CCreature * CCreatureHandler::loadFromJson(const JsonNode & node)
CCreature * CCreatureHandler::loadFromJson(const JsonNode & node, const std::string & identifier)
{
auto cre = new CCreature();
const JsonNode & name = node["name"];
cre->identifier = identifier;
cre->nameSing = name["singular"].String();
cre->namePl = name["plural"].String();
@ -1130,20 +1177,7 @@ void CCreatureHandler::buildBonusTreeForTiers()
void CCreatureHandler::afterLoadFinalization()
{
for (CCreature * crea : creatures)
{
VLC->objtypeh->loadSubObject(crea->nameSing, JsonNode(), Obj::MONSTER, crea->idNumber.num);
if (!crea->advMapDef.empty())
{
JsonNode templ;
templ["animation"].String() = crea->advMapDef;
VLC->objtypeh->getHandlerFor(Obj::MONSTER, crea->idNumber)->addTemplate(templ);
}
// object does not have any templates - this is not usable object (e.g. pseudo-creature like Arrow Tower)
if (VLC->objtypeh->getHandlerFor(Obj::MONSTER, crea->idNumber.num)->getTemplates().empty())
VLC->objtypeh->removeSubObject(Obj::MONSTER, crea->idNumber.num);
}
}
void CCreatureHandler::deserializationFix()

View File

@ -26,6 +26,8 @@ class CCreature;
class DLL_LINKAGE CCreature : public CBonusSystemNode
{
public:
std::string identifier;
std::string nameRef; // reference name, stringID
std::string nameSing;// singular name, e.g. Centaur
std::string namePl; // plural name, e.g. Centaurs
@ -136,6 +138,10 @@ public:
h & idNumber & faction & sounds & animation;
h & doubleWide & special;
if(version>=759)
{
h & identifier;
}
}
CCreature();
@ -148,7 +154,7 @@ private:
CBonusSystemNode creaturesOfLevel[GameConstants::CREATURES_PER_TOWN + 1];//index 0 is used for creatures of unknown tier or outside <1-7> range
/// load one creature from json config
CCreature * loadFromJson(const JsonNode & node);
CCreature * loadFromJson(const JsonNode & node, const std::string & identifier);
void loadJsonAnimation(CCreature * creature, const JsonNode & graphics);
void loadStackExperience(CCreature * creature, const JsonNode &input);
@ -186,6 +192,8 @@ public:
std::vector< std::vector <ui8> > skillLevels; //how much of a bonus will be given to commander with every level. SPELL_POWER also gives CASTS and RESISTANCE
std::vector <std::pair <Bonus*, std::pair <ui8, ui8> > > skillRequirements; // first - Bonus, second - which two skills are needed to use it
const CCreature * getCreature(const std::string & scope, const std::string & identifier) const;
void deserializationFix();
CreatureID pickRandomMonster(CRandomGenerator & rand, int tier = -1) const; //tier <1 - CREATURES_PER_TOWN> or -1 for any
void addBonusForTier(int tier, Bonus *b); //tier must be <1-7>

View File

@ -11,6 +11,7 @@
#include "spells/CSpellHandler.h"
#include "CHeroHandler.h"
#include "IBonusTypeHandler.h"
#include "serializer/JsonSerializeFormat.h"
/*
* CCreatureSet.cpp, part of VCMI engine
@ -479,6 +480,38 @@ CCreatureSet & CCreatureSet::operator=(const CCreatureSet&cs)
void CCreatureSet::armyChanged()
{
}
void CCreatureSet::serializeJson(JsonSerializeFormat & handler, const std::string & fieldName)
{
if(handler.saving && stacks.empty())
return;
JsonNode & json = handler.getCurrent()[fieldName];
if(handler.saving)
{
for(const auto & p : stacks)
{
JsonNode stack_node;
p.second->writeJson(stack_node);
json.Vector()[p.first.getNum()] = stack_node;
}
}
else
{
for(size_t idx = 0; idx < json.Vector().size(); idx++)
{
if(json.Vector()[idx]["amount"].Float() > 0)
{
CStackInstance * new_stack = new CStackInstance();
new_stack->readJson(json.Vector()[idx]);
putStack(SlotID(idx), new_stack);
}
}
}
}
CStackInstance::CStackInstance()
@ -605,7 +638,7 @@ std::string CStackInstance::bonusToString(const Bonus *bonus, bool description)
{
return VLC->getBth()->bonusToString(bonus, this, description);
}
}
std::string CStackInstance::bonusToGraphics(const Bonus *bonus) const
@ -700,6 +733,25 @@ ArtBearer::ArtBearer CStackInstance::bearerType() const
return ArtBearer::CREATURE;
}
void CStackInstance::writeJson(JsonNode& json) const
{
if(idRand > -1)
{
json["level"].Float() = (int)idRand / 2;
json["upgraded"].Bool() = (idRand % 2) > 0;
}
CStackBasicDescriptor::writeJson(json);
}
void CStackInstance::readJson(const JsonNode& json)
{
if(json["type"].String() == "")
{
idRand = json["level"].Float() * 2 + (int)json["upgraded"].Bool();
}
CStackBasicDescriptor::readJson(json);
}
CCommanderInstance::CCommanderInstance()
{
init();
@ -792,6 +844,22 @@ CStackBasicDescriptor::CStackBasicDescriptor(const CCreature *c, TQuantity Count
{
}
void CStackBasicDescriptor::writeJson(JsonNode& json) const
{
json.setType(JsonNode::DATA_STRUCT);
if(type)
json["type"].String() = type->identifier;
json["amount"].Float() = count;
}
void CStackBasicDescriptor::readJson(const JsonNode& json)
{
auto typeName = json["type"].String();
if(typeName != "")
type = VLC->creh->getCreature("core", json["type"].String());
count = json["amount"].Float();
}
DLL_LINKAGE std::ostream & operator<<(std::ostream & str, const CStackInstance & sth)
{
if(!sth.valid(true))

View File

@ -14,11 +14,12 @@
* Full text of license available in license.txt file, in main folder
*
*/
class JsonNode;
class CCreature;
class CGHeroInstance;
class CArmedInstance;
class CCreatureArtifactSet;
class JsonSerializeFormat;
class DLL_LINKAGE CStackBasicDescriptor
{
@ -34,6 +35,10 @@ public:
{
h & type & count;
}
void writeJson(JsonNode & json) const;
void readJson(const JsonNode & json);
};
class DLL_LINKAGE CStackInstance : public CBonusSystemNode, public CStackBasicDescriptor, public CArtifactSet
@ -59,6 +64,10 @@ public:
BONUS_TREE_DESERIALIZATION_FIX
}
void writeJson(JsonNode & json) const;
void readJson(const JsonNode & json);
//overrides CBonusSystemNode
std::string bonusToString(const Bonus *bonus, bool description) const override; // how would bonus description look for this particular type of node
std::string bonusToGraphics(const Bonus *bonus) const; //file name of graphics from StackSkills , in future possibly others
@ -110,7 +119,7 @@ public:
bool gainsLevel() const; //true if commander has lower level than should upon his experience
ui64 getPower() const override {return 0;};
int getExpRank() const override;
int getLevel() const override;
int getLevel() const override;
ArtBearer::ArtBearer bearerType() const override; //from CArtifactSet
template <typename Handler> void serialize(Handler &h, const int version)
@ -211,6 +220,9 @@ public:
{
h & stacks & formation;
}
void serializeJson(JsonSerializeFormat & handler, const std::string & fieldName);
operator bool() const
{
return !stacks.empty();

View File

@ -736,7 +736,7 @@ BattleInfo * CGameState::setupBattle(int3 tile, const CArmedInstance *armies[2],
{
const TerrainTile &t = map->getTile(tile);
ETerrainType terrain = t.terType;
if(t.isCoastal() && !t.isWater())
if(map->isCoastalTile(tile)) //coastal tile is always ground
terrain = ETerrainType::SAND;
BFieldType terType = battleGetBattlefieldType(tile);
@ -1852,7 +1852,7 @@ void CGameState::initMapObjects()
{
if(obj)
{
//logGlobal->traceStream() << boost::format ("Calling Init for object %d, %d") % obj->ID % obj->subID;
logGlobal->traceStream() << boost::format ("Calling Init for object %d, %s, %s") % obj->id.getNum() % obj->typeName % obj->subTypeName;
obj->initObj();
}
}
@ -1956,7 +1956,7 @@ BFieldType CGameState::battleGetBattlefieldType(int3 tile)
}
}
if(!t.isWater() && t.isCoastal())
if(map->isCoastalTile(tile)) //coastal tile is always ground
return BFieldType::SAND_SHORE;
switch(t.terType)

View File

@ -96,12 +96,12 @@ bool CObstacleInfo::isAppropriate(ETerrainType terrainType, int specialBattlefie
return vstd::contains(allowedTerrains, terrainType);
}
CHeroClass *CHeroClassHandler::loadFromJson(const JsonNode & node)
CHeroClass * CHeroClassHandler::loadFromJson(const JsonNode & node, const std::string & identifier)
{
std::string affinityStr[2] = { "might", "magic" };
auto heroClass = new CHeroClass();
heroClass->identifier = identifier;
heroClass->imageBattleFemale = node["animation"]["battle"]["female"].String();
heroClass->imageBattleMale = node["animation"]["battle"]["male"].String();
//MODS COMPATIBILITY FOR 0.96
@ -192,7 +192,7 @@ std::vector<JsonNode> CHeroClassHandler::loadLegacyData(size_t dataSize)
void CHeroClassHandler::loadObject(std::string scope, std::string name, const JsonNode & data)
{
auto object = loadFromJson(data);
auto object = loadFromJson(data, normalizeIdentifier(scope, "core", name));
object->id = heroClasses.size();
heroClasses.push_back(object);
@ -210,7 +210,7 @@ void CHeroClassHandler::loadObject(std::string scope, std::string name, const Js
void CHeroClassHandler::loadObject(std::string scope, std::string name, const JsonNode & data, size_t index)
{
auto object = loadFromJson(data);
auto object = loadFromJson(data, normalizeIdentifier(scope, "core", name));
object->id = index;
assert(heroClasses[index] == nullptr); // ensure that this id was not loaded before
@ -292,10 +292,10 @@ CHeroHandler::CHeroHandler()
loadExperience();
}
CHero * CHeroHandler::loadFromJson(const JsonNode & node)
CHero * CHeroHandler::loadFromJson(const JsonNode & node, const std::string & identifier)
{
auto hero = new CHero;
hero->identifier = identifier;
hero->sex = node["female"].Bool();
hero->special = node["special"].Bool();
@ -540,7 +540,7 @@ std::vector<JsonNode> CHeroHandler::loadLegacyData(size_t dataSize)
void CHeroHandler::loadObject(std::string scope, std::string name, const JsonNode & data)
{
auto object = loadFromJson(data);
auto object = loadFromJson(data, normalizeIdentifier(scope, "core", name));
object->ID = HeroTypeID(heroes.size());
object->imageIndex = heroes.size() + 30; // 2 special frames + some extra portraits
@ -551,7 +551,7 @@ void CHeroHandler::loadObject(std::string scope, std::string name, const JsonNod
void CHeroHandler::loadObject(std::string scope, std::string name, const JsonNode & data, size_t index)
{
auto object = loadFromJson(data);
auto object = loadFromJson(data, normalizeIdentifier(scope, "core", name));
object->ID = HeroTypeID(index);
object->imageIndex = index;
@ -611,3 +611,31 @@ std::vector<bool> CHeroHandler::getDefaultAllowedAbilities() const
allowedAbilities.resize(GameConstants::SKILL_QUANTITY, true);
return allowedAbilities;
}
si32 CHeroHandler::decodeHero(const std::string & identifier)
{
auto rawId = VLC->modh->identifiers.getIdentifier("core", "hero", identifier);
if(rawId)
return rawId.get();
else
return -1;
}
std::string CHeroHandler::encodeHero(const si32 index)
{
return VLC->heroh->heroes.at(index)->identifier;
}
si32 CHeroHandler::decodeSkill(const std::string & identifier)
{
auto rawId = VLC->modh->identifiers.getIdentifier("core", "skill", identifier);
if(rawId)
return rawId.get();
else
return -1;
}
std::string CHeroHandler::encodeSkill(const si32 index)
{
return NSecondarySkill::names[index];
}

View File

@ -59,7 +59,7 @@ public:
h & minAmount & maxAmount & creature;
}
};
std::string identifier;
HeroTypeID ID;
si32 imageIndex;
@ -92,6 +92,10 @@ public:
h & ID & imageIndex & initialArmy & heroClass & secSkillsInit & spec & specialty & spells & haveSpellBook & sex & special;
h & name & biography & specName & specDescr & specTooltip;
h & iconSpecSmall & iconSpecLarge & portraitSmall & portraitLarge;
if(version>=759)
{
h & identifier;
}
}
};
@ -169,7 +173,7 @@ struct DLL_LINKAGE CObstacleInfo
class DLL_LINKAGE CHeroClassHandler : public IHandlerBase
{
CHeroClass *loadFromJson(const JsonNode & node);
CHeroClass *loadFromJson(const JsonNode & node, const std::string & identifier);
public:
std::vector< ConstTransitivePtr<CHeroClass> > heroClasses;
@ -207,7 +211,7 @@ class DLL_LINKAGE CHeroHandler : public IHandlerBase
void loadObstacles();
/// Load single hero from json
CHero * loadFromJson(const JsonNode & node);
CHero * loadFromJson(const JsonNode & node, const std::string & identifier);
public:
CHeroClassHandler classes;
@ -253,6 +257,18 @@ public:
*/
std::vector<bool> getDefaultAllowedAbilities() const;
///json serialization helper
static si32 decodeHero(const std::string & identifier);
///json serialization helper
static std::string encodeHero(const si32 index);
///json serialization helper
static si32 decodeSkill(const std::string & identifier);
///json serialization helper
static std::string encodeSkill(const si32 index);
template <typename Handler> void serialize(Handler &h, const int version)
{
h & classes & heroes & expPerLevel & ballistics & terrCosts;

View File

@ -11,14 +11,17 @@ set(lib_SRCS
filesystem/CCompressedStream.cpp
filesystem/CFilesystemLoader.cpp
filesystem/CArchiveLoader.cpp
filesystem/CMemoryBuffer.cpp
filesystem/CMemoryStream.cpp
filesystem/CBinaryReader.cpp
filesystem/CFileInputStream.cpp
filesystem/CZipLoader.cpp
filesystem/CZipSaver.cpp
filesystem/FileInfo.cpp
filesystem/Filesystem.cpp
filesystem/FileStream.cpp
filesystem/ResourceID.cpp
filesystem/MinizipExtensions.cpp
mapObjects/CArmedInstance.cpp
mapObjects/CBank.cpp
@ -101,6 +104,10 @@ set(lib_SRCS
Connection.cpp
NetPacksLib.cpp
serializer/JsonSerializer.cpp
serializer/JsonDeserializer.cpp
serializer/JsonSerializeFormat.cpp
registerTypes/RegisterTypes.cpp
registerTypes/TypesClientPacks1.cpp
registerTypes/TypesClientPacks2.cpp

View File

@ -205,10 +205,21 @@ std::vector<CIdentifierStorage::ObjectData> CIdentifierStorage::getPossibleIdent
else
{
//...unless destination mod was specified explicitly
auto myDeps = VLC->modh->getModData(request.localScope).dependencies;
if (request.remoteScope == "core" || // allow only available to all core mod
myDeps.count(request.remoteScope)) // or dependencies
//note: getModData does not work for "core" by design
//for map format support core mod has access to any mod
//TODO: better solution for access from map?
if(request.localScope == "core" || request.localScope == "")
{
allowedScopes.insert(request.remoteScope);
}
else
{
// allow only available to all core mod or dependencies
auto myDeps = VLC->modh->getModData(request.localScope).dependencies;
if (request.remoteScope == "core" || myDeps.count(request.remoteScope))
allowedScopes.insert(request.remoteScope);
}
}
std::string fullID = request.type + '.' + request.name;
@ -844,9 +855,16 @@ void CModHandler::loadModFilesystems()
CModInfo & CModHandler::getModData(TModID modId)
{
CModInfo & mod = allMods.at(modId);
assert(vstd::contains(activeMods, modId)); // not really necessary but won't hurt
return mod;
auto it = allMods.find(modId);
if(it == allMods.end())
{
throw std::runtime_error("Mod not found '" + modId+"'");
}
else
{
return it->second;
}
}
void CModHandler::initializeConfig()
@ -905,3 +923,16 @@ void CModHandler::afterLoad()
FileStream file(*CResourceHandler::get()->getResourceName(ResourceID("config/modSettings.json")), std::ofstream::out | std::ofstream::trunc);
file << modSettings;
}
std::string CModHandler::normalizeIdentifier(const std::string & scope, const std::string & remoteScope, const std::string & identifier) const
{
auto p = splitString(identifier, ':');
if(p.first.empty())
p.first = scope;
if(p.first == remoteScope)
p.first.clear();
return p.first.empty() ? p.second : p.first +":"+p.second;
}

View File

@ -292,6 +292,8 @@ public:
CModHandler();
std::string normalizeIdentifier(const std::string & scope, const std::string & remoteScope, const std::string & identifier) const;
template <typename Handler> void serialize(Handler &h, const int version)
{
h & allMods & activeMods & settings & modules & identifiers;

View File

@ -133,6 +133,7 @@ void CPathfinder::calculatePaths()
if(!hlp->isLayerAvailable(i))
continue;
/// Check transition without tile accessability rules
if(cp->layer != i && !isLayerTransitionPossible(i))
continue;
@ -143,6 +144,7 @@ void CPathfinder::calculatePaths()
if(dp->accessible == CGPathNode::NOT_SET)
continue;
/// Check transition using tile accessability rules
if(cp->layer != i && !isLayerTransitionPossible())
continue;
@ -302,16 +304,23 @@ bool CPathfinder::isLayerTransitionPossible(const ELayer destLayer) const
switch(cp->layer)
{
case ELayer::LAND:
if(destLayer != ELayer::AIR)
return true;
if(!options.lightweightFlyingMode || isSourceInitialPosition())
if(destLayer == ELayer::AIR)
{
if(!options.lightweightFlyingMode || isSourceInitialPosition())
return true;
}
else if(destLayer == ELayer::SAIL)
{
if(dt->isWater())
return true;
}
else
return true;
break;
case ELayer::SAIL:
if(destLayer == ELayer::LAND && dt->isCoastal())
if(destLayer == ELayer::LAND && !dt->isWater())
return true;
break;

View File

@ -526,7 +526,7 @@ void CTownHandler::loadClientData(CTown &town, const JsonNode & source)
info.tavernVideo = source["tavernVideo"].String();
else
info.tavernVideo = "TAVERN.BIK";
//end of legacy assignment
//end of legacy assignment
loadTownHall(town, source["hallSlots"]);
loadStructures(town, source["structures"]);
@ -646,7 +646,7 @@ void CTownHandler::loadPuzzle(CFaction &faction, const JsonNode &source)
assert(faction.puzzleMap.size() == GameConstants::PUZZLE_MAP_PIECES);
}
CFaction * CTownHandler::loadFromJson(const JsonNode &source, std::string identifier)
CFaction * CTownHandler::loadFromJson(const JsonNode &source, const std::string & identifier)
{
auto faction = new CFaction();
@ -682,7 +682,7 @@ CFaction * CTownHandler::loadFromJson(const JsonNode &source, std::string identi
void CTownHandler::loadObject(std::string scope, std::string name, const JsonNode & data)
{
auto object = loadFromJson(data, name);
auto object = loadFromJson(data, normalizeIdentifier(scope, "core", name));
object->index = factions.size();
factions.push_back(object);
@ -699,7 +699,7 @@ void CTownHandler::loadObject(std::string scope, std::string name, const JsonNod
{
// register town once objects are loaded
JsonNode config = data["town"]["mapObject"];
config["faction"].String() = object->identifier;
config["faction"].String() = name;
config["faction"].meta = scope;
if (config.meta.empty())// MODS COMPATIBILITY FOR 0.96
config.meta = scope;
@ -722,7 +722,7 @@ void CTownHandler::loadObject(std::string scope, std::string name, const JsonNod
void CTownHandler::loadObject(std::string scope, std::string name, const JsonNode & data, size_t index)
{
auto object = loadFromJson(data, name);
auto object = loadFromJson(data, normalizeIdentifier(scope, "core", name));
object->index = index;
assert(factions[index] == nullptr); // ensure that this id was not loaded before
factions[index] = object;
@ -739,7 +739,7 @@ void CTownHandler::loadObject(std::string scope, std::string name, const JsonNod
{
// register town once objects are loaded
JsonNode config = data["town"]["mapObject"];
config["faction"].String() = object->identifier;
config["faction"].String() = name;
config["faction"].meta = scope;
VLC->objtypeh->loadSubObject(object->identifier, config, index, object->index);
});
@ -795,3 +795,17 @@ std::set<TFaction> CTownHandler::getAllowedFactions(bool withTown /*=true*/) con
return allowedFactions;
}
si32 CTownHandler::decodeFaction(const std::string & identifier)
{
auto rawId = VLC->modh->identifiers.getIdentifier("core", "faction", identifier);
if(rawId)
return rawId.get();
else
return -1;
}
std::string CTownHandler::encodeFaction(const si32 index)
{
return VLC->townh->factions[index]->identifier;
}

View File

@ -122,8 +122,8 @@ public:
std::string creatureBg120;
std::string creatureBg130;
std::vector<SPuzzleInfo> puzzleMap;
@ -142,7 +142,7 @@ public:
static std::vector<BattleHex> defaultMoatHexes();
CFaction * faction;
std::vector<std::string> names; //names of the town instances
/// level -> list of creatures on this tier
@ -264,7 +264,7 @@ class DLL_LINKAGE CTownHandler : public IHandlerBase
void loadPuzzle(CFaction & faction, const JsonNode & source);
CFaction * loadFromJson(const JsonNode & data, std::string identifier);
CFaction * loadFromJson(const JsonNode & data, const std::string & identifier);
public:
std::vector<ConstTransitivePtr<CFaction> > factions;
@ -282,6 +282,12 @@ public:
std::vector<bool> getDefaultAllowed() const override;
std::set<TFaction> getAllowedFactions(bool withTown = true) const;
//json serialization helper
static si32 decodeFaction(const std::string & identifier);
//json serialization helper
static std::string encodeFaction(const si32 index);
template <typename Handler> void serialize(Handler &h, const int version)
{
h & factions;

View File

@ -27,7 +27,7 @@
#include "mapping/CCampaignHandler.h" //for CCampaignState
#include "rmg/CMapGenerator.h" // for CMapGenOptions
const ui32 version = 758;
const ui32 version = 759;
const ui32 minSupportedVersion = 753;
class CISer;

View File

@ -17,3 +17,8 @@ void IHandlerBase::registerObject(std::string scope, std::string type_name, std:
{
return VLC->modh->identifiers.registerObject(scope, type_name, name, index);
}
std::string IHandlerBase::normalizeIdentifier(const std::string& scope, const std::string& remoteScope, const std::string& identifier) const
{
return VLC->modh->normalizeIdentifier(scope, remoteScope, identifier);
}

View File

@ -25,6 +25,7 @@ class DLL_LINKAGE IHandlerBase
protected:
/// Calls modhandler. Mostly needed to avoid large number of includes in headers
void registerObject(std::string scope, std::string type_name, std::string name, si32 index);
std::string normalizeIdentifier(const std::string & scope, const std::string & remoteScope, const std::string & identifier) const;
public:
/// loads all original game data in vector of json nodes
@ -65,7 +66,7 @@ public:
void loadObject(std::string scope, std::string name, const JsonNode & data) override
{
auto type_name = getTypeName();
auto object = loadFromJson(data);
auto object = loadFromJson(data, normalizeIdentifier(scope, "core", name));
object->id = _ObjectID(objects.size());
objects.push_back(object);
@ -75,7 +76,7 @@ public:
void loadObject(std::string scope, std::string name, const JsonNode & data, size_t index) override
{
auto type_name = getTypeName();
auto object = loadFromJson(data);
auto object = loadFromJson(data, normalizeIdentifier(scope, "core", name));
object->id = _ObjectID(index);
@ -99,7 +100,7 @@ public:
return objects[raw_id];
}
protected:
virtual _Object * loadFromJson(const JsonNode & json) = 0;
virtual _Object * loadFromJson(const JsonNode & json, const std::string & identifier) = 0;
virtual const std::string getTypeName() const = 0;
public: //todo: make private
std::vector<ConstTransitivePtr<_Object>> objects;

View File

@ -61,19 +61,32 @@ void JsonWriter::writeEntry(JsonVector::const_iterator entry)
void JsonWriter::writeString(const std::string &string)
{
static const std::string escaped = "\"\\\b\f\n\r\t";
static const std::array<char, 7> escaped_code = {'\"', '\\', 'b', 'f', 'n', 'r', 't'};
out <<'\"';
size_t pos=0, start=0;
for (; pos<string.size(); pos++)
{
size_t escapedChar = escaped.find(string[pos]);
if (escapedChar != std::string::npos)
//we need to check if special character was been already escaped
if((string[pos] == '\\')
&& (pos+1 < string.size())
&& (std::find(escaped_code.begin(), escaped_code.end(), string[pos+1]) != escaped_code.end()) )
{
out.write(string.data()+start, pos - start);
out << '\\' << escaped[escapedChar];
start = pos;
pos++; //write unchanged, next simbol also checked
}
else
{
size_t escapedPos = escaped.find(string[pos]);
if (escapedPos != std::string::npos)
{
out.write(string.data()+start, pos - start);
out << '\\' << escaped_code[escapedPos];
start = pos+1;
}
}
}
out.write(string.data()+start, pos - start);
out <<'\"';

View File

@ -262,7 +262,7 @@ namespace LogicalExpressionDetail
}
};
/// Prints expression in human-readable format
/// Serializes expression in JSON format. Part of map format.
template <typename ContainedClass>
class Writer : public boost::static_visitor<JsonNode>
{

View File

@ -386,6 +386,7 @@ DLL_LINKAGE void RemoveObject::applyGs( CGameState *gs )
//If hero on Boat is removed, the Boat disappears
if(h->boat)
{
gs->map->instanceNames.erase(h->boat->instanceName);
gs->map->objects[h->boat->id.getNum()].dellNull();
h->boat = nullptr;
}
@ -430,7 +431,7 @@ DLL_LINKAGE void RemoveObject::applyGs( CGameState *gs )
};
event.trigger = event.trigger.morph(patcher);
}
gs->map->instanceNames.erase(obj->instanceName);
gs->map->objects[id.getNum()].dellNull();
gs->map->calculateGuardingGreaturePositions();
}

View File

@ -21,7 +21,7 @@ namespace GameConstants
const std::string TERRAIN_NAMES [TERRAIN_TYPES] = {
"dirt", "sand", "grass", "snow", "swamp", "rough", "subterra", "lava", "water", "rock"
};
const std::string RESOURCE_NAMES [RESOURCE_QUANTITY] = {
"wood", "mercury", "ore", "sulfur", "crystal", "gems", "gold", "mithril"
};
@ -84,3 +84,17 @@ namespace ETownType
"stronghold", "fortress", "conflux"
};
}
namespace NArtifactPosition
{
const std::string names [19] =
{
"head", "shoulders", "neck", "rightHand", "leftHand", "torso", //5
"rightRing", "leftRing", "feet", //8
"misc1", "misc2", "misc3", "misc4", //12
"mach1", "mach2", "mach3", "mach4", //16
"spellbook", "misc5" //18
};
const std::string backpack = "backpack";
}

View File

@ -152,6 +152,7 @@
<Unit filename="CGeneralTextHandler.h" />
<Unit filename="CHeroHandler.cpp" />
<Unit filename="CHeroHandler.h" />
<Unit filename="CMakeLists.txt" />
<Unit filename="CModHandler.cpp" />
<Unit filename="CModHandler.h" />
<Unit filename="CObstacleInstance.cpp" />
@ -217,11 +218,18 @@
<Unit filename="filesystem/CFileInputStream.h" />
<Unit filename="filesystem/CFilesystemLoader.cpp" />
<Unit filename="filesystem/CFilesystemLoader.h" />
<Unit filename="filesystem/CInputOutputStream.h" />
<Unit filename="filesystem/CInputStream.h" />
<Unit filename="filesystem/CMemoryBuffer.cpp" />
<Unit filename="filesystem/CMemoryBuffer.h" />
<Unit filename="filesystem/CMemoryStream.cpp" />
<Unit filename="filesystem/CMemoryStream.h" />
<Unit filename="filesystem/COutputStream.h" />
<Unit filename="filesystem/CStream.h" />
<Unit filename="filesystem/CZipLoader.cpp" />
<Unit filename="filesystem/CZipLoader.h" />
<Unit filename="filesystem/CZipSaver.cpp" />
<Unit filename="filesystem/CZipSaver.h" />
<Unit filename="filesystem/FileInfo.cpp" />
<Unit filename="filesystem/FileInfo.h" />
<Unit filename="filesystem/FileStream.cpp" />
@ -229,6 +237,8 @@
<Unit filename="filesystem/Filesystem.cpp" />
<Unit filename="filesystem/Filesystem.h" />
<Unit filename="filesystem/ISimpleResourceLoader.h" />
<Unit filename="filesystem/MinizipExtensions.cpp" />
<Unit filename="filesystem/MinizipExtensions.h" />
<Unit filename="filesystem/ResourceID.cpp" />
<Unit filename="filesystem/ResourceID.h" />
<Unit filename="int3.h" />
@ -306,6 +316,12 @@
<Unit filename="rmg/CZoneGraphGenerator.h" />
<Unit filename="rmg/CZonePlacer.cpp" />
<Unit filename="rmg/CZonePlacer.h" />
<Unit filename="serializer/JsonDeserializer.cpp" />
<Unit filename="serializer/JsonDeserializer.h" />
<Unit filename="serializer/JsonSerializeFormat.cpp" />
<Unit filename="serializer/JsonSerializeFormat.h" />
<Unit filename="serializer/JsonSerializer.cpp" />
<Unit filename="serializer/JsonSerializer.h" />
<Unit filename="spells/AdventureSpellMechanics.cpp" />
<Unit filename="spells/AdventureSpellMechanics.h" />
<Unit filename="spells/BattleSpellMechanics.cpp" />

View File

@ -188,8 +188,14 @@
<ClCompile Include="CThreadHelper.cpp" />
<ClCompile Include="CTownHandler.cpp" />
<ClCompile Include="CRandomGenerator.cpp" />
<ClCompile Include="filesystem\CMemoryBuffer.cpp" />
<ClCompile Include="filesystem\CZipSaver.cpp" />
<ClCompile Include="filesystem\FileInfo.cpp" />
<ClCompile Include="filesystem\FileStream.cpp" />
<ClCompile Include="filesystem\MinizipExtensions.cpp" />
<ClCompile Include="serializer\JsonDeserializer.cpp" />
<ClCompile Include="serializer\JsonSerializeFormat.cpp" />
<ClCompile Include="serializer\JsonSerializer.cpp" />
<ClCompile Include="spells\CSpellHandler.cpp" />
<ClCompile Include="spells\ISpellMechanics.cpp" />
<ClCompile Include="spells\AdventureSpellMechanics.cpp" />
@ -308,13 +314,19 @@
<ClInclude Include="filesystem\CFileInfo.h" />
<ClInclude Include="filesystem\CFileInputStream.h" />
<ClInclude Include="filesystem\CFilesystemLoader.h" />
<ClInclude Include="filesystem\CInputOutputStream.h" />
<ClInclude Include="filesystem\CInputStream.h" />
<ClInclude Include="filesystem\CMemoryBuffer.h" />
<ClInclude Include="filesystem\CMemoryStream.h" />
<ClInclude Include="filesystem\COutputStream.h" />
<ClInclude Include="filesystem\CStream.h" />
<ClInclude Include="filesystem\CZipLoader.h" />
<ClInclude Include="filesystem\CZipSaver.h" />
<ClInclude Include="filesystem\FileInfo.h" />
<ClInclude Include="filesystem\FileStream.h" />
<ClInclude Include="filesystem\Filesystem.h" />
<ClInclude Include="filesystem\ISimpleResourceLoader.h" />
<ClInclude Include="filesystem\MinizipExtensions.h" />
<ClInclude Include="filesystem\ResourceID.h" />
<ClInclude Include="FunctionList.h" />
<ClInclude Include="IBonusTypeHandler.h" />
@ -370,6 +382,9 @@
<ClInclude Include="rmg\CZonePlacer.h" />
<ClInclude Include="rmg\float3.h" />
<ClInclude Include="ScopeGuard.h" />
<ClInclude Include="serializer\JsonDeserializer.h" />
<ClInclude Include="serializer\JsonSerializeFormat.h" />
<ClInclude Include="serializer\JsonSerializer.h" />
<ClInclude Include="spells\AdventureSpellMechanics.h" />
<ClInclude Include="spells\BattleSpellMechanics.h" />
<ClInclude Include="spells\CDefaultSpellMechanics.h" />

View File

@ -26,6 +26,9 @@
<Filter Include="spells">
<UniqueIdentifier>{bda963b1-00e1-412a-9b44-f5cd3f8e9e33}</UniqueIdentifier>
</Filter>
<Filter Include="serializer">
<UniqueIdentifier>{2f582170-d8a6-42f3-8da3-8255bac28f5a}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="BattleAction.cpp" />
@ -234,6 +237,24 @@
<Filter>filesystem</Filter>
</ClCompile>
<ClCompile Include="filesystem\FileInfo.cpp" />
<ClCompile Include="serializer\JsonDeserializer.cpp">
<Filter>serializer</Filter>
</ClCompile>
<ClCompile Include="serializer\JsonSerializeFormat.cpp">
<Filter>serializer</Filter>
</ClCompile>
<ClCompile Include="serializer\JsonSerializer.cpp">
<Filter>serializer</Filter>
</ClCompile>
<ClCompile Include="filesystem\CMemoryBuffer.cpp">
<Filter>filesystem</Filter>
</ClCompile>
<ClCompile Include="filesystem\CZipSaver.cpp">
<Filter>filesystem</Filter>
</ClCompile>
<ClCompile Include="filesystem\MinizipExtensions.cpp">
<Filter>filesystem</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="CCreatureSet.h">
@ -581,5 +602,32 @@
<ClInclude Include="filesystem\FileInfo.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="serializer\JsonDeserializer.h">
<Filter>serializer</Filter>
</ClInclude>
<ClInclude Include="serializer\JsonSerializeFormat.h">
<Filter>serializer</Filter>
</ClInclude>
<ClInclude Include="serializer\JsonSerializer.h">
<Filter>serializer</Filter>
</ClInclude>
<ClInclude Include="filesystem\CInputOutputStream.h">
<Filter>filesystem</Filter>
</ClInclude>
<ClInclude Include="filesystem\CMemoryBuffer.h">
<Filter>filesystem</Filter>
</ClInclude>
<ClInclude Include="filesystem\COutputStream.h">
<Filter>filesystem</Filter>
</ClInclude>
<ClInclude Include="filesystem\CStream.h">
<Filter>filesystem</Filter>
</ClInclude>
<ClInclude Include="filesystem\CZipSaver.h">
<Filter>filesystem</Filter>
</ClInclude>
<ClInclude Include="filesystem\MinizipExtensions.h">
<Filter>filesystem</Filter>
</ClInclude>
</ItemGroup>
</Project>

View File

@ -18,6 +18,7 @@ CFileInputStream::CFileInputStream(const boost::filesystem::path & file, si64 st
fileStream.seekg(dataStart, std::ios::beg);
}
si64 CFileInputStream::read(ui8 * data, si64 size)
{
si64 origin = tell();

View File

@ -0,0 +1,9 @@
#pragma once
#include "CInputStream.h"
#include "COutputStream.h"
class CInputOutputStream: public CInputStream, public COutputStream
{
};

View File

@ -1,5 +1,7 @@
#pragma once
#include "CStream.h"
/*
* CInputStream.h, part of VCMI engine
*
@ -13,7 +15,7 @@
/**
* Abstract class which provides method definitions for reading from a stream.
*/
class DLL_LINKAGE CInputStream : private boost::noncopyable
class DLL_LINKAGE CInputStream : public virtual CStream
{
public:
/**
@ -30,36 +32,6 @@ public:
*/
virtual si64 read(ui8 * data, si64 size) = 0;
/**
* Seeks the internal read pointer to the specified position.
*
* @param position The read position from the beginning.
* @return the position actually moved to, -1 on error.
*/
virtual si64 seek(si64 position) = 0;
/**
* Gets the current read position in the stream.
*
* @return the read position.
*/
virtual si64 tell() = 0;
/**
* Skips delta numbers of bytes.
*
* @param delta The count of bytes to skip.
* @return the count of bytes skipped actually.
*/
virtual si64 skip(si64 delta) = 0;
/**
* Gets the length in bytes of the stream.
*
* @return the length in bytes of the stream.
*/
virtual si64 getSize() = 0;
/**
* @brief for convenience, reads whole stream at once
*

View File

@ -0,0 +1,73 @@
/*
* CMemoryBuffer.cpp, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#include "StdInc.h"
#include "CMemoryBuffer.h"
///CMemoryBuffer
CMemoryBuffer::CMemoryBuffer():
position(0)
{
buffer.reserve(4096);
}
si64 CMemoryBuffer::write(const ui8 * data, si64 size)
{
//do not shrink
const si64 newSize = tell()+size;
if(newSize>getSize())
buffer.resize(newSize);
std::copy(data, data + size, buffer.data() + position);
position += size;
return size;
}
si64 CMemoryBuffer::read(ui8 * data, si64 size)
{
si64 toRead = std::min(getSize() - tell(), size);
if(toRead > 0)
{
std::copy(buffer.data() + position, buffer.data() + position + toRead, data);
position += toRead;
}
return toRead;
}
si64 CMemoryBuffer::seek(si64 position)
{
this->position = position;
if (this->position >getSize())
this->position = getSize();
return this->position;
}
si64 CMemoryBuffer::tell()
{
return position;
}
si64 CMemoryBuffer::skip(si64 delta)
{
auto old_position = tell();
return seek(old_position + delta) - old_position;
}
si64 CMemoryBuffer::getSize()
{
return buffer.size();
}

View File

@ -0,0 +1,88 @@
#pragma once
/*
* CMemoryBuffer.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 "CInputOutputStream.h"
/**
* A class which provides IO memory buffer.
*/
class DLL_LINKAGE CMemoryBuffer : public CInputOutputStream
{
public:
typedef std::vector<ui8> TBuffer;
/**
* C-tor.
*
*/
CMemoryBuffer();
/**
* Write n bytes from the stream into the data buffer.
*
* @param data A pointer to the destination data array.
* @param size The number of bytes to write.
* @return the number of bytes written actually.
*/
si64 write(const ui8 * data, si64 size) override;
/**
* Reads n bytes from the stream into the data buffer.
*
* @param data A pointer to the destination data array.
* @param size The number of bytes to read.
* @return the number of bytes read actually.
*/
si64 read(ui8 * data, si64 size) override;
/**
* Seeks the internal read pointer to the specified position.
*
* @param position The read position from the beginning.
* @return the position actually moved to, -1 on error.
*/
si64 seek(si64 position) override;
/**
* Gets the current read position in the stream.
*
* @return the read position.
*/
si64 tell() override;
/**
* Skips delta numbers of bytes.
*
* @param delta The count of bytes to skip.
* @return the count of bytes skipped actually.
*/
si64 skip(si64 delta) override;
/**
* Gets the length in bytes of the stream.
*
* @return the length in bytes of the stream.
*/
si64 getSize() override;
const TBuffer & getBuffer(){return buffer;}
private:
/** Actual data. */
TBuffer buffer;
/** Current reading position of the stream. */
si64 position;
};

View File

@ -14,7 +14,8 @@
/**
* A class which provides method definitions for reading from memory.
*/
* @deprecated use CMemoryBuffer
*/
class DLL_LINKAGE CMemoryStream : public CInputStream
{
public:

View File

@ -0,0 +1,34 @@
#pragma once
#include "CStream.h"
/*
* COutputStream.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
*
*/
/**
* Abstract class which provides method definitions for writing into a stream.
*/
class DLL_LINKAGE COutputStream : public virtual CStream
{
public:
/**
* D-tor.
*/
virtual ~COutputStream() {}
/**
* Write n bytes from the stream into the data buffer.
*
* @param data A pointer to the destination data array.
* @param size The number of bytes to write.
* @return the number of bytes written actually.
*/
virtual si64 write(const ui8 * data, si64 size) = 0;
};

50
lib/filesystem/CStream.h Normal file
View File

@ -0,0 +1,50 @@
#pragma once
/*
* CStream.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
*
*/
class DLL_LINKAGE CStream : private boost::noncopyable
{
public:
/**
* D-tor.
*/
virtual ~CStream() {}
/**
* Seeks to the specified position.
*
* @param position The position from the beginning.
* @return the position actually moved to, -1 on error.
*/
virtual si64 seek(si64 position) = 0;
/**
* Gets the current position in the stream.
*
* @return the position.
*/
virtual si64 tell() = 0;
/**
* Relative seeks to the specified position.
*
* @param delta The count of bytes to seek from current position.
* @return the count of bytes skipped actually.
*/
virtual si64 skip(si64 delta) = 0;
/**
* Gets the length of the stream.
*
* @return the length in bytes
*/
virtual si64 getSize() = 0;
};

View File

@ -1,5 +1,4 @@
#include "StdInc.h"
//#include "../../Global.h"
#include "CZipLoader.h"
#include "FileStream.h"
@ -15,9 +14,13 @@
*
*/
CZipStream::CZipStream(const boost::filesystem::path & archive, unz64_file_pos filepos)
CZipStream::CZipStream(std::shared_ptr<CIOApi> api, const boost::filesystem::path & archive, unz64_file_pos filepos)
{
file = unzOpen2_64(archive.c_str(), FileStream::GetMinizipFilefunc());
zlib_filefunc64_def zlibApi;
zlibApi = api->getApiStructure();
file = unzOpen2_64(archive.c_str(), &zlibApi);
unzGoToFilePos64(file, &filepos);
unzOpenCurrentFile(file);
}
@ -47,7 +50,10 @@ ui32 CZipStream::calculateCRC32()
return info.crc;
}
CZipLoader::CZipLoader(const std::string & mountPoint, const boost::filesystem::path & archive):
///CZipLoader
CZipLoader::CZipLoader(const std::string & mountPoint, const boost::filesystem::path & archive, std::shared_ptr<CIOApi> api):
ioApi(api),
zlibApi(ioApi->getApiStructure()),
archiveName(archive),
mountPoint(mountPoint),
files(listFiles(mountPoint, archive))
@ -59,7 +65,10 @@ std::unordered_map<ResourceID, unz64_file_pos> CZipLoader::listFiles(const std::
{
std::unordered_map<ResourceID, unz64_file_pos> ret;
unzFile file = unzOpen2_64(archive.c_str(), FileStream::GetMinizipFilefunc());
unzFile file = unzOpen2_64(archive.c_str(), &zlibApi);
if(file == nullptr)
logGlobal->errorStream() << archive << " failed to open";
if (unzGoToFirstFile(file) == UNZ_OK)
{
@ -86,7 +95,7 @@ std::unordered_map<ResourceID, unz64_file_pos> CZipLoader::listFiles(const std::
std::unique_ptr<CInputStream> CZipLoader::load(const ResourceID & resourceName) const
{
return make_unique<CZipStream>(archiveName, files.at(resourceName));
return std::unique_ptr<CInputStream>(new CZipStream(ioApi, archiveName, files.at(resourceName)));
}
bool CZipLoader::existsResource(const ResourceID & resourceName) const

View File

@ -15,12 +15,7 @@
#include "ResourceID.h"
#include "CCompressedStream.h"
// Necessary here in order to get all types
#ifdef USE_SYSTEM_MINIZIP
#include <minizip/unzip.h>
#else
#include "../minizip/unzip.h"
#endif
#include "MinizipExtensions.h"
class DLL_LINKAGE CZipStream : public CBufferedStream
{
@ -29,10 +24,11 @@ class DLL_LINKAGE CZipStream : public CBufferedStream
public:
/**
* @brief constructs zip stream from already opened file
* @param api virtual filesystem interface
* @param archive path to archive to open
* @param filepos position of file to open
*/
CZipStream(const boost::filesystem::path & archive, unz64_file_pos filepos);
CZipStream(std::shared_ptr<CIOApi> api, const boost::filesystem::path & archive, unz64_file_pos filepos);
~CZipStream();
si64 getSize() override;
@ -44,6 +40,8 @@ protected:
class DLL_LINKAGE CZipLoader : public ISimpleResourceLoader
{
std::shared_ptr<CIOApi> ioApi;
zlib_filefunc64_def zlibApi;
boost::filesystem::path archiveName;
std::string mountPoint;
@ -51,7 +49,7 @@ class DLL_LINKAGE CZipLoader : public ISimpleResourceLoader
std::unordered_map<ResourceID, unz64_file_pos> listFiles(const std::string & mountPoint, const boost::filesystem::path &archive);
public:
CZipLoader(const std::string & mountPoint, const boost::filesystem::path & archive);
CZipLoader(const std::string & mountPoint, const boost::filesystem::path & archive, std::shared_ptr<CIOApi> api = std::shared_ptr<CIOApi>(new CDefaultIOApi()));
/// Interface implementation
/// @see ISimpleResourceLoader
@ -61,7 +59,6 @@ public:
std::unordered_set<ResourceID> getFilteredFiles(std::function<bool(const ResourceID &)> filter) const override;
};
namespace ZipArchive
{
/// List all files present in archive

View File

@ -0,0 +1,120 @@
/*
* CZipSaver.cpp, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#include "StdInc.h"
#include "CZipSaver.h"
///CZipOutputStream
CZipOutputStream::CZipOutputStream(CZipSaver * owner_, zipFile archive, const std::string & archiveFilename):
handle(archive),
owner(owner_)
{
zip_fileinfo fileInfo;
std::time_t t = time(nullptr);
fileInfo.dosDate = 0;
struct tm * localTime = std::localtime(&t);
fileInfo.tmz_date.tm_hour = localTime->tm_hour;
fileInfo.tmz_date.tm_mday = localTime->tm_mday;
fileInfo.tmz_date.tm_min = localTime->tm_min;
fileInfo.tmz_date.tm_mon = localTime->tm_mon;
fileInfo.tmz_date.tm_sec = localTime->tm_sec;
fileInfo.tmz_date.tm_year = localTime->tm_year;
fileInfo.external_fa = 0; //???
fileInfo.internal_fa = 0;
int status = zipOpenNewFileInZip4_64(
handle,
archiveFilename.c_str(),
&fileInfo,
nullptr,//extrafield_local
0,
nullptr,//extrafield_global
0,
nullptr,//comment
Z_DEFLATED,
Z_DEFAULT_COMPRESSION,
0,//raw
-15,//windowBits
9,//memLevel
Z_DEFAULT_STRATEGY,//strategy
nullptr,//password
0,//crcForCrypting
20,//versionMadeBy
0,//flagBase
0//zip64
);
if(status != ZIP_OK)
throw new std::runtime_error("CZipOutputStream: zipOpenNewFileInZip failed");
owner->activeStream = this;
}
CZipOutputStream::~CZipOutputStream()
{
int status = zipCloseFileInZip(handle);
if (status != ZIP_OK)
logGlobal->errorStream() << "CZipOutputStream: stream finalize failed: "<<status;
owner->activeStream = nullptr;
}
si64 CZipOutputStream::write(const ui8 * data, si64 size)
{
int ret = zipWriteInFileInZip(handle, (const void*)data, (unsigned)size);
if (ret == ZIP_OK)
return size;
else
return 0;
}
///CZipSaver
CZipSaver::CZipSaver(std::shared_ptr<CIOApi> api, const std::string & path):
ioApi(api),
zipApi(ioApi->getApiStructure()),
handle(nullptr),
activeStream(nullptr)
{
handle = zipOpen2_64(path.c_str(), APPEND_STATUS_CREATE, nullptr, &zipApi);
if (handle == nullptr)
throw new std::runtime_error("CZipSaver: Failed to create archive");
}
CZipSaver::~CZipSaver()
{
if(activeStream != nullptr)
{
logGlobal->error("CZipSaver::~CZipSaver: active stream found");
zipCloseFileInZip(handle);
}
if(handle != nullptr)
{
int status = zipClose(handle, nullptr);
if (status != ZIP_OK)
logGlobal->errorStream() << "CZipSaver: archive finalize failed: "<<status;
}
}
std::unique_ptr<COutputStream> CZipSaver::addFile(const std::string & archiveFilename)
{
if(activeStream != nullptr)
throw new std::runtime_error("CZipSaver::addFile: stream already opened");
std::unique_ptr<COutputStream> stream(new CZipOutputStream(this, handle, archiveFilename));
return std::move(stream);
}

View File

@ -0,0 +1,57 @@
#pragma once
/*
* CZipSaver.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 "COutputStream.h"
#include "MinizipExtensions.h"
class CZipSaver;
class DLL_LINKAGE CZipOutputStream: public COutputStream
{
public:
/**
* @brief constructs zip stream from already opened file
* @param archive archive handle, must be opened
* @param archiveFilename name of file to write
*/
explicit CZipOutputStream(CZipSaver * owner_, zipFile archive, const std::string & archiveFilename);
~CZipOutputStream();
si64 write(const ui8 * data, si64 size) override;
si64 seek(si64 position) override {return -1;};
si64 tell() override {return 0;};
si64 skip(si64 delta) override {return 0;};
si64 getSize() override {return 0;};
private:
zipFile handle;
CZipSaver * owner;
};
class DLL_LINKAGE CZipSaver
{
public:
explicit CZipSaver(std::shared_ptr<CIOApi> api, const std::string & path);
virtual ~CZipSaver();
std::unique_ptr<COutputStream> addFile(const std::string & archiveFilename);
private:
std::shared_ptr<CIOApi> ioApi;
zlib_filefunc64_def zipApi;
zipFile handle;
///due to minizip design only one file stream may opened at a time
COutputStream * activeStream;
friend class CZipOutputStream;
};

View File

@ -0,0 +1,254 @@
/*
* MinizipExtensions.cpp, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#include "StdInc.h"
#include "MinizipExtensions.h"
#include "CMemoryBuffer.h"
#include "FileStream.h"
template <class _Stream> inline uLong streamRead(voidpf opaque, voidpf stream, void * buf, uLong size)
{
assert(opaque != nullptr);
assert(stream != nullptr);
_Stream * actualStream = static_cast<_Stream *>(stream);
return actualStream->read((ui8 *)buf, size);
}
template <class _Stream> inline ZPOS64_T streamTell(voidpf opaque, voidpf stream)
{
assert(opaque != nullptr);
assert(stream != nullptr);
_Stream * actualStream = static_cast<_Stream *>(stream);
return actualStream->tell();
}
template <class _Stream> inline long streamSeek(voidpf opaque, voidpf stream, ZPOS64_T offset, int origin)
{
assert(opaque != nullptr);
assert(stream != nullptr);
_Stream * actualStream = static_cast<_Stream *>(stream);
long ret = 0;
switch (origin)
{
case ZLIB_FILEFUNC_SEEK_CUR :
if(actualStream->skip(offset) != offset)
ret = -1;
break;
case ZLIB_FILEFUNC_SEEK_END:
{
const si64 pos = actualStream->getSize() - offset;
if(actualStream->seek(pos) != pos)
ret = -1;
}
break;
case ZLIB_FILEFUNC_SEEK_SET :
if(actualStream->seek(offset) != offset)
ret = -1;
break;
default: ret = -1;
}
if(ret == -1)
logGlobal->error("Stream seek failed");
return ret;
}
template <class _Stream> inline int streamProxyClose(voidpf opaque, voidpf stream)
{
assert(opaque != nullptr);
assert(stream != nullptr);
_Stream * actualStream = static_cast<_Stream *>(stream);
logGlobal->traceStream() << "Proxy stream closed";
actualStream->seek(0);
return 0;
}
///CDefaultIOApi
CDefaultIOApi::CDefaultIOApi()
{
}
CDefaultIOApi::~CDefaultIOApi()
{
}
zlib_filefunc64_def CDefaultIOApi::getApiStructure()
{
return * FileStream::GetMinizipFilefunc();
}
///CProxyIOApi
CProxyIOApi::CProxyIOApi(CInputOutputStream * buffer):
data(buffer)
{
}
CProxyIOApi::~CProxyIOApi()
{
}
zlib_filefunc64_def CProxyIOApi::getApiStructure()
{
zlib_filefunc64_def api;
api.opaque = this;
api.zopen64_file = &openFileProxy;
api.zread_file = &readFileProxy;
api.zwrite_file = &writeFileProxy;
api.ztell64_file = &tellFileProxy;
api.zseek64_file = &seekFileProxy;
api.zclose_file = &closeFileProxy;
api.zerror_file = &errorFileProxy;
return api;
}
voidpf ZCALLBACK CProxyIOApi::openFileProxy(voidpf opaque, const void * filename, int mode)
{
assert(opaque != nullptr);
boost::filesystem::path path;
if(filename != nullptr)
path = static_cast<const boost::filesystem::path::value_type *>(filename);
return ((CProxyIOApi *)opaque)->openFile(path, mode);
}
uLong ZCALLBACK CProxyIOApi::readFileProxy(voidpf opaque, voidpf stream, void * buf, uLong size)
{
return streamRead<CInputOutputStream>(opaque, stream, buf, size);
}
uLong ZCALLBACK CProxyIOApi::writeFileProxy(voidpf opaque, voidpf stream, const void * buf, uLong size)
{
assert(opaque != nullptr);
assert(stream != nullptr);
CInputOutputStream * actualStream = static_cast<CInputOutputStream *>(stream);
return (uLong)actualStream->write((const ui8 *)buf, size);
}
ZPOS64_T ZCALLBACK CProxyIOApi::tellFileProxy(voidpf opaque, voidpf stream)
{
return streamTell<CInputOutputStream>(opaque, stream);
}
long ZCALLBACK CProxyIOApi::seekFileProxy(voidpf opaque, voidpf stream, ZPOS64_T offset, int origin)
{
return streamSeek<CInputOutputStream>(opaque, stream, offset, origin);
}
int ZCALLBACK CProxyIOApi::closeFileProxy(voidpf opaque, voidpf stream)
{
return streamProxyClose<CInputOutputStream>(opaque, stream);
}
int ZCALLBACK CProxyIOApi::errorFileProxy(voidpf opaque, voidpf stream)
{
return 0;
}
CInputOutputStream * CProxyIOApi::openFile(const boost::filesystem::path & filename, int mode)
{
logGlobal->traceStream() << "CProxyIOApi: stream opened for " <<filename.string() <<" with mode "<<mode;
data->seek(0);
return data;
}
///CProxyROIOApi
CProxyROIOApi::CProxyROIOApi(CInputStream * buffer):
data(buffer)
{
}
CProxyROIOApi::~CProxyROIOApi()
{
}
zlib_filefunc64_def CProxyROIOApi::getApiStructure()
{
zlib_filefunc64_def api;
api.opaque = this;
api.zopen64_file = &openFileProxy;
api.zread_file = &readFileProxy;
api.zwrite_file = &writeFileProxy;
api.ztell64_file = &tellFileProxy;
api.zseek64_file = &seekFileProxy;
api.zclose_file = &closeFileProxy;
api.zerror_file = &errorFileProxy;
return api;
}
CInputStream * CProxyROIOApi::openFile(const boost::filesystem::path& filename, int mode)
{
logGlobal->traceStream() << "CProxyIOApi: stream opened for " <<filename.string() <<" with mode "<<mode;
data->seek(0);
return data;
}
voidpf ZCALLBACK CProxyROIOApi::openFileProxy(voidpf opaque, const void* filename, int mode)
{
assert(opaque != nullptr);
boost::filesystem::path path;
if(filename != nullptr)
path = static_cast<const boost::filesystem::path::value_type *>(filename);
return ((CProxyROIOApi *)opaque)->openFile(path, mode);
}
uLong ZCALLBACK CProxyROIOApi::readFileProxy(voidpf opaque, voidpf stream, void * buf, uLong size)
{
return streamRead<CInputStream>(opaque, stream, buf, size);
}
uLong ZCALLBACK CProxyROIOApi::writeFileProxy(voidpf opaque, voidpf stream, const void* buf, uLong size)
{
logGlobal->errorStream() << "Attempt to write to read-only stream";
return 0;
}
ZPOS64_T ZCALLBACK CProxyROIOApi::tellFileProxy(voidpf opaque, voidpf stream)
{
return streamTell<CInputStream>(opaque, stream);
}
long ZCALLBACK CProxyROIOApi::seekFileProxy(voidpf opaque, voidpf stream, ZPOS64_T offset, int origin)
{
return streamSeek<CInputStream>(opaque, stream, offset, origin);
}
int ZCALLBACK CProxyROIOApi::closeFileProxy(voidpf opaque, voidpf stream)
{
return streamProxyClose<CInputStream>(opaque, stream);
}
int ZCALLBACK CProxyROIOApi::errorFileProxy(voidpf opaque, voidpf stream)
{
return 0;
}

View File

@ -0,0 +1,88 @@
#pragma once
/*
* MinizipExtensions.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
*
*/
#ifdef USE_SYSTEM_MINIZIP
#include <minizip/unzip.h>
#include <minizip/zip.h>
#include <minizip/ioapi.h>
#else
#include "../minizip/unzip.h"
#include "../minizip/zip.h"
#include "../minizip/ioapi.h"
#endif
class CInputStream;
class CInputOutputStream;
class CMemoryBuffer;
class DLL_LINKAGE CIOApi
{
public:
virtual ~CIOApi(){};
virtual zlib_filefunc64_def getApiStructure() = 0;
};
///redirects back to minizip ioapi
//todo: replace with Virtual FileSystem interface
class DLL_LINKAGE CDefaultIOApi: public CIOApi
{
public:
CDefaultIOApi();
~CDefaultIOApi();
zlib_filefunc64_def getApiStructure() override;
};
///redirects all file IO to single stream
class DLL_LINKAGE CProxyIOApi: public CIOApi
{
public:
CProxyIOApi(CInputOutputStream * buffer);
~CProxyIOApi();
zlib_filefunc64_def getApiStructure() override;
private:
CInputOutputStream * openFile(const boost::filesystem::path & filename, int mode);
CInputOutputStream * data;
static voidpf ZCALLBACK openFileProxy(voidpf opaque, const void * filename, int mode);
static uLong ZCALLBACK readFileProxy(voidpf opaque, voidpf stream, void * buf, uLong size);
static uLong ZCALLBACK writeFileProxy(voidpf opaque, voidpf stream, const void * buf, uLong size);
static ZPOS64_T ZCALLBACK tellFileProxy(voidpf opaque, voidpf stream);
static long ZCALLBACK seekFileProxy(voidpf opaque, voidpf stream, ZPOS64_T offset, int origin);
static int ZCALLBACK closeFileProxy(voidpf opaque, voidpf stream);
static int ZCALLBACK errorFileProxy(voidpf opaque, voidpf stream);
};
///redirects all file IO to single stream read-only
class DLL_LINKAGE CProxyROIOApi: public CIOApi
{
public:
CProxyROIOApi(CInputStream * buffer);
~CProxyROIOApi();
zlib_filefunc64_def getApiStructure() override;
private:
CInputStream * openFile(const boost::filesystem::path & filename, int mode);
CInputStream * data;
static voidpf ZCALLBACK openFileProxy(voidpf opaque, const void * filename, int mode);
static uLong ZCALLBACK readFileProxy(voidpf opaque, voidpf stream, void * buf, uLong size);
static uLong ZCALLBACK writeFileProxy(voidpf opaque, voidpf stream, const void * buf, uLong size);
static ZPOS64_T ZCALLBACK tellFileProxy(voidpf opaque, voidpf stream);
static long ZCALLBACK seekFileProxy(voidpf opaque, voidpf stream, ZPOS64_T offset, int origin);
static int ZCALLBACK closeFileProxy(voidpf opaque, voidpf stream);
static int ZCALLBACK errorFileProxy(voidpf opaque, voidpf stream);
};

View File

@ -136,7 +136,8 @@ EResType::Type EResTypeHelper::getTypeFromExtension(std::string extension)
{".VSGM1", EResType::SERVER_SAVEGAME},
{".ERM", EResType::ERM},
{".ERT", EResType::ERT},
{".ERS", EResType::ERS}
{".ERS", EResType::ERS},
{".VMAP", EResType::MAP}
};
auto iter = stringToRes.find(extension);

View File

@ -108,7 +108,7 @@ void CArmedInstance::updateMoraleBonusFromArmy()
undeadModifier = new Bonus(Bonus::PERMANENT, Bonus::MORALE, Bonus::ARMY, -1, UNDEAD_MODIFIER_ID, VLC->generaltexth->arraytxt[116]);
undeadModifier->description = undeadModifier->description.substr(0, undeadModifier->description.size()-2);//trim value
addNewBonus(undeadModifier);
}
}
}
else if(undeadModifier)
removeBonus(undeadModifier);

View File

@ -25,6 +25,7 @@
#include "../CTownHandler.h"
#include "../mapping/CMap.h"
#include "CGTownInstance.h"
#include "../serializer/JsonSerializeFormat.h"
///helpers
static void showInfoDialog(const PlayerColor playerID, const ui32 txtID, const ui16 soundID)
@ -1470,6 +1471,49 @@ bool CGHeroInstance::hasVisions(const CGObjectInstance * target, const int subty
return (distance < visionsRange) && (target->pos.z == pos.z);
}
void CGHeroInstance::serializeJsonOptions(JsonSerializeFormat& handler)
{
serializeJsonOwner(handler);
if(handler.saving)
{
if(type)
{
handler.serializeString("type", type->identifier);
}
else
{
auto temp = VLC->heroh->heroes[subID]->identifier;
handler.serializeString("type", temp);
}
}
else
{
if(ID == Obj::HERO || ID == Obj::PRISON)
{
std::string typeName;
handler.serializeString("type", typeName);
auto rawId = VLC->modh->identifiers.getIdentifier("core", "hero", typeName);
if(rawId)
subID = rawId.get();
else
subID = 0; //fallback to Orrin, throw error instead?
}
}
CCreatureSet::serializeJson(handler, "army");
{
auto artifacts = handler.enterStruct("artifacts");
if(handler.saving)
CArtifactSet::writeJson(handler.getCurrent());
else
CArtifactSet::readJson(handler.getCurrent());
}
}
bool CGHeroInstance::isMissionCritical() const
{
for(const TriggeredEvent & event : IObjectInterface::cb->getMapHeader()->triggeredEvents)

View File

@ -251,6 +251,7 @@ public:
std::string getObjectName() const override;
protected:
void setPropertyDer(ui8 what, ui32 val) override;//synchr
void serializeJsonOptions(JsonSerializeFormat & handler) override;
private:
void levelUpAutomatically();

View File

@ -11,6 +11,7 @@
#include "StdInc.h"
#include "CGTownInstance.h"
#include "CObjectClassesHandler.h"
#include "../spells/CSpellHandler.h"
#include "../NetPacks.h"
#include "../CGeneralTextHandler.h"
@ -19,6 +20,7 @@
#include "../CGameState.h"
#include "../mapping/CMapDefines.h"
#include "../CPlayerState.h"
#include "../serializer/JsonSerializeFormat.h"
std::vector<const CArtifact *> CGTownInstance::merchantArtifacts;
std::vector<int> CGTownInstance::universitySkills;
@ -314,6 +316,13 @@ void CGDwelling::blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer)
}
}
void CGDwelling::serializeJsonOptions(JsonSerializeFormat & handler)
{
//todo: CGDwelling::serializeJsonOptions
if(ID != Obj::WAR_MACHINE_FACTORY && ID != Obj::REFUGEE_CAMP)
serializeJsonOwner(handler);
}
int CGTownInstance::getSightRadius() const //returns sight distance
{
if (subID == ETownType::TOWER)
@ -1181,6 +1190,63 @@ void CGTownInstance::battleFinished(const CGHeroInstance *hero, const BattleResu
}
}
void CGTownInstance::serializeJsonOptions(JsonSerializeFormat & handler)
{
CGObjectInstance::serializeJsonOwner(handler);
CCreatureSet::serializeJson(handler, "army");
handler.serializeBool<ui8>("tightFormation", 1, 0, formation);
handler.serializeString("name", name);
if(!handler.saving)
{
builtBuildings.insert(BuildingID::DEFAULT);//just in case
}
//todo: serialize buildings
// {
// std::vector<bool> standard;
// standard.resize(44, true);
//
//
// JsonSerializeFormat::LIC buildingsLIC(, CTownHandler::decodeBuilding, CTownHandler::encodeBuilding);
// }
{
JsonSerializeFormat::LIC spellsLIC(VLC->spellh->getDefaultAllowed(), CSpellHandler::decodeSpell, CSpellHandler::encodeSpell);
for(SpellID id : possibleSpells)
spellsLIC.any[id.num] = true;
for(SpellID id : obligatorySpells)
spellsLIC.all[id.num] = true;
handler.serializeLIC("spells", spellsLIC);
if(!handler.saving)
{
possibleSpells.clear();
for(si32 idx = 0; idx < spellsLIC.any.size(); idx++)
{
if(spellsLIC.any[idx])
{
possibleSpells.push_back(SpellID(idx));
}
}
obligatorySpells.clear();
for(si32 idx = 0; idx < spellsLIC.all.size(); idx++)
{
if(spellsLIC.all[idx])
{
obligatorySpells.push_back(SpellID(idx));
}
}
}
}
}
COPWBonus::COPWBonus (BuildingID index, CGTownInstance *TOWN)
{
ID = index;

View File

@ -52,6 +52,9 @@ public:
CSpecObjInfo * info; //h3m info about dewlling
TCreaturesSet creatures; //creatures[level] -> <vector of alternative ids (base creature and upgrades, creatures amount>
protected:
void serializeJsonOptions(JsonSerializeFormat & handler) override;
private:
void initObj() override;
void onHeroVisit(const CGHeroInstance * h) const override;
@ -254,4 +257,5 @@ public:
std::string getObjectName() const override;
protected:
void setPropertyDer(ui8 what, ui32 val) override;
void serializeJsonOptions(JsonSerializeFormat & handler) override;
};

View File

@ -74,7 +74,6 @@ CObjectClassesHandler::CObjectClassesHandler()
SET_HANDLER("pandora", CGPandoraBox);
SET_HANDLER("pickable", CGPickable);
SET_HANDLER("prison", CGHeroInstance);
SET_HANDLER("prison", CGHeroInstance);
SET_HANDLER("questGuard", CGQuestGuard);
SET_HANDLER("resource", CGResource);
SET_HANDLER("scholar", CGScholar);
@ -148,17 +147,21 @@ si32 selectNextID(const JsonNode & fixedID, const Map & map, si32 defaultID)
return defaultID; // some H3M objects loaded, first modded found
}
void CObjectClassesHandler::loadObjectEntry(const JsonNode & entry, ObjectContainter * obj)
void CObjectClassesHandler::loadObjectEntry(const std::string & identifier, const JsonNode & entry, ObjectContainter * obj)
{
if (!handlerConstructors.count(obj->handlerName))
{
logGlobal->errorStream() << "Handler with name " << obj->handlerName << " was not found!";
return;
}
si32 id = selectNextID(entry["index"], obj->objects, 1000);
std::string convertedId = VLC->modh->normalizeIdentifier(entry.meta, "core", identifier);
si32 id = selectNextID(entry["index"], obj->subObjects, 1000);
auto handler = handlerConstructors.at(obj->handlerName)();
handler->setType(obj->id, id);
handler->setTypeName(obj->identifier, convertedId);
if (customNames.count(obj->id) && customNames.at(obj->id).size() > id)
handler->init(entry, customNames.at(obj->id).at(id));
@ -175,50 +178,49 @@ void CObjectClassesHandler::loadObjectEntry(const JsonNode & entry, ObjectContai
legacyTemplates.erase(range.first, range.second);
}
logGlobal->debugStream() << "Loaded object " << obj->id << ":" << id;
assert(!obj->objects.count(id)); // DO NOT override
obj->objects[id] = handler;
logGlobal->debugStream() << "Loaded object " << obj->identifier << "(" << obj->id << ")" << ":" << convertedId << "(" << id << ")" ;
assert(!obj->subObjects.count(id)); // DO NOT override
obj->subObjects[id] = handler;
obj->subIds[convertedId] = id;
}
CObjectClassesHandler::ObjectContainter * CObjectClassesHandler::loadFromJson(const JsonNode & json)
CObjectClassesHandler::ObjectContainter * CObjectClassesHandler::loadFromJson(const JsonNode & json, const std::string & name)
{
auto obj = new ObjectContainter();
obj->identifier = name;
obj->name = json["name"].String();
obj->handlerName = json["handler"].String();
obj->base = json["base"];
obj->id = selectNextID(json["index"], objects, 256);
for (auto entry : json["types"].Struct())
{
loadObjectEntry(entry.second, obj);
loadObjectEntry(entry.first, entry.second, obj);
}
return obj;
}
void CObjectClassesHandler::loadObject(std::string scope, std::string name, const JsonNode & data)
{
auto object = loadFromJson(data);
auto object = loadFromJson(data, normalizeIdentifier(scope, "core", name));
objects[object->id] = object;
VLC->modh->identifiers.registerObject(scope, "object", name, object->id);
}
void CObjectClassesHandler::loadObject(std::string scope, std::string name, const JsonNode & data, size_t index)
{
auto object = loadFromJson(data);
auto object = loadFromJson(data, normalizeIdentifier(scope, "core", name));
assert(objects[index] == nullptr); // ensure that this id was not loaded before
objects[index] = object;
VLC->modh->identifiers.registerObject(scope, "object", name, object->id);
}
void CObjectClassesHandler::loadSubObject(std::string name, JsonNode config, si32 ID, boost::optional<si32> subID)
void CObjectClassesHandler::loadSubObject(const std::string & identifier, JsonNode config, si32 ID, boost::optional<si32> subID)
{
config.setType(JsonNode::DATA_STRUCT); // ensure that input is not NULL
assert(objects.count(ID));
if (subID)
{
assert(objects.at(ID)->objects.count(subID.get()) == 0);
assert(objects.at(ID)->subObjects.count(subID.get()) == 0);
assert(config["index"].isNull());
config["index"].Float() = subID.get();
}
@ -227,14 +229,14 @@ void CObjectClassesHandler::loadSubObject(std::string name, JsonNode config, si3
JsonUtils::inherit(config, objects.at(ID)->base);
config.setMeta(oldMeta);
loadObjectEntry(config, objects[ID]);
loadObjectEntry(identifier, config, objects[ID]);
}
void CObjectClassesHandler::removeSubObject(si32 ID, si32 subID)
{
assert(objects.count(ID));
assert(objects.at(ID)->objects.count(subID));
objects.at(ID)->objects.erase(subID);
assert(objects.at(ID)->subObjects.count(subID));
objects.at(ID)->subObjects.erase(subID); //TODO: cleanup string id map
}
std::vector<bool> CObjectClassesHandler::getDefaultAllowed() const
@ -246,14 +248,28 @@ TObjectTypeHandler CObjectClassesHandler::getHandlerFor(si32 type, si32 subtype)
{
if (objects.count(type))
{
if (objects.at(type)->objects.count(subtype))
return objects.at(type)->objects.at(subtype);
if (objects.at(type)->subObjects.count(subtype))
return objects.at(type)->subObjects.at(subtype);
}
logGlobal->errorStream() << "Failed to find object of type " << type << ":" << subtype;
assert(0); // FIXME: throw error?
throw std::runtime_error("Object type handler not found");
return nullptr;
}
TObjectTypeHandler CObjectClassesHandler::getHandlerFor(std::string type, std::string subtype) const
{
boost::optional<si32> id = VLC->modh->identifiers.getIdentifier("core", "object", type, false);
if(id)
{
si32 subId = objects.at(id.get())->subIds.at(subtype);
return objects.at(id.get())->subObjects.at(subId);
}
logGlobal->errorStream() << "Failed to find object of type " << type << ":" << subtype;
throw std::runtime_error("Object type handler not found");
return nullptr;
}
std::set<si32> CObjectClassesHandler::knownObjects() const
{
std::set<si32> ret;
@ -270,7 +286,7 @@ std::set<si32> CObjectClassesHandler::knownSubObjects(si32 primaryID) const
if (objects.count(primaryID))
{
for (auto entry : objects.at(primaryID)->objects)
for (auto entry : objects.at(primaryID)->subObjects)
ret.insert(entry.first);
}
return ret;
@ -292,7 +308,7 @@ void CObjectClassesHandler::afterLoadFinalization()
{
for (auto entry : objects)
{
for (auto obj : entry.second->objects)
for (auto obj : entry.second->subObjects)
{
obj.second->afterLoadFinalization();
if (obj.second->getTemplates().empty())
@ -301,7 +317,7 @@ void CObjectClassesHandler::afterLoadFinalization()
}
//duplicate existing two-way portals to make reserve for RMG
auto& portalVec = objects[Obj::MONOLITH_TWO_WAY]->objects;
auto& portalVec = objects[Obj::MONOLITH_TWO_WAY]->subObjects;
size_t portalCount = portalVec.size();
size_t currentIndex = portalCount;
while (portalVec.size() < 100)
@ -341,6 +357,12 @@ void AObjectTypeHandler::setType(si32 type, si32 subtype)
this->subtype = subtype;
}
void AObjectTypeHandler::setTypeName(std::string type, std::string subtype)
{
this->typeName = type;
this->subTypeName = subtype;
}
static ui32 loadJsonOrMax(const JsonNode & input)
{
if (input.isNull())
@ -387,6 +409,14 @@ bool AObjectTypeHandler::objectFilter(const CGObjectInstance *, const ObjectTemp
return false; // by default there are no overrides
}
void AObjectTypeHandler::preInitObject(CGObjectInstance * obj) const
{
obj->ID = Obj(type);
obj->subID = subtype;
obj->typeName = typeName;
obj->subTypeName = subTypeName;
}
void AObjectTypeHandler::initTypeData(const JsonNode & input)
{
// empty implementation for overrides

View File

@ -101,22 +101,26 @@ class DLL_LINKAGE AObjectTypeHandler : public boost::noncopyable
/// Human-readable name of this object, used for objects like banks and dwellings, if set
boost::optional<std::string> objectName;
si32 type;
si32 subtype;
JsonNode base; /// describes base template
std::vector<ObjectTemplate> templates;
protected:
void preInitObject(CGObjectInstance * obj) const;
virtual bool objectFilter(const CGObjectInstance *, const ObjectTemplate &) const;
/// initialization for classes that inherit this one
virtual void initTypeData(const JsonNode & input);
public:
std::string typeName;
std::string subTypeName;
si32 type;
si32 subtype;
virtual ~AObjectTypeHandler(){}
void setType(si32 type, si32 subtype);
void setTypeName(std::string type, std::string subtype);
/// loads generic data from Json structure and passes it towards type-specific constructors
void init(const JsonNode & input, boost::optional<std::string> name = boost::optional<std::string>());
@ -149,12 +153,16 @@ public:
/// This should set remaining properties, including randomized or depending on map
virtual void configureObject(CGObjectInstance * object, CRandomGenerator & rng) const = 0;
/// Returns object configuration, if available. Othervice returns NULL
/// Returns object configuration, if available. Otherwise returns NULL
virtual std::unique_ptr<IObjectInfo> getObjectInfo(ObjectTemplate tmpl) const = 0;
template <typename Handler> void serialize(Handler &h, const int version)
{
h & type & subtype & templates & rmgInfo & objectName;
if(version >= 759)
{
h & typeName & subTypeName;
}
}
};
@ -167,16 +175,21 @@ class DLL_LINKAGE CObjectClassesHandler : public IHandlerBase
struct ObjectContainter
{
si32 id;
std::string identifier;
std::string name; // human-readable name
std::string handlerName; // ID of handler that controls this object, shoul be determined using hadlerConstructor map
std::string handlerName; // ID of handler that controls this object, should be determined using handlerConstructor map
JsonNode base;
std::map<si32, TObjectTypeHandler> objects;
std::map<si32, TObjectTypeHandler> subObjects;
std::map<std::string, si32> subIds;//full id from core scope -> subtype
template <typename Handler> void serialize(Handler &h, const int version)
{
h & name & handlerName & base & objects;
h & name & handlerName & base & subObjects;
if(version >= 759)
{
h & identifier & subIds;
}
}
};
@ -194,8 +207,8 @@ class DLL_LINKAGE CObjectClassesHandler : public IHandlerBase
/// format: customNames[primaryID][secondaryID] -> name
std::map<si32, std::vector<std::string>> customNames;
void loadObjectEntry(const JsonNode & entry, ObjectContainter * obj);
ObjectContainter * loadFromJson(const JsonNode & json);
void loadObjectEntry(const std::string & identifier, const JsonNode & entry, ObjectContainter * obj);
ObjectContainter * loadFromJson(const JsonNode & json, const std::string & name);
public:
CObjectClassesHandler();
@ -204,7 +217,7 @@ public:
void loadObject(std::string scope, std::string name, const JsonNode & data) override;
void loadObject(std::string scope, std::string name, const JsonNode & data, size_t index) override;
void loadSubObject(std::string name, JsonNode config, si32 ID, boost::optional<si32> subID = boost::optional<si32>());
void loadSubObject(const std::string & identifier, JsonNode config, si32 ID, boost::optional<si32> subID = boost::optional<si32>());
void removeSubObject(si32 ID, si32 subID);
void beforeValidate(JsonNode & object) override;
@ -218,10 +231,11 @@ public:
/// returns handler for specified object (ID-based). ObjectHandler keeps ownership
TObjectTypeHandler getHandlerFor(si32 type, si32 subtype) const;
TObjectTypeHandler getHandlerFor(std::string type, std::string subtype) const;
std::string getObjectName(si32 type) const;
std::string getObjectName(si32 type, si32 subtype) const;
/// Returns handler string describing the handler (for use in client)
std::string getObjectHandlerName(si32 type) const;

View File

@ -18,11 +18,14 @@
#include "../filesystem/ResourceID.h"
#include "../IGameCallback.h"
#include "../CGameState.h"
#include "../StringConstants.h"
#include "../mapping/CMap.h"
#include "CObjectClassesHandler.h"
#include "CGTownInstance.h"
#include "../serializer/JsonSerializeFormat.h"
IGameCallback * IObjectInterface::cb = nullptr;
///helpers
@ -317,6 +320,66 @@ bool CGObjectInstance::passableFor(PlayerColor color) const
return false;
}
void CGObjectInstance::serializeJson(JsonSerializeFormat & handler)
{
//only save here, loading is handled by map loader
if(handler.saving)
{
handler.serializeString("type", typeName);
handler.serializeString("subtype", subTypeName);
handler.serializeNumeric("x", pos.x);
handler.serializeNumeric("y", pos.y);
handler.serializeNumeric("l", pos.z);
appearance.writeJson(handler.getCurrent()["template"], false);
}
{
auto options = handler.enterStruct("options");
serializeJsonOptions(handler);
}
if(handler.saving && handler.getCurrent()["options"].Struct().empty())
{
handler.getCurrent().Struct().erase("options");
}
}
void CGObjectInstance::serializeJsonOptions(JsonSerializeFormat & handler)
{
}
void CGObjectInstance::serializeJsonOwner(JsonSerializeFormat & handler)
{
std::string temp;
//todo: use enum serialize
if(handler.saving)
{
if(tempOwner.isValidPlayer())
{
temp = GameConstants::PLAYER_COLOR_NAMES[tempOwner.getNum()];
handler.serializeString("owner", temp);
}
}
else
{
tempOwner = PlayerColor::NEUTRAL;//this method assumes that object is ownable
handler.serializeString("owner", temp);
if(temp != "")
{
auto rawOwner = vstd::find_pos(GameConstants::PLAYER_COLOR_NAMES, temp);
if(rawOwner >=0)
tempOwner = PlayerColor(rawOwner);
else
logGlobal->errorStream() << "Invalid owner :" << temp;
}
}
}
CGObjectInstanceBySubIdFinder::CGObjectInstanceBySubIdFinder(CGObjectInstance * obj) : obj(obj)
{

View File

@ -1,4 +1,4 @@
#pragma once
#pragma once
#include "ObjectTemplate.h"
@ -21,6 +21,7 @@ class IGameCallback;
class CGObjectInstance;
struct MetaString;
struct BattleResult;
class JsonSerializeFormat;
// This one teleport-specific, but has to be available everywhere in callbacks and netpacks
// For now it's will be there till teleports code refactored and moved into own file
@ -39,7 +40,7 @@ public:
virtual void newTurn() const;
virtual void initObj(); //synchr
virtual void setProperty(ui8 what, ui32 val);//synchr
//Called when queries created DURING HERO VISIT are resolved
//First parameter is always hero that visited object and triggered the query
virtual void battleFinished(const CGHeroInstance *hero, const BattleResult &result) const;
@ -117,6 +118,10 @@ public:
/// If true hero can visit this object only from neighbouring tiles and can't stand on this object
bool blockVisit;
std::string instanceName;
std::string typeName;
std::string subTypeName;
CGObjectInstance();
~CGObjectInstance();
@ -168,17 +173,32 @@ public:
//friend class CGameHandler;
///Entry point of binary (de-)serialization
template <typename Handler> void serialize(Handler &h, const int version)
{
if(version >= 759)
{
h & instanceName & typeName & subTypeName;
}
h & pos & ID & subID & id & tempOwner & blockVisit & appearance;
//definfo is handled by map serializer
}
///Entry point of Json (de-)serialization
void serializeJson(JsonSerializeFormat & handler);
protected:
/// virtual method that allows synchronously update object state on server and all clients
virtual void setPropertyDer(ui8 what, ui32 val);
/// Gives dummy bonus from this object to hero. Can be used to track visited state
void giveDummyBonus(ObjectInstanceID heroID, ui8 duration = Bonus::ONE_DAY) const;
///Serialize object-type specific options
virtual void serializeJsonOptions(JsonSerializeFormat & handler);
void serializeJsonOwner(JsonSerializeFormat & handler);
};
/// function object which can be used to find an object with an specific sub ID

View File

@ -188,6 +188,7 @@ void CRewardableConstructor::initTypeData(const JsonNode & config)
CGObjectInstance * CRewardableConstructor::create(ObjectTemplate tmpl) const
{
auto ret = new CRewardableObject();
preInitObject(ret);
ret->appearance = tmpl;
return ret;
}

View File

@ -32,8 +32,7 @@ protected:
ObjectType * createTyped(ObjectTemplate tmpl) const
{
auto obj = new ObjectType();
obj->ID = tmpl.id;
obj->subID = tmpl.subid;
preInitObject(obj);
obj->appearance = tmpl;
return obj;
}

View File

@ -11,17 +11,19 @@
#include "StdInc.h"
#include "MiscObjects.h"
#include "../StringConstants.h"
#include "../NetPacks.h"
#include "../CGeneralTextHandler.h"
#include "../CSoundBase.h"
#include "../CModHandler.h"
#include "../CHeroHandler.h"
#include "CObjectClassesHandler.h"
#include "../spells/CSpellHandler.h"
#include "../IGameCallback.h"
#include "../CGameState.h"
#include "../mapping/CMap.h"
#include "../CPlayerState.h"
#include "../serializer/JsonSerializeFormat.h"
std::map <si32, std::vector<ObjectInstanceID> > CGMagi::eyelist;
ui8 CGObelisk::obeliskCount = 0; //how many obelisks are on map
@ -592,6 +594,63 @@ void CGCreature::giveReward(const CGHeroInstance * h) const
}
}
static const std::vector<std::string> CHARACTER_JSON =
{
"compliant", "friendly", "aggressive", "hostile", "savage"
};
void CGCreature::serializeJsonOptions(JsonSerializeFormat & handler)
{
handler.serializeNumericEnum("character", CHARACTER_JSON, (si8)0, character);
if(handler.saving)
{
if(hasStackAtSlot(SlotID(0)))
{
si32 amount = getStack(SlotID(0)).count;
handler.serializeNumeric("amount", amount);
}
if(resources.nonZero())
{
for(size_t idx = 0; idx < resources.size(); idx++)
handler.getCurrent()["rewardResources"][GameConstants::RESOURCE_NAMES[idx]].Float() = resources[idx];
}
auto tmp = (gainedArtifact == ArtifactID(ArtifactID::NONE) ? "" : gainedArtifact.toArtifact()->identifier);
handler.serializeString("rewardArtifact", tmp);
}
else
{
si32 amount = 0;
handler.serializeNumeric("amount", amount);
auto hlp = new CStackInstance();
hlp->count = amount;
//type will be set during initialization
putStack(SlotID(0), hlp);
{
TResources tmp(handler.getCurrent()["rewardResources"]);
std::swap(tmp,resources);
}
{
gainedArtifact = ArtifactID(ArtifactID::NONE);
std::string tmp;
handler.serializeString("rewardArtifact", tmp);
if(tmp != "")
{
auto artid = VLC->modh->identifiers.getIdentifier("core", "artifact", tmp);
if(artid)
gainedArtifact = ArtifactID(artid.get());
}
}
}
handler.serializeBool("noGrowing", notGrowingTeam);
handler.serializeBool("neverFlees", neverFlees);
handler.serializeString("rewardMessage", message);
}
//CGMine
void CGMine::onHeroVisit( const CGHeroInstance * h ) const
{
int relations = cb->gameState()->getPlayerRelations(h->tempOwner, tempOwner);
@ -630,7 +689,7 @@ void CGMine::newTurn() const
void CGMine::initObj()
{
if(subID >= 7) //Abandoned Mine
if(isAbandoned())
{
//set guardians
int howManyTroglodytes = cb->gameState()->getRandomGenerator().nextInt(100, 199);
@ -657,6 +716,11 @@ void CGMine::initObj()
producedQuantity = defaultResProduction();
}
bool CGMine::isAbandoned() const
{
return (subID >= 7);
}
std::string CGMine::getObjectName() const
{
return VLC->generaltexth->mines.at(subID).first;
@ -714,7 +778,7 @@ void CGMine::battleFinished(const CGHeroInstance *hero, const BattleResult &resu
{
if(result.winner == 0) //attacker won
{
if(subID == 7)
if(isAbandoned())
{
showInfoDialog(hero->tempOwner, 85, 0);
}
@ -728,6 +792,63 @@ void CGMine::blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) con
cb->startBattleI(hero, this);
}
void CGMine::serializeJsonOptions(JsonSerializeFormat & handler)
{
CCreatureSet::serializeJson(handler, "army");
if(isAbandoned())
{
auto possibleResources = handler.enterStruct("possibleResources");
JsonNode & node = handler.getCurrent();
if(handler.saving)
{
for(int i = 0; i < PlayerColor::PLAYER_LIMIT_I; i++)
if(tempOwner.getNum() & 1<<i)
{
JsonNode one(JsonNode::DATA_STRING);
one.String() = GameConstants::RESOURCE_NAMES[i];
node.Vector().push_back(one);
}
}
else
{
std::set<int> possibleResources;
if(node.Vector().size() == 0)
{
//assume all allowed
for(int i = (int)Res::WOOD; i < (int) Res::GOLD; i++)
possibleResources.insert(i);
}
else
{
auto names = node.convertTo<std::vector<std::string>>();
for(const std::string & s : names)
{
int raw_res = vstd::find_pos(GameConstants::RESOURCE_NAMES, s);
if(raw_res < 0)
logGlobal->errorStream() << "Invalid resource name: "+s;
else
possibleResources.insert(raw_res);
}
int tmp = 0;
for(int r : possibleResources)
tmp |= (1<<r);
tempOwner = PlayerColor(tmp);
}
}
}
else
{
serializeJsonOwner(handler);
}
}
std::string CGResource::getHoverText(PlayerColor player) const
{
return VLC->generaltexth->restypes[subID];
@ -812,6 +933,13 @@ void CGResource::blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer)
cb->startBattleI(hero, this);
}
void CGResource::serializeJsonOptions(JsonSerializeFormat & handler)
{
CCreatureSet::serializeJson(handler, "guards");
handler.serializeNumeric("amount", amount);
handler.serializeString("guardMessage", message);
}
CGTeleport::CGTeleport() :
type(UNKNOWN), channel(TeleportChannelID())
{
@ -1298,6 +1426,21 @@ void CGArtifact::blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer)
cb->startBattleI(hero, this);
}
void CGArtifact::serializeJsonOptions(JsonSerializeFormat& handler)
{
handler.serializeString("guardMessage", message);
CCreatureSet::serializeJson(handler, "guards");
if(handler.saving && ID == Obj::SPELL_SCROLL)
{
const Bonus * b = storedArtifact->getBonusLocalFirst(Selector::type(Bonus::SPELL));
SpellID spellId(b->subtype);
std::string spell = SpellID(b->subtype).toSpell()->identifier;
handler.serializeString("spell", spell);
}
}
void CGWitchHut::initObj()
{
if (allowedAbilities.empty()) //this can happen for RMG. regular maps load abilities from map file
@ -1355,6 +1498,33 @@ std::string CGWitchHut::getHoverText(const CGHeroInstance * hero) const
return hoverName;
}
void CGWitchHut::serializeJsonOptions(JsonSerializeFormat & handler)
{
//TODO: unify allowed abilities with others - make them std::vector<bool>
std::vector<bool> temp;
temp.resize(GameConstants::SKILL_QUANTITY, false);
auto standard = VLC->heroh->getDefaultAllowedAbilities(); //todo: for WitchHut default is all except Leadership and Necromancy
if(handler.saving)
{
for(si32 i = 0; i < GameConstants::SKILL_QUANTITY; ++i)
if(vstd::contains(allowedAbilities, i))
temp[i] = true;
}
handler.serializeLIC("allowedSkills", &CHeroHandler::decodeSkill, &CHeroHandler::encodeSkill, standard, temp);
if(!handler.saving)
{
allowedAbilities.clear();
for (si32 i=0; i<temp.size(); i++)
if(temp[i])
allowedAbilities.push_back(i);
}
}
void CGMagicWell::onHeroVisit( const CGHeroInstance * h ) const
{
int message;
@ -1494,6 +1664,11 @@ std::string CGShrine::getHoverText(const CGHeroInstance * hero) const
return hoverName;
}
void CGShrine::serializeJsonOptions(JsonSerializeFormat& handler)
{
handler.serializeId("spell", &CSpellHandler::decodeSpell, &CSpellHandler::encodeSpell, SpellID(SpellID::NONE), spell);
}
void CGSignBottle::initObj()
{
//if no text is set than we pick random from the predefined ones
@ -1520,11 +1695,10 @@ void CGSignBottle::onHeroVisit( const CGHeroInstance * h ) const
cb->removeObject(this);
}
//TODO: remove
//void CGScholar::giveAnyBonus( const CGHeroInstance * h ) const
//{
//
//}
void CGSignBottle::serializeJsonOptions(JsonSerializeFormat& handler)
{
handler.serializeString("text", message);
}
void CGScholar::onHeroVisit( const CGHeroInstance * h ) const
{
@ -1599,6 +1773,59 @@ void CGScholar::initObj()
}
}
void CGScholar::serializeJsonOptions(JsonSerializeFormat & handler)
{
JsonNode& json = handler.getCurrent();
if(handler.saving)
{
switch(bonusType)
{
case PRIM_SKILL:
json["rewardPrimSkill"].String() = PrimarySkill::names[bonusID];
break;
case SECONDARY_SKILL:
json["rewardSkill"].String() = NSecondarySkill::names[bonusID];
break;
case SPELL:
json["rewardSpell"].String() = VLC->spellh->objects.at(bonusID)->identifier;
break;
case RANDOM:
break;
}
}
else
{
bonusType = RANDOM;
if(json["rewardPrimSkill"].String() != "")
{
auto raw = VLC->modh->identifiers.getIdentifier("core", "primSkill", json["rewardPrimSkill"].String());
if(raw)
{
bonusType = PRIM_SKILL;
bonusID = raw.get();
}
}
else if(json["rewardSkill"].String() != "")
{
auto raw = VLC->modh->identifiers.getIdentifier("core", "skill", json["rewardSkill"].String());
if(raw)
{
bonusType = SECONDARY_SKILL;
bonusID = raw.get();
}
}
else if(json["rewardSpell"].String() != "")
{
auto raw = VLC->modh->identifiers.getIdentifier("core", "spell", json["rewardSpell"].String());
if(raw)
{
bonusType = SPELL;
bonusID = raw.get();
}
}
}
}
void CGGarrison::onHeroVisit (const CGHeroInstance *h) const
{
int ally = cb->gameState()->getPlayerRelations(h->tempOwner, tempOwner);
@ -1635,6 +1862,13 @@ void CGGarrison::battleFinished(const CGHeroInstance *hero, const BattleResult &
onHeroVisit(hero);
}
void CGGarrison::serializeJsonOptions(JsonSerializeFormat& handler)
{
handler.serializeBool("removableUnits", removableUnits);
serializeJsonOwner(handler);
CCreatureSet::serializeJson(handler, "army");
}
void CGMagi::initObj()
{
if (ID == Obj::EYE_OF_MAGI)
@ -1772,6 +2006,11 @@ void CGShipyard::onHeroVisit( const CGHeroInstance * h ) const
}
}
void CGShipyard::serializeJsonOptions(JsonSerializeFormat& handler)
{
serializeJsonOwner(handler);
}
void CCartographer::onHeroVisit( const CGHeroInstance * h ) const
{
//if player has not bought map of this subtype yet and underground exist for stalagmite cartographer
@ -1953,3 +2192,8 @@ void CGLighthouse::giveBonusTo( PlayerColor player ) const
gb.bonus.sid = id.getNum();
cb->sendAndApply(&gb);
}
void CGLighthouse::serializeJsonOptions(JsonSerializeFormat& handler)
{
serializeJsonOwner(handler);
}

View File

@ -14,6 +14,8 @@
*
*/
class CMap;
class DLL_LINKAGE CPlayersVisited: public CGObjectInstance
{
public:
@ -84,8 +86,9 @@ public:
}
protected:
void setPropertyDer(ui8 what, ui32 val) override;
private:
void serializeJsonOptions(JsonSerializeFormat & handler) override;
private:
void fight(const CGHeroInstance *h) const;
void flee( const CGHeroInstance * h ) const;
void fleeDecision(const CGHeroInstance *h, ui32 pursue) const;
@ -96,7 +99,6 @@ private:
};
class DLL_LINKAGE CGSignBottle : public CGObjectInstance //signs and ocean bottles
{
public:
@ -110,6 +112,8 @@ public:
h & static_cast<CGObjectInstance&>(*this);
h & message;
}
protected:
void serializeJsonOptions(JsonSerializeFormat & handler) override;
};
class DLL_LINKAGE CGWitchHut : public CPlayersVisited
@ -127,6 +131,8 @@ public:
h & static_cast<CPlayersVisited&>(*this);
h & allowedAbilities & ability;
}
protected:
void serializeJsonOptions(JsonSerializeFormat & handler) override;
};
class DLL_LINKAGE CGScholar : public CGObjectInstance
@ -136,7 +142,6 @@ public:
EBonusType bonusType;
ui16 bonusID; //ID of skill/spell
// void giveAnyBonus(const CGHeroInstance * h) const; //TODO: remove
CGScholar() : bonusType(EBonusType::RANDOM){};
void onHeroVisit(const CGHeroInstance * h) const override;
void initObj() override;
@ -145,6 +150,8 @@ public:
h & static_cast<CGObjectInstance&>(*this);
h & bonusType & bonusID;
}
protected:
void serializeJsonOptions(JsonSerializeFormat & handler) override;
};
class DLL_LINKAGE CGGarrison : public CArmedInstance
@ -161,6 +168,8 @@ public:
h & static_cast<CArmedInstance&>(*this);
h & removableUnits;
}
protected:
void serializeJsonOptions(JsonSerializeFormat & handler) override;
};
class DLL_LINKAGE CGArtifact : public CArmedInstance
@ -185,6 +194,8 @@ public:
h & static_cast<CArmedInstance&>(*this);
h & message & storedArtifact;
}
protected:
void serializeJsonOptions(JsonSerializeFormat & handler) override;
};
class DLL_LINKAGE CGResource : public CArmedInstance
@ -207,6 +218,8 @@ public:
h & static_cast<CArmedInstance&>(*this);
h & amount & message;
}
protected:
void serializeJsonOptions(JsonSerializeFormat & handler) override;
};
class DLL_LINKAGE CGShrine : public CPlayersVisited
@ -223,6 +236,8 @@ public:
h & static_cast<CPlayersVisited&>(*this);;
h & spell;
}
protected:
void serializeJsonOptions(JsonSerializeFormat & handler) override;
};
class DLL_LINKAGE CGMine : public CArmedInstance
@ -243,6 +258,7 @@ private:
std::string getObjectName() const override;
std::string getHoverText(PlayerColor player) const override;
bool isAbandoned() const;
public:
template <typename Handler> void serialize(Handler &h, const int version)
{
@ -250,6 +266,8 @@ public:
h & producedResource & producedQuantity;
}
ui32 defaultResProduction();
protected:
void serializeJsonOptions(JsonSerializeFormat & handler) override;
};
struct DLL_LINKAGE TeleportChannel
@ -414,6 +432,8 @@ public:
h & static_cast<CGObjectInstance&>(*this);
h & static_cast<IShipyard&>(*this);
}
protected:
void serializeJsonOptions(JsonSerializeFormat & handler) override;
};
class DLL_LINKAGE CGMagi : public CGObjectInstance
@ -430,8 +450,6 @@ public:
}
};
class DLL_LINKAGE CCartographer : public CPlayersVisited
{
///behaviour varies depending on surface and floor
@ -482,4 +500,6 @@ public:
h & static_cast<CGObjectInstance&>(*this);
}
void giveBonusTo( PlayerColor player ) const;
protected:
void serializeJsonOptions(JsonSerializeFormat & handler) override;
};

View File

@ -183,7 +183,7 @@ void ObjectTemplate::readMap(CBinaryReader & reader)
}
}
void ObjectTemplate::readJson(const JsonNode &node)
void ObjectTemplate::readJson(const JsonNode &node, const bool withTerrain)
{
animationFile = node["animation"].String();
@ -202,7 +202,7 @@ void ObjectTemplate::readJson(const JsonNode &node)
else
visitDir = 0x00;
if (!node["allowedTerrains"].isNull())
if(withTerrain && !node["allowedTerrains"].isNull())
{
for (auto & entry : node["allowedTerrains"].Vector())
allowedTerrains.insert(ETerrainType(vstd::find_pos(GameConstants::TERRAIN_NAMES, entry.String())));
@ -211,11 +211,14 @@ void ObjectTemplate::readJson(const JsonNode &node)
{
for (size_t i=0; i< GameConstants::TERRAIN_TYPES; i++)
allowedTerrains.insert(ETerrainType(i));
allowedTerrains.erase(ETerrainType::ROCK);
}
if (allowedTerrains.empty())
if(withTerrain && allowedTerrains.empty())
logGlobal->warnStream() << "Loaded template without allowed terrains!";
auto charToTile = [&](const char & ch) -> ui8
{
switch (ch)
@ -249,11 +252,97 @@ void ObjectTemplate::readJson(const JsonNode &node)
usedTiles[mask.size() - 1 - i][line.size() - 1 - j] = charToTile(line[j]);
}
const JsonNode zindex = node["zIndex"];
if (!zindex.isNull())
printPriority = node["zIndex"].Float();
else
printPriority = 0; //default value
printPriority = node["zIndex"].Float();
}
void ObjectTemplate::writeJson(JsonNode & node, const bool withTerrain) const
{
node["animation"].String() = animationFile;
if(visitDir != 0x0 && isVisitable())
{
JsonVector & visitDirs = node["visitableFrom"].Vector();
visitDirs.resize(3);
visitDirs[0].String().resize(3);
visitDirs[1].String().resize(3);
visitDirs[2].String().resize(3);
visitDirs[0].String()[0] = (visitDir & 1) ? '+' : '-';
visitDirs[0].String()[1] = (visitDir & 2) ? '+' : '-';
visitDirs[0].String()[2] = (visitDir & 4) ? '+' : '-';
visitDirs[1].String()[2] = (visitDir & 8) ? '+' : '-';
visitDirs[2].String()[2] = (visitDir & 16) ? '+' : '-';
visitDirs[2].String()[1] = (visitDir & 32) ? '+' : '-';
visitDirs[2].String()[0] = (visitDir & 64) ? '+' : '-';
visitDirs[1].String()[0] = (visitDir & 128) ? '+' : '-';
visitDirs[1].String()[1] = '-';
}
if(withTerrain)
{
//assumed that ROCK terrain not included
if(allowedTerrains.size() < (GameConstants::TERRAIN_TYPES - 1))
{
JsonVector & data = node["allowedTerrains"].Vector();
for(auto type : allowedTerrains)
{
JsonNode value(JsonNode::DATA_STRING);
value.String() = GameConstants::TERRAIN_NAMES[type.num];
data.push_back(value);
}
}
}
auto tileToChar = [&](const ui8 & tile) -> char
{
if(tile & VISIBLE)
{
if(tile & BLOCKED)
{
if(tile & VISITABLE)
return 'A';
else
return 'B';
}
else
return 'V';
}
else
{
if(tile & BLOCKED)
{
if(tile & VISITABLE)
return 'T';
else
return 'H';
}
else
return '0';
}
};
size_t height = getHeight();
size_t width = getWidth();
JsonVector & mask = node["mask"].Vector();
for(size_t i=0; i < height; i++)
{
JsonNode lineNode(JsonNode::DATA_STRING);
std::string & line = lineNode.String();
line.resize(width);
for(size_t j=0; j < width; j++)
line[j] = tileToChar(usedTiles[height - 1 - i][width - 1 - j]);
mask.push_back(lineNode);
}
if(printPriority != 0)
node["zIndex"].Float() = printPriority;
}
ui32 ObjectTemplate::getWidth() const

View File

@ -73,7 +73,8 @@ public:
void readTxt(CLegacyConfigParser & parser);
void readMsk();
void readMap(CBinaryReader & reader);
void readJson(const JsonNode & node);
void readJson(const JsonNode & node, const bool withTerrain = true);
void writeJson(JsonNode & node, const bool withTerrain = true) const;
bool operator==(const ObjectTemplate& ot) const { return (id == ot.id && subid == ot.subid); }

View File

@ -19,7 +19,7 @@ SHeroName::SHeroName() : heroId(-1)
PlayerInfo::PlayerInfo(): canHumanPlay(false), canComputerPlay(false),
aiTactic(EAiTactic::RANDOM), isFactionRandom(false), mainCustomHeroPortrait(-1), mainCustomHeroId(-1), hasMainTown(false),
generateHeroAtMainTown(false), team(255), hasRandomHero(false), /* following are unused */ generateHero(false), p7(0), powerPlaceholders(-1)
generateHeroAtMainTown(false), team(TeamID::NO_TEAM), hasRandomHero(false), /* following are unused */ generateHero(false), p7(0), powerPlaceholders(-1)
{
allowedFactions = VLC->townh->getAllowedFactions();
}
@ -63,6 +63,7 @@ EventCondition::EventCondition(EWinLoseType condition):
object(nullptr),
value(-1),
objectType(-1),
objectSubtype(-1),
position(-1, -1, -1),
condition(condition)
{
@ -72,6 +73,7 @@ EventCondition::EventCondition(EWinLoseType condition, si32 value, si32 objectTy
object(nullptr),
value(value),
objectType(objectType),
objectSubtype(-1),
position(position),
condition(condition)
{}
@ -141,11 +143,6 @@ CGObjectInstance * TerrainTile::topVisitableObj(bool excludeTop) const
return visitableObjects.back();
}
bool TerrainTile::isCoastal() const
{
return extTileFlags & 64;
}
EDiggingStatus TerrainTile::getDiggingStatus(const bool excludeTop) const
{
if(terType == ETerrainType::WATER || terType == ETerrainType::ROCK)
@ -322,6 +319,35 @@ CGHeroInstance * CMap::getHero(int heroID)
return nullptr;
}
bool CMap::isCoastalTile(const int3 & pos) const
{
//todo: refactoring: extract neighbor tile iterator and use it in GameState
static const int3 dirs[] = { int3(0,1,0),int3(0,-1,0),int3(-1,0,0),int3(+1,0,0),
int3(1,1,0),int3(-1,1,0),int3(1,-1,0),int3(-1,-1,0) };
if(!isInTheMap(pos))
{
logGlobal->errorStream() << "Coastal check outside of map :"<<pos;
return false;
}
if(isWaterTile(pos))
return false;
for (auto & dir : dirs)
{
const int3 hlp = pos + dir;
if(!isInTheMap(hlp))
continue;
const TerrainTile &hlpt = getTile(hlp);
if(hlpt.isWater())
return true;
}
return false;
}
bool CMap::isInTheMap(const int3 & pos) const
{
if(pos.x < 0 || pos.y < 0 || pos.z < 0 || pos.x >= width || pos.y >= height
@ -349,7 +375,7 @@ const TerrainTile & CMap::getTile(const int3 & tile) const
bool CMap::isWaterTile(const int3 &pos) const
{
return isInTheMap(pos) && getTile(pos).terType == ETerrainType::WATER;
return isInTheMap(pos) && getTile(pos).isWater();
}
bool CMap::checkForVisitableDir(const int3 & src, const TerrainTile *pom, const int3 & dst ) const
@ -534,6 +560,32 @@ void CMap::addQuest(CGObjectInstance * quest)
quests.push_back(q->quest);
}
void CMap::addNewObject(CGObjectInstance * obj)
{
if(obj->id != ObjectInstanceID(objects.size()))
throw std::runtime_error("Invalid object instance id");
if(obj->instanceName == "")
throw std::runtime_error("Object instance name missing");
auto it = instanceNames.find(obj->instanceName);
if(it != instanceNames.end())
throw std::runtime_error("Object instance name duplicated:"+obj->instanceName);
objects.push_back(obj);
instanceNames[obj->instanceName] = obj;
addBlockVisTiles(obj);
if(obj->ID == Obj::TOWN)
{
towns.push_back(static_cast<CGTownInstance *>(obj));
}
if(obj->ID == Obj::HERO)
{
heroesOnMap.push_back(static_cast<CGHeroInstance*>(obj));
}
}
void CMap::initTerrain()
{
int level = twoLevel ? 2 : 1;

View File

@ -76,7 +76,7 @@ struct DLL_LINKAGE PlayerInfo
bool hasMainTown; /// The default value is false.
bool generateHeroAtMainTown; /// The default value is false.
int3 posOfMainTown;
TeamID team; /// The default value is 255 representing that the player belongs to no team.
TeamID team; /// The default value NO_TEAM
bool hasRandomHero; /// Player has a random hero
bool generateHero; /// Unused.
@ -98,6 +98,7 @@ struct DLL_LINKAGE PlayerInfo
struct DLL_LINKAGE EventCondition
{
enum EWinLoseType {
//internal use, deprecated
HAVE_ARTIFACT, // type - required artifact
HAVE_CREATURES, // type - creatures to collect, value - amount to collect
HAVE_RESOURCES, // type - resource ID, value - amount to collect
@ -105,26 +106,44 @@ struct DLL_LINKAGE EventCondition
CONTROL, // position - position of object, optional, type - type of object
DESTROY, // position - position of object, optional, type - type of object
TRANSPORT, // position - where artifact should be transported, type - type of artifact
//map format version pre 1.0
DAYS_PASSED, // value - number of days from start of the game
IS_HUMAN, // value - 0 = player is AI, 1 = player is human
DAYS_WITHOUT_TOWN, // value - how long player can live without town, 0=instakill
STANDARD_WIN, // normal defeat all enemies condition
CONST_VALUE // condition that always evaluates to "value" (0 = false, 1 = true)
CONST_VALUE, // condition that always evaluates to "value" (0 = false, 1 = true)
//map format version 1.0+
HAVE_0,
HAVE_BUILDING_0,
DESTROY_0
};
EventCondition(EWinLoseType condition = STANDARD_WIN);
EventCondition(EWinLoseType condition, si32 value, si32 objectType, int3 position = int3(-1, -1, -1));
const CGObjectInstance * object; // object that was at specified position on start
const CGObjectInstance * object; // object that was at specified position or with instance name on start
si32 value;
si32 objectType;
si32 objectSubtype;
std::string objectInstanceName;
int3 position;
EWinLoseType condition;
template <typename Handler>
void serialize(Handler & h, const int version)
{
h & object & value & objectType & position & condition;
h & object;
h & value;
h & objectType;
h & position;
h & condition;
if(version > 759)
{
h & objectSubtype;
h & objectInstanceName;
}
}
};
@ -171,7 +190,10 @@ struct DLL_LINKAGE TriggeredEvent
template <typename Handler>
void serialize(Handler & h, const int version)
{
h & identifier & trigger & description & onFulfill & effect;
h & identifier;
h & trigger;
h & description;
h & onFulfill & effect;
}
};
@ -207,7 +229,7 @@ struct DLL_LINKAGE DisposedHero
namespace EMapFormat
{
enum EMapFormat
enum EMapFormat: ui8
{
INVALID = 0,
// HEX DEC
@ -215,7 +237,8 @@ enum EMapFormat
AB = 0x15, // 21
SOD = 0x1c, // 28
// HOTA = 0x1e ... 0x20 // 28 ... 30
WOG = 0x33 // 51
WOG = 0x33, // 51
VCMI = 0xF0
};
}
@ -251,7 +274,7 @@ public:
std::vector<PlayerInfo> players; /// The default size of the vector is PlayerColor::PLAYER_LIMIT.
ui8 howManyTeams;
std::vector<bool> allowedHeroes;
std::vector<ui16> placeholdedHeroes;
bool areAnyPlayers; /// Unused. True if there are any playable players on the map.
/// "main quests" of the map that describe victory and loss conditions
@ -277,8 +300,10 @@ public:
CMapEditManager * getEditManager();
TerrainTile & getTile(const int3 & tile);
const TerrainTile & getTile(const int3 & tile) const;
bool isCoastalTile(const int3 & pos) const;
bool isInTheMap(const int3 & pos) const;
bool isWaterTile(const int3 & pos) const;
bool checkForVisitableDir( const int3 & src, const TerrainTile *pom, const int3 & dst ) const;
int3 guardingCreaturePosition (int3 pos) const;
@ -289,6 +314,7 @@ public:
void addNewArtifactInstance(CArtifactInstance * art);
void eraseArtifactInstance(CArtifactInstance * art);
void addQuest(CGObjectInstance * quest);
void addNewObject(CGObjectInstance * obj);
/// Gets object of specified type on requested position
const CGObjectInstance * getObjectiveObjectFrom(int3 pos, Obj::EObj type);
@ -326,6 +352,8 @@ public:
int3 ***guardingCreaturePositions;
std::map<std::string, ConstTransitivePtr<CGObjectInstance> > instanceNames;
private:
/// a 3-dimensional array of terrain tiles, access is as follows: x, y, level. where level=1 is underground
TerrainTile*** terrain;
@ -393,5 +421,10 @@ public:
h & CGObelisk::obeliskCount & CGObelisk::visited;
h & CGTownInstance::merchantArtifacts;
h & CGTownInstance::universitySkills;
if(formatVersion >= 759)
{
h & instanceNames;
}
}
};

View File

@ -70,7 +70,6 @@ struct DLL_LINKAGE TerrainTile
Obj topVisitableId(bool excludeTop = false) const;
CGObjectInstance * topVisitableObj(bool excludeTop = false) const;
bool isWater() const;
bool isCoastal() const;
EDiggingStatus getDiggingStatus(const bool excludeTop = true) const;
bool hasFavorableWinds() const;

View File

@ -478,7 +478,6 @@ void CDrawTerrainOperation::execute()
updateTerrainTypes();
updateTerrainViews();
//TODO add coastal bit to extTileFlags appropriately
}
void CDrawTerrainOperation::undo()
@ -1044,16 +1043,12 @@ void CInsertObjectOperation::execute()
{
obj->pos = pos;
obj->id = ObjectInstanceID(map->objects.size());
map->objects.push_back(obj);
if(obj->ID == Obj::TOWN)
{
map->towns.push_back(static_cast<CGTownInstance *>(obj));
}
if(obj->ID == Obj::HERO)
{
map->heroesOnMap.push_back(static_cast<CGHeroInstance*>(obj));
}
map->addBlockVisTiles(obj);
boost::format fmt("%s_%d");
fmt % obj->typeName % obj->id.getNum();
obj->instanceName = fmt.str();
map->addNewObject(obj);
}
void CInsertObjectOperation::undo()

View File

@ -5,6 +5,7 @@
#include "../filesystem/CBinaryReader.h"
#include "../filesystem/CCompressedStream.h"
#include "../filesystem/CMemoryStream.h"
#include "CMap.h"
#include "MapFormatH3M.h"
@ -68,21 +69,31 @@ std::unique_ptr<IMapLoader> CMapService::getMapLoader(std::unique_ptr<CInputStre
ui32 header = reader.readUInt32();
reader.getStream()->seek(0);
// Check which map format is used
// gzip header is 3 bytes only in size
switch(header & 0xffffff)
//check for ZIP magic. Zip files are VCMI maps
switch(header)
{
// gzip header magic number, reversed for LE
case 0x00088B1F:
stream = std::unique_ptr<CInputStream>(new CCompressedStream(std::move(stream), true));
return std::unique_ptr<IMapLoader>(new CMapLoaderH3M(stream.get()));
case EMapFormat::WOG :
case EMapFormat::AB :
case EMapFormat::ROE :
case EMapFormat::SOD :
return std::unique_ptr<IMapLoader>(new CMapLoaderH3M(stream.get()));
default :
throw std::runtime_error("Unknown map format");
case 0x06054b50:
case 0x04034b50:
case 0x02014b50:
return std::unique_ptr<IMapLoader>(new CMapLoaderJson(stream.get()));
break;
default:
// Check which map format is used
// gzip header is 3 bytes only in size
switch(header & 0xffffff)
{
// gzip header magic number, reversed for LE
case 0x00088B1F:
stream = std::unique_ptr<CInputStream>(new CCompressedStream(std::move(stream), true));
return std::unique_ptr<IMapLoader>(new CMapLoaderH3M(stream.get()));
case EMapFormat::WOG :
case EMapFormat::AB :
case EMapFormat::ROE :
case EMapFormat::SOD :
return std::unique_ptr<IMapLoader>(new CMapLoaderH3M(stream.get()));
default :
throw std::runtime_error("Unknown map format");
}
}
}
@ -103,5 +114,5 @@ std::unique_ptr<IMapPatcher> CMapService::getMapPatcher(std::string scenarioName
boost::to_lower(scenarioName);
logGlobal->debugStream() << "Request to patch map " << scenarioName;
return std::unique_ptr<IMapPatcher>(new CMapLoaderJson(node[scenarioName]));
return std::unique_ptr<IMapPatcher>(new CMapPatcher(node[scenarioName]));
}

View File

@ -126,7 +126,7 @@ public:
virtual std::unique_ptr<CMapHeader> loadMapHeader() = 0;
};
class DLL_LINKAGE IMapPatcher : public IMapLoader
class DLL_LINKAGE IMapPatcher
{
public:
/**
@ -135,3 +135,16 @@ public:
*/
virtual void patchMapHeader(std::unique_ptr<CMapHeader> & header) = 0;
};
/**
* Interface for saving a map.
*/
class DLL_LINKAGE IMapSaver
{
public:
/**
* Saves the VCMI/H3 map file.
*
*/
virtual void saveMap(const std::unique_ptr<CMap> & map) = 0;
};

View File

@ -119,11 +119,6 @@ void CMapLoaderH3M::init()
readEvents();
times.push_back(MapLoadingTime("events", sw.getDiff()));
// Calculate blocked / visitable positions
for(auto & elem : map->objects)
{
map->addBlockVisTiles(elem);
}
times.push_back(MapLoadingTime("blocked/visitable tiles", sw.getDiff()));
// Print profiling times
@ -615,10 +610,15 @@ void CMapLoaderH3M::readAllowedHeroes()
if(mapHeader->version > EMapFormat::ROE)
{
int placeholdersQty = reader.readUInt32();
for(int p = 0; p < placeholdersQty; ++p)
{
mapHeader->placeholdedHeroes.push_back(reader.readUInt8());
}
reader.skip(placeholdersQty * 1);
// std::vector<ui16> placeholdedHeroes;
//
// for(int p = 0; p < placeholdersQty; ++p)
// {
// placeholdedHeroes.push_back(reader.readUInt8());
// }
}
}
@ -828,7 +828,7 @@ void CMapLoaderH3M::loadArtifactsOfHero(CGHeroInstance * hero)
{
// catapult by default
assert(!hero->getArt(ArtifactPosition::MACH4));
hero->putArtifact(ArtifactPosition::MACH4, createArtifact(ArtifactID::CATAPULT));
hero->putArtifact(ArtifactPosition::MACH4, CArtifactInstance::createArtifact(map, ArtifactID::CATAPULT));
}
}
@ -885,7 +885,7 @@ bool CMapLoaderH3M::loadArtifactToSlot(CGHeroInstance * hero, int slot)
}
// this is needed, because some H3M maps (last scenario of ROE map) contain invalid data like misplaced artifacts
auto artifact = createArtifact(aid);
auto artifact = CArtifactInstance::createArtifact(map, aid);
auto artifactPos = ArtifactPosition(slot);
if (artifact->canBePutAt(ArtifactLocation(hero, artifactPos)))
{
@ -900,40 +900,6 @@ bool CMapLoaderH3M::loadArtifactToSlot(CGHeroInstance * hero, int slot)
return isArt;
}
CArtifactInstance * CMapLoaderH3M::createArtifact(int aid, int spellID /*= -1*/)
{
CArtifactInstance * a = nullptr;
if(aid >= 0)
{
if(spellID < 0)
{
a = CArtifactInstance::createNewArtifactInstance(aid);
}
else
{
a = CArtifactInstance::createScroll(SpellID(spellID).toSpell());
}
}
else //FIXME: create combined artifact instance for random combined artifacts, just in case
{
a = new CArtifactInstance(); //random, empty
}
map->addNewArtifactInstance(a);
//TODO make it nicer
if(a->artType && (!!a->artType->constituents))
{
CCombinedArtifactInstance * comb = dynamic_cast<CCombinedArtifactInstance *>(a);
for(CCombinedArtifactInstance::ConstituentInfo & ci : comb->constituentsInfo)
{
map->addNewArtifactInstance(ci.art);
}
}
return a;
}
void CMapLoaderH3M::readTerrain()
{
map->initTerrain();
@ -1236,7 +1202,7 @@ void CMapLoaderH3M::readObjects()
artID = objTempl.subid;
}
art->storedArtifact = createArtifact(artID, spellID);
art->storedArtifact = CArtifactInstance::createArtifact(map, artID, spellID);
break;
}
case Obj::RANDOM_RESOURCE:
@ -1488,16 +1454,16 @@ void CMapLoaderH3M::readObjects()
}
nobj->appearance = objTempl;
assert(idToBeGiven == ObjectInstanceID(map->objects.size()));
map->objects.push_back(nobj);
if(nobj->ID == Obj::TOWN)
{
map->towns.push_back(static_cast<CGTownInstance *>(nobj));
}
if(nobj->ID == Obj::HERO)
{
logGlobal->debugStream() << "Hero: " << VLC->heroh->heroes[nobj->subID]->name << " at " << objPos;
map->heroesOnMap.push_back(static_cast<CGHeroInstance*>(nobj));
//TODO: define valid typeName and subtypeName fro H3M maps
//boost::format fmt("%s_%d");
//fmt % nobj->typeName % nobj->id.getNum();
boost::format fmt("obj_%d");
fmt % nobj->id.getNum();
nobj->instanceName = fmt.str();
}
map->addNewObject(nobj);
}
std::sort(map->heroesOnMap.begin(), map->heroesOnMap.end(), [](const ConstTransitivePtr<CGHeroInstance> & a, const ConstTransitivePtr<CGHeroInstance> & b)
@ -1998,7 +1964,7 @@ CGTownInstance * CMapLoaderH3M::readTown(int castleID)
ui8 c = reader.readUInt8();
for(int yy = 0; yy < 8; ++yy)
{
int spellid = i * 8 + yy;
int spellid = i * 8 + yy;
if(spellid < GameConstants::SPELLS_QUANTITY)
{
if(c != (c | static_cast<ui8>(std::pow(2., yy))) && map->allowedSpell[spellid]) //add random spell only if it's allowed on entire map

View File

@ -124,15 +124,6 @@ private:
*/
bool loadArtifactToSlot(CGHeroInstance * hero, int slot);
/**
* Creates an artifact instance.
*
* @param aid the id of the artifact
* @param spellID optional. the id of a spell if a spell scroll object should be created
* @return the created artifact instance
*/
CArtifactInstance * createArtifact(int aid, int spellID = -1);
/**
* Read rumors.
*/

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,5 @@
/*
* MapFormatH3M.h, part of VCMI engine
* MapFormatJSON.h, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
@ -14,17 +13,136 @@
#include "CMapService.h"
#include "../JsonNode.h"
class TriggeredEvent;
#include "../filesystem/CZipSaver.h"
#include "../filesystem/CZipLoader.h"
#include "../GameConstants.h"
class DLL_LINKAGE CMapLoaderJson : public IMapPatcher
class TriggeredEvent;
struct TerrainTile;
struct PlayerInfo;
class CGObjectInstance;
class AObjectTypeHandler;
class JsonSerializeFormat;
class JsonDeserializer;
class JsonSerializer;
class DLL_LINKAGE CMapFormatJson
{
public:
static const int VERSION_MAJOR;
static const int VERSION_MINOR;
static const std::string HEADER_FILE_NAME;
static const std::string OBJECTS_FILE_NAME;
int fileVersionMajor;
int fileVersionMinor;
protected:
/** ptr to the map object which gets filled by data from the buffer or written to buffer */
CMap * map;
/**
* ptr to the map header object which gets filled by data from the buffer.
* (when loading map and mapHeader point to the same object)
*/
CMapHeader * mapHeader;
void serializeAllowedFactions(JsonSerializeFormat & handler, std::set<TFaction> & value);
///common part of header saving/loading
void serializeHeader(JsonSerializeFormat & handler);
///player information saving/loading
void serializePlayerInfo(JsonSerializeFormat & handler);
/**
* Reads team settings to header
*/
void readTeams(JsonDeserializer & handler);
/**
* Saves team settings to header
*/
void writeTeams(JsonSerializer & handler);
///common part triggered events of saving/loading
void serializeTriggeredEvents(JsonSerializeFormat & handler);
/**
* Reads triggered events, including victory/loss conditions
*/
void readTriggeredEvents(JsonDeserializer & handler);
/**
* Writes triggered events, including victory/loss conditions
*/
void writeTriggeredEvents(JsonSerializer & handler);
/**
* Reads one of triggered events
*/
void readTriggeredEvent(TriggeredEvent & event, const JsonNode & source);
/**
* Writes one of triggered events
*/
void writeTriggeredEvent(const TriggeredEvent & event, JsonNode & dest);
///common part of map attributes saving/loading
void serializeOptions(JsonSerializeFormat & handler);
/**
* Loads map attributes except header ones
*/
void readOptions(JsonDeserializer & handler);
/**
* Saves map attributes except header ones
*/
void writeOptions(JsonSerializer & handler);
};
class DLL_LINKAGE CMapPatcher : public CMapFormatJson, public IMapPatcher
{
public:
/**
* Default constructor.
*
* @param stream. A stream containing the map data.
*/
CMapPatcher(JsonNode stream);
public: //IMapPatcher
/**
* Modifies supplied map header using Json data
*
*/
void patchMapHeader(std::unique_ptr<CMapHeader> & header) override;
private:
/**
* Reads subset of header that can be replaced by patching.
*/
void readPatchData();
JsonNode input;
};
class DLL_LINKAGE CMapLoaderJson : public CMapFormatJson, public IMapLoader
{
public:
/**
* Constructor.
*
* @param stream a stream containing the map data
*/
CMapLoaderJson(JsonNode stream);
CMapLoaderJson(CInputStream * stream);
/**
* Loads the VCMI/Json map file.
@ -40,52 +158,107 @@ public:
*/
std::unique_ptr<CMapHeader> loadMapHeader() override;
/**
* Modifies supplied map header using Json data
*
*/
void patchMapHeader(std::unique_ptr<CMapHeader> & header) override;
private:
struct MapObjectLoader
{
MapObjectLoader(CMapLoaderJson * _owner, JsonMap::value_type & json);
CMapLoaderJson * owner;
CGObjectInstance * instance;
ObjectInstanceID id;
std::string jsonKey;//full id defined by map creator
JsonNode & configuration;
///constructs object (without configuration)
void construct();
///configures object
void configure();
};
si32 getIdentifier(const std::string & type, const std::string & name);
/**
* Reads the map header.
*/
void readHeader(const bool complete);
/**
* Reads complete map.
*/
void readMap();
/**
* Reads the map header.
*/
void readHeader();
void readTerrainTile(const std::string & src, TerrainTile & tile);
void readTerrainLevel(const JsonNode & src, const int index);
void readTerrain();
/**
* Reads subset of header that can be replaced by patching.
* Loads all map objects from zip archive
*/
void readPatchData();
void readObjects();
/**
* Reads player information.
*/
void readPlayerInfo();
JsonNode getFromArchive(const std::string & archiveFilename);
/**
* Reads triggered events, including victory/loss conditions
*/
void readTriggeredEvents();
CInputStream * buffer;
std::shared_ptr<CIOApi> ioApi;
/**
* Reads one of triggered events
*/
void readTriggeredEvent(TriggeredEvent & event, const JsonNode & source);
/** ptr to the map object which gets filled by data from the buffer */
CMap * map;
/**
* ptr to the map header object which gets filled by data from the buffer.
* (when loading map and mapHeader point to the same object)
*/
std::unique_ptr<CMapHeader> mapHeader;
const JsonNode input;
CZipLoader loader;///< object to handle zip archive operations
};
class DLL_LINKAGE CMapSaverJson : public CMapFormatJson, public IMapSaver
{
public:
/**
* Constructor.
*
* @param stream a stream to save the map to, will contain zip archive
*/
CMapSaverJson(CInputOutputStream * stream);
~CMapSaverJson();
/**
* Actually saves the VCMI/Json map into stream.
*/
void saveMap(const std::unique_ptr<CMap> & map) override;
private:
/**
* Saves @data as json file with specified @filename
*/
void addToArchive(const JsonNode & data, const std::string & filename);
/**
* Saves header to zip archive
*/
void writeHeader();
/**
* Encodes one tile into string
* @param tile tile to serialize
*/
const std::string writeTerrainTile(const TerrainTile & tile);
/**
* Saves map level into json
* @param index z coordinate
*/
JsonNode writeTerrainLevel(const int index);
/**
* Saves all terrain into zip archive
*/
void writeTerrain();
/**
* Saves all map objects into zip archive
*/
void writeObjects();
CInputOutputStream * buffer;
std::shared_ptr<CIOApi> ioApi;
CZipSaver saver;///< object to handle zip archive operations
};

View File

@ -565,11 +565,10 @@ void CMapGenerator::createConnections()
}
if (!guardPos.valid())
{
auto teleport1 = new CGMonolith;
teleport1->ID = Obj::MONOLITH_TWO_WAY;
teleport1->subID = getNextMonlithIndex();
auto factory = VLC->objtypeh->getHandlerFor(Obj::MONOLITH_TWO_WAY, getNextMonlithIndex());
auto teleport1 = factory->create(ObjectTemplate());
auto teleport2 = new CGMonolith(*teleport1);
auto teleport2 = factory->create(ObjectTemplate());
zoneA->addRequiredObject (teleport1, connection.getGuardStrength());
zoneB->addRequiredObject (teleport2, connection.getGuardStrength());
@ -579,7 +578,7 @@ void CMapGenerator::createConnections()
void CMapGenerator::addHeaderInfo()
{
map->version = EMapFormat::SOD;
map->version = EMapFormat::VCMI;
map->width = mapGenOptions->getWidth();
map->height = mapGenOptions->getHeight();
map->twoLevel = mapGenOptions->getHasTwoLevels();

View File

@ -1036,10 +1036,9 @@ bool CRmgTemplateZone::addMonster(CMapGenerator* gen, int3 &pos, si32 strength,
amount = strength / VLC->creh->creatures[creId]->AIValue;
}
auto guardFactory = VLC->objtypeh->getHandlerFor(Obj::MONSTER, creId);
auto guard = new CGCreature();
guard->ID = Obj::MONSTER;
guard->subID = creId;
auto guard = (CGCreature *) guardFactory->create(ObjectTemplate());
guard->character = CGCreature::HOSTILE;
auto hlp = new CStackInstance(creId, amount);
//will be set during initialization
@ -1318,19 +1317,23 @@ void CRmgTemplateZone::initTownType (CMapGenerator* gen)
{
for (int i = 0; i < count; i++)
{
auto town = new CGTownInstance();
town->ID = Obj::TOWN;
si32 subType = townType;
if (this->townsAreSameType)
town->subID = townType;
else
if(totalTowns>0)
{
if (townTypes.size())
town->subID = *RandomGeneratorUtil::nextItem(townTypes, gen->rand);
else
town->subID = *RandomGeneratorUtil::nextItem(getDefaultTownTypes(), gen->rand); //it is possible to have zone with no towns allowed
if(!this->townsAreSameType)
{
if (townTypes.size())
subType = *RandomGeneratorUtil::nextItem(townTypes, gen->rand);
else
subType = *RandomGeneratorUtil::nextItem(getDefaultTownTypes(), gen->rand); //it is possible to have zone with no towns allowed
}
}
auto townFactory = VLC->objtypeh->getHandlerFor(Obj::TOWN, subType);
auto town = (CGTownInstance *) townFactory->create(ObjectTemplate());
town->ID = Obj::TOWN;
town->tempOwner = player;
if (hasFort)
town->builtBuildings.insert(BuildingID::FORT);
@ -1342,10 +1345,8 @@ void CRmgTemplateZone::initTownType (CMapGenerator* gen)
town->possibleSpells.push_back(spell->id);
}
if (!totalTowns)
if (totalTowns <= 0)
{
//first town in zone sets the facton of entire zone
town->subID = townType;
//register MAIN town of zone
gen->registerZone(town->subID);
//first town in zone goes in the middle
@ -1381,10 +1382,9 @@ void CRmgTemplateZone::initTownType (CMapGenerator* gen)
randomizeTownType(gen);
}
auto town = new CGTownInstance();
town->ID = Obj::TOWN;
auto townFactory = VLC->objtypeh->getHandlerFor(Obj::TOWN, townType);
town->subID = townType;
CGTownInstance * town = (CGTownInstance *) townFactory->create(ObjectTemplate());
town->tempOwner = player;
town->builtBuildings.insert(BuildingID::FORT);
town->builtBuildings.insert(BuildingID::DEFAULT);
@ -1495,21 +1495,27 @@ void CRmgTemplateZone::paintZoneTerrain (CMapGenerator* gen, ETerrainType terrai
bool CRmgTemplateZone::placeMines (CMapGenerator* gen)
{
std::vector<Res::ERes> required_mines;
required_mines.push_back(Res::ERes::WOOD);
required_mines.push_back(Res::ERes::ORE);
static const Res::ERes woodOre[] = {Res::ERes::WOOD, Res::ERes::ORE};
static const Res::ERes preciousResources[] = {Res::ERes::GEMS, Res::ERes::CRYSTAL, Res::ERes::MERCURY, Res::ERes::SULFUR};
std::array<TObjectTypeHandler, 7> factory =
{
VLC->objtypeh->getHandlerFor(Obj::MINE, 0),
VLC->objtypeh->getHandlerFor(Obj::MINE, 1),
VLC->objtypeh->getHandlerFor(Obj::MINE, 2),
VLC->objtypeh->getHandlerFor(Obj::MINE, 3),
VLC->objtypeh->getHandlerFor(Obj::MINE, 4),
VLC->objtypeh->getHandlerFor(Obj::MINE, 5),
VLC->objtypeh->getHandlerFor(Obj::MINE, 6)
};
for (const auto & res : woodOre)
{
for (int i = 0; i < mines[res]; i++)
{
auto mine = new CGMine();
mine->ID = Obj::MINE;
mine->subID = static_cast<si32>(res);
auto mine = (CGMine *) factory.at(static_cast<si32>(res))->create(ObjectTemplate());
mine->producedResource = res;
mine->tempOwner = PlayerColor::NEUTRAL;
mine->producedQuantity = mine->defaultResProduction();
if (!i)
addCloseObject(mine, 1500); //only firts one is close
@ -1521,20 +1527,18 @@ bool CRmgTemplateZone::placeMines (CMapGenerator* gen)
{
for (int i = 0; i < mines[res]; i++)
{
auto mine = new CGMine();
mine->ID = Obj::MINE;
mine->subID = static_cast<si32>(res);
auto mine = (CGMine *) factory.at(static_cast<si32>(res))->create(ObjectTemplate());
mine->producedResource = res;
mine->tempOwner = PlayerColor::NEUTRAL;
mine->producedQuantity = mine->defaultResProduction();
addRequiredObject(mine, 3500);
}
}
for (int i = 0; i < mines[Res::GOLD]; i++)
{
auto mine = new CGMine();
mine->ID = Obj::MINE;
mine->subID = static_cast<si32>(Res::GOLD);
auto mine = (CGMine *) factory.at(Res::GOLD)->create(ObjectTemplate());
mine->producedResource = Res::GOLD;
mine->tempOwner = PlayerColor::NEUTRAL;
mine->producedQuantity = mine->defaultResProduction();
addRequiredObject(mine, 7000);
}
@ -2105,9 +2109,8 @@ void CRmgTemplateZone::placeAndGuardObject(CMapGenerator* gen, CGObjectInstance*
void CRmgTemplateZone::placeSubterraneanGate(CMapGenerator* gen, int3 pos, si32 guardStrength)
{
auto gate = new CGSubterraneanGate;
gate->ID = Obj::SUBTERRANEAN_GATE;
gate->subID = 0;
auto factory = VLC->objtypeh->getHandlerFor(Obj::SUBTERRANEAN_GATE, 0);
auto gate = factory->create(ObjectTemplate());
placeObject (gen, gate, pos, true);
addToConnectLater (getAccessibleOffset (gen, gate->appearance, pos)); //guard will be placed on accessibleOffset
guardObject (gen, gate, guardStrength, true);
@ -2301,9 +2304,8 @@ ObjectInfo CRmgTemplateZone::getRandomObject(CMapGenerator* gen, CTreasurePileIn
{
oi.generateObject = [minValue]() -> CGObjectInstance *
{
auto obj = new CGPandoraBox();
obj->ID = Obj::PANDORAS_BOX;
obj->subID = 0;
auto factory = VLC->objtypeh->getHandlerFor(Obj::PANDORAS_BOX, 0);
auto obj = (CGPandoraBox *) factory->create(ObjectTemplate());
obj->resources[Res::GOLD] = minValue;
return obj;
};
@ -2391,9 +2393,6 @@ void CRmgTemplateZone::addAllPossibleObjects(CMapGenerator* gen)
{
oi.generateObject = [i, gen, this]() -> CGObjectInstance *
{
auto obj = new CGHeroInstance;
obj->ID = Obj::PRISON;
std::vector<ui32> possibleHeroes;
for (int j = 0; j < gen->map->allowedHeroes.size(); j++)
{
@ -2402,6 +2401,10 @@ void CRmgTemplateZone::addAllPossibleObjects(CMapGenerator* gen)
}
auto hid = *RandomGeneratorUtil::nextItem(possibleHeroes, gen->rand);
auto factory = VLC->objtypeh->getHandlerFor(Obj::PRISON, 0);
auto obj = (CGHeroInstance *) factory->create(ObjectTemplate());
obj->subID = hid; //will be initialized later
obj->exp = prisonExp[i];
obj->setOwner(PlayerColor::NEUTRAL);
@ -2469,9 +2472,8 @@ void CRmgTemplateZone::addAllPossibleObjects(CMapGenerator* gen)
{
oi.generateObject = [i, gen]() -> CGObjectInstance *
{
auto obj = new CGArtifact();
obj->ID = Obj::SPELL_SCROLL;
obj->subID = 0;
auto factory = VLC->objtypeh->getHandlerFor(Obj::SPELL_SCROLL, 0);
auto obj = (CGArtifact *) factory->create(ObjectTemplate());
std::vector<SpellID> out;
for (auto spell : VLC->spellh->objects) //spellh size appears to be greater (?)
@ -2497,9 +2499,8 @@ void CRmgTemplateZone::addAllPossibleObjects(CMapGenerator* gen)
{
oi.generateObject = [i]() -> CGObjectInstance *
{
auto obj = new CGPandoraBox();
obj->ID = Obj::PANDORAS_BOX;
obj->subID = 0;
auto factory = VLC->objtypeh->getHandlerFor(Obj::PANDORAS_BOX, 0);
auto obj = (CGPandoraBox *) factory->create(ObjectTemplate());
obj->resources[Res::GOLD] = i * 5000;
return obj;
};
@ -2514,9 +2515,8 @@ void CRmgTemplateZone::addAllPossibleObjects(CMapGenerator* gen)
{
oi.generateObject = [i]() -> CGObjectInstance *
{
auto obj = new CGPandoraBox();
obj->ID = Obj::PANDORAS_BOX;
obj->subID = 0;
auto factory = VLC->objtypeh->getHandlerFor(Obj::PANDORAS_BOX, 0);
auto obj = (CGPandoraBox *) factory->create(ObjectTemplate());
obj->gainedExp = i * 5000;
return obj;
};
@ -2562,9 +2562,8 @@ void CRmgTemplateZone::addAllPossibleObjects(CMapGenerator* gen)
oi.generateObject = [creature, creaturesAmount]() -> CGObjectInstance *
{
auto obj = new CGPandoraBox();
obj->ID = Obj::PANDORAS_BOX;
obj->subID = 0;
auto factory = VLC->objtypeh->getHandlerFor(Obj::PANDORAS_BOX, 0);
auto obj = (CGPandoraBox *) factory->create(ObjectTemplate());
auto stack = new CStackInstance(creature, creaturesAmount);
obj->creatures.putStack(SlotID(0), stack);
return obj;
@ -2580,9 +2579,8 @@ void CRmgTemplateZone::addAllPossibleObjects(CMapGenerator* gen)
{
oi.generateObject = [i, gen]() -> CGObjectInstance *
{
auto obj = new CGPandoraBox();
obj->ID = Obj::PANDORAS_BOX;
obj->subID = 0;
auto factory = VLC->objtypeh->getHandlerFor(Obj::PANDORAS_BOX, 0);
auto obj = (CGPandoraBox *) factory->create(ObjectTemplate());
std::vector <CSpell *> spells;
for (auto spell : VLC->spellh->objects)
@ -2610,9 +2608,8 @@ void CRmgTemplateZone::addAllPossibleObjects(CMapGenerator* gen)
{
oi.generateObject = [i,gen]() -> CGObjectInstance *
{
auto obj = new CGPandoraBox();
obj->ID = Obj::PANDORAS_BOX;
obj->subID = 0;
auto factory = VLC->objtypeh->getHandlerFor(Obj::PANDORAS_BOX, 0);
auto obj = (CGPandoraBox *) factory->create(ObjectTemplate());
std::vector <CSpell *> spells;
for (auto spell : VLC->spellh->objects)
@ -2640,9 +2637,8 @@ void CRmgTemplateZone::addAllPossibleObjects(CMapGenerator* gen)
oi.generateObject = [gen]() -> CGObjectInstance *
{
auto obj = new CGPandoraBox();
obj->ID = Obj::PANDORAS_BOX;
obj->subID = 0;
auto factory = VLC->objtypeh->getHandlerFor(Obj::PANDORAS_BOX, 0);
auto obj = (CGPandoraBox *) factory->create(ObjectTemplate());
std::vector <CSpell *> spells;
for (auto spell : VLC->spellh->objects)
@ -2713,9 +2709,8 @@ void CRmgTemplateZone::addAllPossibleObjects(CMapGenerator* gen)
oi.generateObject = [creature, creaturesAmount, randomAppearance, gen, this, generateArtInfo]() -> CGObjectInstance *
{
auto obj = new CGSeerHut();
obj->ID = Obj::SEER_HUT;
obj->subID = randomAppearance;
auto factory = VLC->objtypeh->getHandlerFor(Obj::SEER_HUT, randomAppearance);
auto obj = (CGSeerHut *) factory->create(ObjectTemplate());
obj->rewardType = CGSeerHut::CREATURE;
obj->rID = creature->idNumber;
obj->rVal = creaturesAmount;
@ -2752,9 +2747,9 @@ void CRmgTemplateZone::addAllPossibleObjects(CMapGenerator* gen)
oi.generateObject = [i, randomAppearance, gen, this, generateArtInfo]() -> CGObjectInstance *
{
auto obj = new CGSeerHut();
obj->ID = Obj::SEER_HUT;
obj->subID = randomAppearance;
auto factory = VLC->objtypeh->getHandlerFor(Obj::SEER_HUT, randomAppearance);
auto obj = (CGSeerHut *) factory->create(ObjectTemplate());
obj->rewardType = CGSeerHut::EXPERIENCE;
obj->rID = 0; //unitialized?
obj->rVal = seerExpGold[i];
@ -2777,9 +2772,8 @@ void CRmgTemplateZone::addAllPossibleObjects(CMapGenerator* gen)
oi.generateObject = [i, randomAppearance, gen, this, generateArtInfo]() -> CGObjectInstance *
{
auto obj = new CGSeerHut();
obj->ID = Obj::SEER_HUT;
obj->subID = randomAppearance;
auto factory = VLC->objtypeh->getHandlerFor(Obj::SEER_HUT, randomAppearance);
auto obj = (CGSeerHut *) factory->create(ObjectTemplate());
obj->rewardType = CGSeerHut::RESOURCES;
obj->rID = Res::GOLD;
obj->rVal = seerExpGold[i];

View File

@ -0,0 +1,163 @@
/*
* JsonDeserializer.cpp, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#include "StdInc.h"
#include "JsonDeserializer.h"
#include "../JsonNode.h"
JsonDeserializer::JsonDeserializer(JsonNode & root_):
JsonSerializeFormat(root_, false)
{
}
void JsonDeserializer::serializeBool(const std::string & fieldName, bool & value)
{
value = current->operator[](fieldName).Bool();
}
void JsonDeserializer::serializeEnum(const std::string & fieldName, const std::string & trueValue, const std::string & falseValue, bool & value)
{
const JsonNode & tmp = current->operator[](fieldName);
value = tmp.String() == trueValue;
}
void JsonDeserializer::serializeFloat(const std::string & fieldName, double & value)
{
value = current->operator[](fieldName).Float();
}
void JsonDeserializer::serializeIntEnum(const std::string & fieldName, const std::vector<std::string> & enumMap, const si32 defaultValue, si32 & value)
{
const std::string & valueName = current->operator[](fieldName).String();
si32 rawValue = vstd::find_pos(enumMap, valueName);
if(rawValue < 0)
value = defaultValue;
else
value = rawValue;
}
void JsonDeserializer::serializeIntId(const std::string & fieldName, const TDecoder & decoder, const TEncoder & encoder, const si32 defaultValue, si32 & value)
{
std::string identifier;
serializeString(fieldName, identifier);
if(identifier == "")
{
value = defaultValue;
return;
}
si32 rawId = decoder(identifier);
if(rawId >= 0)
value = rawId;
else
value = defaultValue;
}
void JsonDeserializer::serializeLIC(const std::string & fieldName, const TDecoder & decoder, const TEncoder & encoder, const std::vector<bool> & standard, std::vector<bool> & value)
{
const JsonNode & field = current->operator[](fieldName);
if(field.isNull())
return;
const JsonNode & anyOf = field["anyOf"];
const JsonNode & allOf = field["allOf"];
const JsonNode & noneOf = field["noneOf"];
if(anyOf.Vector().empty() && allOf.Vector().empty())
{
//permissive mode
value = standard;
}
else
{
//restrictive mode
value.clear();
value.resize(standard.size(), false);
readLICPart(anyOf, decoder, true, value);
readLICPart(allOf, decoder, true, value);
}
readLICPart(noneOf, decoder, false, value);
}
void JsonDeserializer::serializeLIC(const std::string & fieldName, LIC & value)
{
const JsonNode & field = current->operator[](fieldName);
const JsonNode & anyOf = field["anyOf"];
const JsonNode & allOf = field["allOf"];
const JsonNode & noneOf = field["noneOf"];
if(anyOf.Vector().empty())
{
//permissive mode
value.any = value.standard;
}
else
{
//restrictive mode
value.any.clear();
value.any.resize(value.standard.size(), false);
readLICPart(anyOf, value.decoder, true, value.any);
}
readLICPart(allOf, value.decoder, true, value.all);
readLICPart(noneOf, value.decoder, true, value.none);
//remove any banned from allowed and required
for(si32 idx = 0; idx < value.none.size(); idx++)
{
if(value.none[idx])
{
value.all[idx] = false;
value.any[idx] = false;
}
}
//add all required to allowed
for(si32 idx = 0; idx < value.all.size(); idx++)
{
if(value.all[idx])
{
value.any[idx] = true;
}
}
}
void JsonDeserializer::serializeString(const std::string & fieldName, std::string & value)
{
value = current->operator[](fieldName).String();
}
void JsonDeserializer::readLICPart(const JsonNode & part, const TDecoder & decoder, const bool val, std::vector<bool> & value)
{
for(size_t index = 0; index < part.Vector().size(); index++)
{
const std::string & identifier = part.Vector()[index].String();
const si32 rawId = decoder(identifier);
if(rawId >= 0)
{
if(rawId < value.size())
value[rawId] = val;
else
logGlobal->errorStream() << "JsonDeserializer::serializeLIC: id out of bounds " << rawId;
}
}
}

View File

@ -0,0 +1,34 @@
/*
* JsonDeserializer.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 "JsonSerializeFormat.h"
class JsonNode;
class JsonDeserializer: public JsonSerializeFormat
{
public:
JsonDeserializer(JsonNode & root_);
void serializeBool(const std::string & fieldName, bool & value) override;
void serializeEnum(const std::string & fieldName, const std::string & trueValue, const std::string & falseValue, bool & value) override;
void serializeLIC(const std::string & fieldName, const TDecoder & decoder, const TEncoder & encoder, const std::vector<bool> & standard, std::vector<bool> & value) override;
void serializeLIC(const std::string & fieldName, LIC & value) override;
void serializeString(const std::string & fieldName, std::string & value) override;
protected:
void serializeFloat(const std::string & fieldName, double & value) override;
void serializeIntEnum(const std::string & fieldName, const std::vector<std::string> & enumMap, const si32 defaultValue, si32 & value) override;
void serializeIntId(const std::string & fieldName, const TDecoder & decoder, const TEncoder & encoder, const si32 defaultValue, si32 & value) override;
private:
void readLICPart(const JsonNode & part, const TDecoder & decoder, const bool val, std::vector<bool> & value);
};

View File

@ -0,0 +1,90 @@
/*
* JsonSerializeFormat.cpp, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#include "StdInc.h"
#include "JsonSerializeFormat.h"
#include "../JsonNode.h"
//JsonStructSerializer
JsonStructSerializer::JsonStructSerializer(JsonStructSerializer&& other):
restoreState(false),
owner(other.owner),
parentNode(other.parentNode),
thisNode(other.thisNode)
{
}
JsonStructSerializer::~JsonStructSerializer()
{
if(restoreState)
owner.current = parentNode;
}
JsonStructSerializer::JsonStructSerializer(JsonSerializeFormat& owner_, const std::string& fieldName):
restoreState(true),
owner(owner_),
parentNode(owner.current),
thisNode(&(parentNode->operator[](fieldName)))
{
owner.current = thisNode;
}
JsonStructSerializer::JsonStructSerializer(JsonStructSerializer & parent, const std::string & fieldName):
restoreState(true),
owner(parent.owner),
parentNode(parent.thisNode),
thisNode(&(parentNode->operator[](fieldName)))
{
owner.current = thisNode;
}
JsonStructSerializer JsonStructSerializer::enterStruct(const std::string & fieldName)
{
return JsonStructSerializer(*this, fieldName);
}
JsonNode& JsonStructSerializer::get()
{
return *thisNode;
}
JsonSerializeFormat * JsonStructSerializer::operator->()
{
return &owner;
}
JsonSerializeFormat::LIC::LIC(const std::vector<bool> & Standard, const TDecoder & Decoder, const TEncoder & Encoder):
standard(Standard), decoder(Decoder), encoder(Encoder)
{
any.resize(standard.size(), false);
all.resize(standard.size(), false);
none.resize(standard.size(), false);
}
//JsonSerializeFormat
JsonSerializeFormat::JsonSerializeFormat(JsonNode & root_, const bool saving_):
saving(saving_),
root(&root_),
current(root)
{
}
JsonStructSerializer JsonSerializeFormat::enterStruct(const std::string & fieldName)
{
JsonStructSerializer res(*this, fieldName);
return res;
}

View File

@ -0,0 +1,148 @@
/*
* JsonSerializeFormat.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
class JsonNode;
class JsonSerializeFormat;
class JsonStructSerializer: public boost::noncopyable
{
public:
JsonStructSerializer(JsonStructSerializer && other);
virtual ~JsonStructSerializer();
JsonStructSerializer enterStruct(const std::string & fieldName);
JsonNode & get();
JsonSerializeFormat * operator->();
private:
JsonStructSerializer(JsonSerializeFormat & owner_, const std::string & fieldName);
JsonStructSerializer(JsonStructSerializer & parent, const std::string & fieldName);
bool restoreState;
JsonSerializeFormat & owner;
JsonNode * parentNode;
JsonNode * thisNode;
friend class JsonSerializeFormat;
};
class JsonSerializeFormat: public boost::noncopyable
{
public:
///user-provided callback to resolve string identifier
///returns resolved identifier or -1 on error
typedef std::function<si32(const std::string &)> TDecoder;
///user-provided callback to get string identifier
///may assume that object index is valid
typedef std::function<std::string(si32)> TEncoder;
struct LIC
{
LIC(const std::vector<bool> & Standard, const TDecoder & Decoder, const TEncoder & Encoder);
const std::vector<bool> & standard;
const TDecoder & decoder;
const TEncoder & encoder;
std::vector<bool> all, any, none;
};
const bool saving;
JsonSerializeFormat() = delete;
virtual ~JsonSerializeFormat() = default;
JsonNode & getRoot()
{
return * root;
};
JsonNode & getCurrent()
{
return * current;
};
JsonStructSerializer enterStruct(const std::string & fieldName);
template <typename T>
void serializeBool(const std::string & fieldName, const T trueValue, const T falseValue, T & value)
{
bool temp = (value == trueValue);
serializeBool(fieldName, temp);
if(!saving)
value = temp ? trueValue : falseValue;
}
virtual void serializeBool(const std::string & fieldName, bool & value) = 0;
virtual void serializeEnum(const std::string & fieldName, const std::string & trueValue, const std::string & falseValue, bool & value) = 0;
/** @brief Restrictive ("anyOf") simple serialization of Logical identifier condition, simple deserialization (allOf=anyOf)
*
* @param fieldName
* @param decoder resolve callback, should report errors itself and do not throw
* @param encoder encode callback, should report errors itself and do not throw
* @param value target value, must be resized properly
*
*/
virtual void serializeLIC(const std::string & fieldName, const TDecoder & decoder, const TEncoder & encoder, const std::vector<bool> & standard, std::vector<bool> & value) = 0;
/** @brief Complete serialization of Logical identifier condition
*/
virtual void serializeLIC(const std::string & fieldName, LIC & value) = 0;
template <typename T>
void serializeNumericEnum(const std::string & fieldName, const std::vector<std::string> & enumMap, const T defaultValue, T & value)
{
si32 temp = value;
serializeIntEnum(fieldName, enumMap, defaultValue, temp);
if(!saving)
value = temp;
};
template <typename T>
void serializeNumeric(const std::string & fieldName, T & value)
{
double temp = value;
serializeFloat(fieldName, temp);
if(!saving)
value = temp;
};
virtual void serializeString(const std::string & fieldName, std::string & value) = 0;
template <typename T>
void serializeId(const std::string & fieldName, const TDecoder & decoder, const TEncoder & encoder, const T & defaultValue, T & value)
{
const si32 tempDefault = defaultValue.num;
si32 tempValue = value.num;
serializeIntId(fieldName, decoder, encoder, tempDefault, tempValue);
if(!saving)
value = T(tempValue);
}
protected:
JsonNode * root;
JsonNode * current;
JsonSerializeFormat(JsonNode & root_, const bool saving_);
virtual void serializeFloat(const std::string & fieldName, double & value) = 0;
virtual void serializeIntEnum(const std::string & fieldName, const std::vector<std::string> & enumMap, const si32 defaultValue, si32 & value) = 0;
virtual void serializeIntId(const std::string & fieldName, const TDecoder & decoder, const TEncoder & encoder, const si32 defaultValue, si32 & value) = 0;
private:
friend class JsonStructSerializer;
};

View File

@ -0,0 +1,94 @@
/*
* JsonSerializer.cpp, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#include "StdInc.h"
#include "JsonSerializer.h"
#include "../JsonNode.h"
JsonSerializer::JsonSerializer(JsonNode & root_):
JsonSerializeFormat(root_, true)
{
}
void JsonSerializer::serializeBool(const std::string & fieldName, bool & value)
{
if(value)
current->operator[](fieldName).Bool() = true;
}
void JsonSerializer::serializeEnum(const std::string & fieldName, const std::string & trueValue, const std::string & falseValue, bool & value)
{
current->operator[](fieldName).String() = value ? trueValue : falseValue;
}
void JsonSerializer::serializeFloat(const std::string & fieldName, double & value)
{
if(value != 0)
current->operator[](fieldName).Float() = value;
}
void JsonSerializer::serializeIntEnum(const std::string & fieldName, const std::vector<std::string> & enumMap, const si32 defaultValue, si32 & value)
{
if(defaultValue != value)
current->operator[](fieldName).String() = enumMap.at(value);
}
void JsonSerializer::serializeIntId(const std::string & fieldName, const TDecoder & decoder, const TEncoder & encoder, const si32 defaultValue, si32 & value)
{
if(defaultValue != value)
{
std::string identifier = encoder(value);
serializeString(fieldName, identifier);
}
}
void JsonSerializer::serializeLIC(const std::string & fieldName, const TDecoder & decoder, const TEncoder & encoder, const std::vector<bool> & standard, std::vector<bool> & value)
{
assert(standard.size() == value.size());
if(standard == value)
return;
writeLICPart(fieldName, "anyOf", encoder, value);
}
void JsonSerializer::serializeLIC(const std::string & fieldName, LIC & value)
{
if(value.any != value.standard)
{
writeLICPart(fieldName, "anyOf", value.encoder, value.any);
}
writeLICPart(fieldName, "allOf", value.encoder, value.all);
writeLICPart(fieldName, "noneOf", value.encoder, value.none);
}
void JsonSerializer::serializeString(const std::string & fieldName, std::string & value)
{
if(value != "")
current->operator[](fieldName).String() = value;
}
void JsonSerializer::writeLICPart(const std::string& fieldName, const std::string& partName, const TEncoder& encoder, const std::vector<bool> & data)
{
auto & target = current->operator[](fieldName)[partName].Vector();
for(si32 idx = 0; idx < data.size(); idx ++)
{
if(data[idx])
{
JsonNode val(JsonNode::DATA_STRING);
val.String() = encoder(idx);
target.push_back(std::move(val));
}
}
}

View File

@ -0,0 +1,35 @@
/*
* JsonSerializer.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 "JsonSerializeFormat.h"
class JsonNode;
class JsonSerializer: public JsonSerializeFormat
{
public:
JsonSerializer(JsonNode & root_);
void serializeBool(const std::string & fieldName, bool & value) override;
void serializeEnum(const std::string & fieldName, const std::string & trueValue, const std::string & falseValue, bool & value) override;
void serializeLIC(const std::string & fieldName, const TDecoder & decoder, const TEncoder & encoder, const std::vector<bool> & standard, std::vector<bool> & value) override;
void serializeLIC(const std::string & fieldName, LIC & value) override;
void serializeString(const std::string & fieldName, std::string & value) override;
protected:
void serializeFloat(const std::string & fieldName, double & value) override;
void serializeIntEnum(const std::string & fieldName, const std::vector<std::string> & enumMap, const si32 defaultValue, si32 & value) override;
void serializeIntId(const std::string & fieldName, const TDecoder & decoder, const TEncoder & encoder, const si32 defaultValue, si32 & value) override;
private:
void writeLICPart(const std::string & fieldName, const std::string & partName, const TEncoder & encoder, const std::vector<bool> & data);
};

View File

@ -799,11 +799,12 @@ const std::string CSpellHandler::getTypeName() const
return "spell";
}
CSpell * CSpellHandler::loadFromJson(const JsonNode & json)
CSpell * CSpellHandler::loadFromJson(const JsonNode & json, const std::string & identifier)
{
using namespace SpellConfig;
CSpell * spell = new CSpell();
spell->identifier = identifier;
const auto type = json["type"].String();
@ -1077,6 +1078,27 @@ CSpellHandler::~CSpellHandler()
std::vector<bool> CSpellHandler::getDefaultAllowed() const
{
std::vector<bool> allowedSpells;
allowedSpells.resize(GameConstants::SPELLS_QUANTITY, true);
allowedSpells.reserve(objects.size());
for(const CSpell * s : objects)
{
allowedSpells.push_back( !(s->isSpecialSpell() || s->isCreatureAbility()));
}
return allowedSpells;
}
si32 CSpellHandler::decodeSpell(const std::string& identifier)
{
auto rawId = VLC->modh->identifiers.getIdentifier("core", "spell", identifier);
if(rawId)
return rawId.get();
else
return -1;
}
std::string CSpellHandler::encodeSpell(const si32 index)
{
return VLC->spellh->objects[index]->identifier;
}

View File

@ -172,7 +172,7 @@ public:
};
SpellID id;
std::string identifier; //???
std::string identifier;
std::string name;
si32 level;
@ -360,11 +360,17 @@ public:
const std::string getTypeName() const override;
///json serialization helper
static si32 decodeSpell(const std::string & identifier);
///json serialization helper
static std::string encodeSpell(const si32 index);
template <typename Handler> void serialize(Handler &h, const int version)
{
h & objects ;
}
protected:
CSpell * loadFromJson(const JsonNode & json) override;
CSpell * loadFromJson(const JsonNode & json, const std::string & identifier) override;
};

View File

@ -8,6 +8,8 @@ set(test_SRCS
StdInc.cpp
CVcmiTestConfig.cpp
CMapEditManagerTest.cpp
MapComparer.cpp
CMapFormatTest.cpp
)
add_executable(vcmitest ${test_SRCS})

View File

@ -12,11 +12,9 @@
#include "StdInc.h"
#include <boost/test/unit_test.hpp>
#include "../lib/filesystem/ResourceID.h"
#include "../lib/mapping/CMapService.h"
#include "../lib/mapping/CMap.h"
#include "../lib/filesystem/Filesystem.h"
#include "../lib/filesystem/CFilesystemLoader.h"
#include "../lib/filesystem/AdapterLoaders.h"
#include "../lib/JsonNode.h"
#include "../lib/mapping/CMapEditManager.h"
#include "../lib/int3.h"
@ -25,11 +23,12 @@
BOOST_AUTO_TEST_CASE(CMapEditManager_DrawTerrain_Type)
{
logGlobal->infoStream() << "CMapEditManager_DrawTerrain_Type start";
try
{
auto map = make_unique<CMap>();
map->width = 100;
map->height = 100;
map->width = 52;
map->height = 52;
map->initTerrain();
auto editManager = map->getEditManager();
editManager->clearTerrain();
@ -81,6 +80,9 @@ BOOST_AUTO_TEST_CASE(CMapEditManager_DrawTerrain_Type)
editManager->drawTerrain(ETerrainType::ROCK);
BOOST_CHECK(map->getTile(int3(5, 6, 1)).terType == ETerrainType::ROCK || map->getTile(int3(7, 8, 1)).terType == ETerrainType::ROCK);
//todo: add checks here and enable, also use smaller size
#if 0
// next check
auto map2 = make_unique<CMap>();
map2->width = 128;
@ -96,29 +98,23 @@ BOOST_AUTO_TEST_CASE(CMapEditManager_DrawTerrain_Type)
int3(98, 46, 1), int3(99, 46, 1)});
editManager2->getTerrainSelection().setSelection(selection);
editManager2->drawTerrain(ETerrainType::ROCK);
#endif // 0
}
catch(const std::exception & e)
{
logGlobal-> errorStream() << e.what();
logGlobal->errorStream() << "CMapEditManager_DrawTerrain_Type crash" << "\n" << e.what();
throw;
}
logGlobal->infoStream() << "CMapEditManager_DrawTerrain_Type finish";
}
BOOST_AUTO_TEST_CASE(CMapEditManager_DrawTerrain_View)
{
logGlobal->infoStream() << "CMapEditManager_DrawTerrain_View start";
try
{
// Load maps and json config
#if defined(__MINGW32__)
const std::string TEST_DATA_DIR = "test/";
#else
const std::string TEST_DATA_DIR = ".";
#endif // defined
auto loader = new CFilesystemLoader("test/", TEST_DATA_DIR);
dynamic_cast<CFilesystemList*>(CResourceHandler::get())->addLoader(loader, false);
const auto originalMap = CMapService::loadMap("test/TerrainViewTest");
auto map = CMapService::loadMap("test/TerrainViewTest");
logGlobal->infoStream() << "Loaded test map successfully.";
@ -149,8 +145,10 @@ BOOST_AUTO_TEST_CASE(CMapEditManager_DrawTerrain_View)
const auto & posVector = posNode.Vector();
if(posVector.size() != 3) throw std::runtime_error("A position should consist of three values x,y,z. Continue with next position.");
int3 pos(posVector[0].Float(), posVector[1].Float(), posVector[2].Float());
logGlobal->infoStream() << boost::format("Test pattern '%s' on position x '%d', y '%d', z '%d'.") % patternStr % pos.x % pos.y % pos.z;
#if 0
logGlobal->traceStream() << boost::format("Test pattern '%s' on position x '%d', y '%d', z '%d'.") % patternStr % pos.x % pos.y % pos.z;
CTerrainViewPatternUtils::printDebuggingInfoAboutTile(map.get(), pos);
#endif // 0
const auto & originalTile = originalMap->getTile(pos);
editManager->getTerrainSelection().selectRange(MapRect(pos, 1, 1));
editManager->drawTerrain(originalTile.terType, &gen);
@ -171,6 +169,8 @@ BOOST_AUTO_TEST_CASE(CMapEditManager_DrawTerrain_View)
}
catch(const std::exception & e)
{
logGlobal-> errorStream() << e.what();
logGlobal->infoStream() << "CMapEditManager_DrawTerrain_View crash"<< "\n" << e.what();
throw;
}
logGlobal->infoStream() << "CMapEditManager_DrawTerrain_View finish";
}

97
test/CMapFormatTest.cpp Normal file
View File

@ -0,0 +1,97 @@
/*
* CMapFormatTest.cpp, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#include "StdInc.h"
#include <boost/test/unit_test.hpp>
#include "../lib/filesystem/CMemoryBuffer.h"
#include "../lib/mapping/CMap.h"
#include "../lib/rmg/CMapGenOptions.h"
#include "../lib/rmg/CMapGenerator.h"
#include "../lib/mapping/MapFormatJson.h"
#include "../lib/VCMIDirs.h"
#include "MapComparer.h"
static const int TEST_RANDOM_SEED = 1337;
static std::unique_ptr<CMap> initialMap;
class CMapTestFixture
{
public:
CMapTestFixture()
{
CMapGenOptions opt;
opt.setHeight(CMapHeader::MAP_SIZE_MIDDLE);
opt.setWidth(CMapHeader::MAP_SIZE_MIDDLE);
opt.setHasTwoLevels(true);
opt.setPlayerCount(4);
opt.setPlayerTypeForStandardPlayer(PlayerColor(0), EPlayerType::HUMAN);
opt.setPlayerTypeForStandardPlayer(PlayerColor(1), EPlayerType::AI);
opt.setPlayerTypeForStandardPlayer(PlayerColor(2), EPlayerType::AI);
opt.setPlayerTypeForStandardPlayer(PlayerColor(3), EPlayerType::AI);
CMapGenerator gen;
initialMap = gen.generate(&opt, TEST_RANDOM_SEED);
initialMap->name = "Test";
};
~CMapTestFixture()
{
initialMap.reset();
};
};
BOOST_GLOBAL_FIXTURE(CMapTestFixture);
BOOST_AUTO_TEST_CASE(CMapFormatVCMI_Simple)
{
logGlobal->info("CMapFormatVCMI_Simple start");
BOOST_TEST_CHECKPOINT("CMapFormatVCMI_Simple start");
CMemoryBuffer serializeBuffer;
{
CMapSaverJson saver(&serializeBuffer);
saver.saveMap(initialMap);
}
BOOST_TEST_CHECKPOINT("CMapFormatVCMI_Simple serialized");
#if 1
{
auto path = VCMIDirs::get().userDataPath()/"test.vmap";
boost::filesystem::remove(path);
boost::filesystem::ofstream tmp(path, boost::filesystem::ofstream::binary);
tmp.write((const char *)serializeBuffer.getBuffer().data(),serializeBuffer.getSize());
tmp.flush();
tmp.close();
logGlobal->infoStream() << "Test map has been saved to " << path;
}
BOOST_TEST_CHECKPOINT("CMapFormatVCMI_Simple saved");
#endif // 1
serializeBuffer.seek(0);
{
CMapLoaderJson loader(&serializeBuffer);
std::unique_ptr<CMap> serialized = loader.loadMap();
MapComparer c;
c(serialized, initialMap);
}
logGlobal->info("CMapFormatVCMI_Simple finish");
}

View File

@ -0,0 +1,52 @@
/*
* CMemoryBufferTest.cpp, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#include "StdInc.h"
#include <boost/test/unit_test.hpp>
#include "../lib/filesystem/CMemoryBuffer.h"
struct CMemoryBufferFixture
{
CMemoryBuffer subject;
};
BOOST_FIXTURE_TEST_CASE(CMemoryBuffer_Empty, CMemoryBufferFixture)
{
BOOST_REQUIRE_EQUAL(0, subject.tell());
BOOST_REQUIRE_EQUAL(0, subject.getSize());
si32 dummy = 1337;
auto ret = subject.read((ui8 *)&dummy, sizeof(si32));
BOOST_CHECK_EQUAL(0, ret);
BOOST_CHECK_EQUAL(1337, dummy);
BOOST_CHECK_EQUAL(0, subject.tell());
}
BOOST_FIXTURE_TEST_CASE(CMemoryBuffer_Write, CMemoryBufferFixture)
{
const si32 initial = 1337;
subject.write((const ui8 *)&initial, sizeof(si32));
BOOST_CHECK_EQUAL(4, subject.tell());
subject.seek(0);
BOOST_CHECK_EQUAL(0, subject.tell());
si32 current = 0;
auto ret = subject.read((ui8 *)&current, sizeof(si32));
BOOST_CHECK_EQUAL(sizeof(si32), ret);
BOOST_CHECK_EQUAL(initial, current);
BOOST_CHECK_EQUAL(4, subject.tell());
}

View File

@ -18,6 +18,9 @@
#include "../lib/VCMI_Lib.h"
#include "../lib/logging/CLogger.h"
#include "../lib/CConfigHandler.h"
#include "../lib/filesystem/Filesystem.h"
#include "../lib/filesystem/CFilesystemLoader.h"
#include "../lib/filesystem/AdapterLoaders.h"
CVcmiTestConfig::CVcmiTestConfig()
{
@ -29,6 +32,11 @@ CVcmiTestConfig::CVcmiTestConfig()
logConfig.configure();
loadDLLClasses();
logGlobal->infoStream() << "Initialized global test setup.";
const std::string TEST_DATA_DIR = "test/";
auto loader = new CFilesystemLoader("test/", TEST_DATA_DIR);
dynamic_cast<CFilesystemList*>(CResourceHandler::get())->addLoader(loader, false);
}
CVcmiTestConfig::~CVcmiTestConfig()

274
test/MapComparer.cpp Normal file
View File

@ -0,0 +1,274 @@
/*
* MapComparer.cpp, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#include "StdInc.h"
#include <boost/test/unit_test.hpp>
#include "MapComparer.h"
#define VCMI_CHECK_FIELD_EQUAL_P(field) BOOST_CHECK_EQUAL(actual->field, expected->field)
#define VCMI_CHECK_FIELD_EQUAL(field) BOOST_CHECK_EQUAL(actual.field, expected.field)
#define VCMI_REQUIRE_FIELD_EQUAL_P(field) BOOST_REQUIRE_EQUAL(actual->field, expected->field)
#define VCMI_REQUIRE_FIELD_EQUAL(field) BOOST_REQUIRE_EQUAL(actual.field, expected.field)
template <class T>
void checkEqual(const T & actual, const T & expected)
{
BOOST_CHECK_EQUAL(actual, expected) ;
}
void checkEqual(const std::vector<bool> & actual, const std::vector<bool> & expected)
{
BOOST_CHECK_EQUAL(actual.size(), expected.size());
for(auto actualIt = actual.begin(), expectedIt = expected.begin(); actualIt != actual.end() && expectedIt != expected.end(); actualIt++, expectedIt++)
{
checkEqual(*actualIt, *expectedIt);
}
}
template <class Element>
void checkEqual(const std::vector<Element> & actual, const std::vector<Element> & expected)
{
BOOST_CHECK_EQUAL(actual.size(), expected.size());
for(auto actualIt = actual.begin(), expectedIt = expected.begin(); actualIt != actual.end() && expectedIt != expected.end(); actualIt++, expectedIt++)
{
checkEqual(*actualIt, *expectedIt);
}
}
template <class Element>
void checkEqual(const std::set<Element> & actual, const std::set<Element> & expected)
{
BOOST_CHECK_EQUAL(actual.size(), expected.size());
for(auto elem : expected)
{
if(!vstd::contains(actual, elem))
BOOST_ERROR("Required element not found "+boost::to_string(elem));
}
}
void checkEqual(const SHeroName & actual, const SHeroName & expected)
{
VCMI_CHECK_FIELD_EQUAL(heroId);
VCMI_CHECK_FIELD_EQUAL(heroName);
}
void checkEqual(const PlayerInfo & actual, const PlayerInfo & expected)
{
VCMI_CHECK_FIELD_EQUAL(canHumanPlay);
VCMI_CHECK_FIELD_EQUAL(canComputerPlay);
VCMI_CHECK_FIELD_EQUAL(aiTactic);
checkEqual(actual.allowedFactions, expected.allowedFactions);
VCMI_CHECK_FIELD_EQUAL(isFactionRandom);
VCMI_CHECK_FIELD_EQUAL(mainCustomHeroPortrait);
VCMI_CHECK_FIELD_EQUAL(mainCustomHeroName);
VCMI_CHECK_FIELD_EQUAL(mainCustomHeroId);
checkEqual(actual.heroesNames, expected.heroesNames);
VCMI_CHECK_FIELD_EQUAL(hasMainTown);
VCMI_CHECK_FIELD_EQUAL(generateHeroAtMainTown);
VCMI_CHECK_FIELD_EQUAL(posOfMainTown);
VCMI_CHECK_FIELD_EQUAL(team);
VCMI_CHECK_FIELD_EQUAL(hasRandomHero);
}
void checkEqual(const EventExpression & actual, const EventExpression & expected)
{
//todo: checkEventExpression
}
void checkEqual(const TriggeredEvent & actual, const TriggeredEvent & expected)
{
VCMI_CHECK_FIELD_EQUAL(identifier);
VCMI_CHECK_FIELD_EQUAL(description);
VCMI_CHECK_FIELD_EQUAL(onFulfill);
VCMI_CHECK_FIELD_EQUAL(effect.type);
VCMI_CHECK_FIELD_EQUAL(effect.toOtherMessage);
checkEqual(actual.trigger, expected.trigger);
}
void checkEqual(const Rumor & actual, const Rumor & expected)
{
VCMI_CHECK_FIELD_EQUAL(name);
VCMI_CHECK_FIELD_EQUAL(text);
}
void checkEqual(const DisposedHero & actual, const DisposedHero & expected)
{
VCMI_CHECK_FIELD_EQUAL(heroId);
VCMI_CHECK_FIELD_EQUAL(portrait);
VCMI_CHECK_FIELD_EQUAL(name);
VCMI_CHECK_FIELD_EQUAL(players);
}
void checkEqual(const ObjectTemplate & actual, const ObjectTemplate & expected)
{
VCMI_CHECK_FIELD_EQUAL(id);
VCMI_CHECK_FIELD_EQUAL(subid);
VCMI_CHECK_FIELD_EQUAL(printPriority);
VCMI_CHECK_FIELD_EQUAL(animationFile);
//VCMI_CHECK_FIELD_EQUAL(stringID);
}
void checkEqual(const TerrainTile & actual, const TerrainTile & expected)
{
//fatal fail here on any error
VCMI_REQUIRE_FIELD_EQUAL(terType);
VCMI_REQUIRE_FIELD_EQUAL(terView);
VCMI_REQUIRE_FIELD_EQUAL(riverType);
VCMI_REQUIRE_FIELD_EQUAL(riverDir);
VCMI_REQUIRE_FIELD_EQUAL(roadType);
VCMI_REQUIRE_FIELD_EQUAL(roadDir);
VCMI_REQUIRE_FIELD_EQUAL(extTileFlags);
BOOST_REQUIRE_EQUAL(actual.blockingObjects.size(), expected.blockingObjects.size());
BOOST_REQUIRE_EQUAL(actual.visitableObjects.size(), expected.visitableObjects.size());
VCMI_REQUIRE_FIELD_EQUAL(visitable);
VCMI_REQUIRE_FIELD_EQUAL(blocked);
}
void MapComparer::compareHeader()
{
//map size parameters are vital for further checks
VCMI_REQUIRE_FIELD_EQUAL_P(height);
VCMI_REQUIRE_FIELD_EQUAL_P(width);
VCMI_REQUIRE_FIELD_EQUAL_P(twoLevel);
VCMI_CHECK_FIELD_EQUAL_P(name);
VCMI_CHECK_FIELD_EQUAL_P(description);
VCMI_CHECK_FIELD_EQUAL_P(difficulty);
VCMI_CHECK_FIELD_EQUAL_P(levelLimit);
VCMI_CHECK_FIELD_EQUAL_P(victoryMessage);
VCMI_CHECK_FIELD_EQUAL_P(defeatMessage);
VCMI_CHECK_FIELD_EQUAL_P(victoryIconIndex);
VCMI_CHECK_FIELD_EQUAL_P(defeatIconIndex);
VCMI_CHECK_FIELD_EQUAL_P(howManyTeams);
checkEqual(actual->players, expected->players);
checkEqual(actual->allowedHeroes, expected->allowedHeroes);
std::vector<TriggeredEvent> actualEvents = actual->triggeredEvents;
std::vector<TriggeredEvent> expectedEvents = expected->triggeredEvents;
auto sortByIdentifier = [](const TriggeredEvent & lhs, const TriggeredEvent & rhs) -> bool
{
return lhs.identifier < rhs.identifier;
};
boost::sort (actualEvents, sortByIdentifier);
boost::sort (expectedEvents, sortByIdentifier);
checkEqual(actualEvents, expectedEvents);
}
void MapComparer::compareOptions()
{
checkEqual(actual->rumors, expected->rumors);
checkEqual(actual->disposedHeroes, expected->disposedHeroes);
//todo: compareOptions predefinedHeroes
checkEqual(actual->allowedAbilities, expected->allowedAbilities);
checkEqual(actual->allowedArtifact, expected->allowedArtifact);
checkEqual(actual->allowedSpell, expected->allowedSpell);
//checkEqual(actual->allowedAbilities, expected->allowedAbilities);
//todo: compareOptions events
}
void MapComparer::compareObject(const CGObjectInstance * actual, const CGObjectInstance * expected)
{
BOOST_CHECK_EQUAL(actual->instanceName, expected->instanceName);
BOOST_CHECK_EQUAL(typeid(actual).name(), typeid(expected).name());//todo: remove and use just comparison
std::string actualFullID = boost::to_string(boost::format("%s(%d)|%s(%d) %d") % actual->typeName % actual->ID % actual->subTypeName % actual->subID % actual->tempOwner);
std::string expectedFullID = boost::to_string(boost::format("%s(%d)|%s(%d) %d") % expected->typeName % expected->ID % expected->subTypeName % expected->subID % expected->tempOwner);
BOOST_CHECK_EQUAL(actualFullID, expectedFullID);
VCMI_CHECK_FIELD_EQUAL_P(pos);
checkEqual(actual->appearance, expected->appearance);
}
void MapComparer::compareObjects()
{
BOOST_CHECK_EQUAL(actual->objects.size(), expected->objects.size());
for(size_t idx = 0; idx < expected->objects.size(); idx++)
{
auto expectedObject = expected->objects[idx];
BOOST_REQUIRE_EQUAL(idx, expectedObject->id.getNum());
{
auto it = expected->instanceNames.find(expectedObject->instanceName);
BOOST_REQUIRE(it != expected->instanceNames.end());
}
{
auto it = actual->instanceNames.find(expectedObject->instanceName);
BOOST_REQUIRE(it != expected->instanceNames.end());
auto actualObject = it->second;
compareObject(actualObject, expectedObject);
}
}
}
void MapComparer::compareTerrain()
{
//assume map dimensions check passed
//todo: separate check for underground
for(int x = 0; x < expected->width; x++)
for(int y = 0; y < expected->height; y++)
{
int3 coord(x,y,0);
BOOST_TEST_CHECKPOINT(coord);
checkEqual(actual->getTile(coord), expected->getTile(coord));
}
}
void MapComparer::compare()
{
BOOST_REQUIRE_NE((void *) actual, (void *) expected); //should not point to the same object
BOOST_REQUIRE_MESSAGE(actual != nullptr, "Actual map is not defined");
BOOST_REQUIRE_MESSAGE(expected != nullptr, "Expected map is not defined");
compareHeader();
compareOptions();
compareObjects();
compareTerrain();
}
void MapComparer::operator() (const std::unique_ptr<CMap>& actual, const std::unique_ptr<CMap>& expected)
{
this->actual = actual.get();
this->expected = expected.get();
compare();
}

31
test/MapComparer.h Normal file
View File

@ -0,0 +1,31 @@
/*
* MapComparer.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 "../lib/mapping/CMap.h"
struct MapComparer
{
const CMap * actual;
const CMap * expected;
void compareHeader();
void compareOptions();
void compareObject(const CGObjectInstance * actual, const CGObjectInstance * expected);
void compareObjects();
void compareTerrain();
void compare();
void operator() (const std::unique_ptr<CMap>& actual, const std::unique_ptr<CMap>& expected);
};

View File

@ -10,6 +10,7 @@
<Option platforms="Windows;" />
<Option output="../Test" prefix_auto="1" extension_auto="1" />
<Option object_output="obj/Debug/x86/" />
<Option working_dir="../." />
<Option type="1" />
<Option compiler="gcc" />
<Compiler>
@ -46,6 +47,7 @@
<Add option="-Wno-unused-parameter" />
<Add option="-Wno-overloaded-virtual" />
<Add option="-Wno-unused-local-typedefs" />
<Add directory="$(#zlib.include)" />
<Add directory="$(#boost.include)" />
</Compiler>
<Linker>
@ -57,8 +59,12 @@
<Add directory="../" />
</Linker>
<Unit filename="CMapEditManagerTest.cpp" />
<Unit filename="CMapFormatTest.cpp" />
<Unit filename="CMemoryBufferTest.cpp" />
<Unit filename="CVcmiTestConfig.cpp" />
<Unit filename="CVcmiTestConfig.h" />
<Unit filename="MapComparer.cpp" />
<Unit filename="MapComparer.h" />
<Unit filename="StdInc.cpp">
<Option weight="0" />
</Unit>

View File

@ -27,13 +27,12 @@
<Depends filename="lib/VCMI_lib.cbp" />
</Project>
<Project filename="test/Test.cbp">
<Depends filename="AI/EmptyAI/EmptyAI.cbp" />
<Depends filename="AI/StupidAI/StupidAI.cbp" />
<Depends filename="AI/BattleAI/BattleAI.cbp" />
<Depends filename="lib/VCMI_lib.cbp" />
<Depends filename="client/VCMI_client.cbp" />
<Depends filename="server/VCMI_server.cbp" />
<Depends filename="AI/VCAI/VCAI.cbp" />
<Depends filename="AI/EmptyAI/EmptyAI.cbp" />
<Depends filename="AI/StupidAI/StupidAI.cbp" />
<Depends filename="AI/BattleAI/BattleAI.cbp" />
</Project>
<Project filename="scripting/erm/ERM.cbp">
<Depends filename="lib/VCMI_lib.cbp" />