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:
commit
8f1fba9551
@ -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)
|
||||
|
||||
############################################
|
||||
|
7
Mods/vcmi/Sprites/ScSelC.json
Normal file
7
Mods/vcmi/Sprites/ScSelC.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"basepath" : "mapFormatIcons/",
|
||||
"images" :
|
||||
[
|
||||
{ "group" : 1, "frame" : 0, "file" : "vcmi1.png"}
|
||||
]
|
||||
}
|
BIN
Mods/vcmi/Sprites/mapFormatIcons/vcmi1.png
Normal file
BIN
Mods/vcmi/Sprites/mapFormatIcons/vcmi1.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.0 KiB |
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
{
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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()
|
||||
|
@ -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>
|
||||
|
@ -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))
|
||||
|
@ -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();
|
||||
|
@ -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)
|
||||
|
@ -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];
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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 <<'\"';
|
||||
|
@ -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>
|
||||
{
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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";
|
||||
}
|
||||
|
@ -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" />
|
||||
|
@ -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" />
|
||||
|
@ -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>
|
@ -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();
|
||||
|
9
lib/filesystem/CInputOutputStream.h
Normal file
9
lib/filesystem/CInputOutputStream.h
Normal file
@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "CInputStream.h"
|
||||
#include "COutputStream.h"
|
||||
|
||||
class CInputOutputStream: public CInputStream, public COutputStream
|
||||
{
|
||||
|
||||
};
|
@ -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
|
||||
*
|
||||
|
73
lib/filesystem/CMemoryBuffer.cpp
Normal file
73
lib/filesystem/CMemoryBuffer.cpp
Normal 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();
|
||||
}
|
||||
|
||||
|
88
lib/filesystem/CMemoryBuffer.h
Normal file
88
lib/filesystem/CMemoryBuffer.h
Normal 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;
|
||||
};
|
||||
|
@ -14,7 +14,8 @@
|
||||
|
||||
/**
|
||||
* A class which provides method definitions for reading from memory.
|
||||
*/
|
||||
* @deprecated use CMemoryBuffer
|
||||
*/
|
||||
class DLL_LINKAGE CMemoryStream : public CInputStream
|
||||
{
|
||||
public:
|
||||
|
34
lib/filesystem/COutputStream.h
Normal file
34
lib/filesystem/COutputStream.h
Normal 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
50
lib/filesystem/CStream.h
Normal 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;
|
||||
};
|
@ -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
|
||||
|
@ -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
|
||||
|
120
lib/filesystem/CZipSaver.cpp
Normal file
120
lib/filesystem/CZipSaver.cpp
Normal 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);
|
||||
}
|
||||
|
57
lib/filesystem/CZipSaver.h
Normal file
57
lib/filesystem/CZipSaver.h
Normal 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;
|
||||
};
|
254
lib/filesystem/MinizipExtensions.cpp
Normal file
254
lib/filesystem/MinizipExtensions.cpp
Normal 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;
|
||||
}
|
88
lib/filesystem/MinizipExtensions.h
Normal file
88
lib/filesystem/MinizipExtensions.h
Normal 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);
|
||||
};
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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
|
||||
|
@ -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); }
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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]));
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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
|
||||
|
@ -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
@ -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
|
||||
};
|
||||
|
@ -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();
|
||||
|
@ -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];
|
||||
|
163
lib/serializer/JsonDeserializer.cpp
Normal file
163
lib/serializer/JsonDeserializer.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
34
lib/serializer/JsonDeserializer.h
Normal file
34
lib/serializer/JsonDeserializer.h
Normal 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);
|
||||
};
|
90
lib/serializer/JsonSerializeFormat.cpp
Normal file
90
lib/serializer/JsonSerializeFormat.cpp
Normal 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;
|
||||
}
|
148
lib/serializer/JsonSerializeFormat.h
Normal file
148
lib/serializer/JsonSerializeFormat.h
Normal 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;
|
||||
};
|
||||
|
94
lib/serializer/JsonSerializer.cpp
Normal file
94
lib/serializer/JsonSerializer.cpp
Normal 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
35
lib/serializer/JsonSerializer.h
Normal file
35
lib/serializer/JsonSerializer.h
Normal 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);
|
||||
};
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -8,6 +8,8 @@ set(test_SRCS
|
||||
StdInc.cpp
|
||||
CVcmiTestConfig.cpp
|
||||
CMapEditManagerTest.cpp
|
||||
MapComparer.cpp
|
||||
CMapFormatTest.cpp
|
||||
)
|
||||
|
||||
add_executable(vcmitest ${test_SRCS})
|
||||
|
@ -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
97
test/CMapFormatTest.cpp
Normal 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");
|
||||
}
|
52
test/CMemoryBufferTest.cpp
Normal file
52
test/CMemoryBufferTest.cpp
Normal 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 *)¤t, sizeof(si32));
|
||||
BOOST_CHECK_EQUAL(sizeof(si32), ret);
|
||||
BOOST_CHECK_EQUAL(initial, current);
|
||||
BOOST_CHECK_EQUAL(4, subject.tell());
|
||||
}
|
@ -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
274
test/MapComparer.cpp
Normal 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
31
test/MapComparer.h
Normal 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);
|
||||
};
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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" />
|
||||
|
Loading…
x
Reference in New Issue
Block a user