mirror of
https://github.com/vcmi/vcmi.git
synced 2025-01-02 00:10:22 +02:00
Some bits towards replacements of objects.txt, first step towards
configurable objects support: - fixes for object template loader - schema for current object template format
This commit is contained in:
parent
771c1ce255
commit
1cec8d4cfc
46
config/schemas/objectTemplate.json
Normal file
46
config/schemas/objectTemplate.json
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
{
|
||||||
|
"type":"object",
|
||||||
|
"$schema": "http://json-schema.org/draft-04/schema",
|
||||||
|
"title" : "VCMI map object template format",
|
||||||
|
"description" : "Description of map object tempate that describes appearence of object instance",
|
||||||
|
"required": ["basebase", "base", "animation", "mask" ],
|
||||||
|
|
||||||
|
"additionalProperties" : false,
|
||||||
|
"properties":{
|
||||||
|
"basebase": {
|
||||||
|
"type" : "number",
|
||||||
|
"description": "Base object type, e.g. town or hero"
|
||||||
|
},
|
||||||
|
"base": {
|
||||||
|
"type" : "number",
|
||||||
|
"description": "Object subtype, e.g. Castle, Rampart, Cleric, Demon"
|
||||||
|
},
|
||||||
|
"animation": {
|
||||||
|
"type" : "string",
|
||||||
|
"description": "Path to def file with animation of this object",
|
||||||
|
"format" : "defFile"
|
||||||
|
},
|
||||||
|
"visitableFrom": {
|
||||||
|
"type":"array",
|
||||||
|
"description": "Directions from which this object is visible",
|
||||||
|
"minItems" : 3,
|
||||||
|
"maxItems" : 3,
|
||||||
|
"items": {
|
||||||
|
"type": "string",
|
||||||
|
"maxLength" : 3,
|
||||||
|
"minLength" : 3
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"allowedTerrains" : {
|
||||||
|
"type":"array",
|
||||||
|
"items": { "type": "string" },
|
||||||
|
"description": "List of terrain on which this object can be placed"
|
||||||
|
},
|
||||||
|
"mask" : {
|
||||||
|
"type":"array",
|
||||||
|
"items": { "type": "string" },
|
||||||
|
"description": "Object mask that describes on which tiles object is visible/blocked/activatable"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -64,6 +64,7 @@ void ObjectTemplate::readTxt(CLegacyConfigParser & parser)
|
|||||||
assert(strings.size() == 9);
|
assert(strings.size() == 9);
|
||||||
|
|
||||||
animationFile = strings[0];
|
animationFile = strings[0];
|
||||||
|
stringID = strings[0];
|
||||||
|
|
||||||
std::string & blockStr = strings[1]; //block map, 0 = blocked, 1 = unblocked
|
std::string & blockStr = strings[1]; //block map, 0 = blocked, 1 = unblocked
|
||||||
std::string & visitStr = strings[2]; //visit map, 1 = visitable, 0 = not visitable
|
std::string & visitStr = strings[2]; //visit map, 1 = visitable, 0 = not visitable
|
||||||
@ -182,7 +183,7 @@ void ObjectTemplate::readMap(CBinaryReader & reader)
|
|||||||
|
|
||||||
void ObjectTemplate::readJson(const JsonNode &node)
|
void ObjectTemplate::readJson(const JsonNode &node)
|
||||||
{
|
{
|
||||||
id = Obj(node["basebase"].Float()); // temporary, should be replaced
|
id = Obj(node["basebase"].Float()); // temporary, should be removed and determined indirectly via object type parent (e.g. base->base)
|
||||||
subid = node["base"].Float();
|
subid = node["base"].Float();
|
||||||
animationFile = node["animation"].String();
|
animationFile = node["animation"].String();
|
||||||
|
|
||||||
@ -199,48 +200,51 @@ void ObjectTemplate::readJson(const JsonNode &node)
|
|||||||
if (visitDirs[1].String()[0] == '+') visitDir |= 128;
|
if (visitDirs[1].String()[0] == '+') visitDir |= 128;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
visitDir = 0xff;
|
visitDir = 0x00;
|
||||||
|
|
||||||
|
if (!node["allowedTerrains"].isNull())
|
||||||
|
{
|
||||||
for (auto & entry : node["allowedTerrains"].Vector())
|
for (auto & entry : node["allowedTerrains"].Vector())
|
||||||
allowedTerrains.insert(ETerrainType(vstd::find_pos(GameConstants::TERRAIN_NAMES, entry.String())));
|
allowedTerrains.insert(ETerrainType(vstd::find_pos(GameConstants::TERRAIN_NAMES, entry.String())));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (size_t i=0; i< GameConstants::TERRAIN_TYPES; i++)
|
||||||
|
allowedTerrains.insert(ETerrainType(i));
|
||||||
|
}
|
||||||
|
|
||||||
auto charToTile = [&](const char & ch) -> ui8
|
auto charToTile = [&](const char & ch) -> ui8
|
||||||
{
|
{
|
||||||
switch (ch)
|
switch (ch)
|
||||||
{
|
{
|
||||||
|
case ' ' : return 0;
|
||||||
case '0' : return 0;
|
case '0' : return 0;
|
||||||
case 'V' : return VISIBLE;
|
case 'V' : return VISIBLE;
|
||||||
case 'B' : return VISIBLE | BLOCKED;
|
case 'B' : return VISIBLE | BLOCKED;
|
||||||
case 'H' : return BLOCKED;
|
case 'H' : return BLOCKED;
|
||||||
case 'A' : return VISIBLE | BLOCKED | VISITABLE;
|
case 'A' : return VISIBLE | BLOCKED | VISITABLE;
|
||||||
case 'T' : return VISIBLE | VISITABLE;
|
case 'T' : return BLOCKED | VISITABLE;
|
||||||
default:
|
default:
|
||||||
logGlobal->errorStream() << "Unrecognized char " << ch << " in template mask";
|
logGlobal->errorStream() << "Unrecognized char " << ch << " in template mask";
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
size_t maxHeight = 0, maxWidth = 0;
|
const JsonVector & mask = node["mask"].Vector();
|
||||||
size_t width = node["mask"].Vector()[0].String().size();
|
|
||||||
size_t height = node["mask"].Vector().size();
|
size_t height = mask.size();
|
||||||
|
size_t width = 0;
|
||||||
|
for (auto & line : mask)
|
||||||
|
vstd::amax(width, line.String().size());
|
||||||
|
|
||||||
setSize(width, height);
|
setSize(width, height);
|
||||||
|
|
||||||
for (size_t i=0; i<height; i++)
|
for (size_t i=0; i<mask.size(); i++)
|
||||||
{
|
{
|
||||||
const std::string & line = node["mask"].Vector()[i].String();
|
const std::string & line = mask[i].String();
|
||||||
assert(line.size() == width);
|
for (size_t j=0; j < line.size(); j++)
|
||||||
for (size_t j=0; j < width; j++)
|
usedTiles[mask.size() - 1 - i][line.size() - 1 - j] = charToTile(line[j]);
|
||||||
{
|
|
||||||
ui8 tile = charToTile(line[j]);
|
|
||||||
if (tile != 0)
|
|
||||||
{
|
|
||||||
vstd::amax(maxHeight, i);
|
|
||||||
vstd::amax(maxWidth, j);
|
|
||||||
usedTiles[i][j] = tile;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
setSize(maxWidth, maxHeight);
|
|
||||||
|
|
||||||
printPriority = node["zIndex"].Float();
|
printPriority = node["zIndex"].Float();
|
||||||
}
|
}
|
||||||
@ -259,7 +263,7 @@ void ObjectTemplate::setSize(ui32 width, ui32 height)
|
|||||||
{
|
{
|
||||||
usedTiles.resize(height);
|
usedTiles.resize(height);
|
||||||
for (auto & line : usedTiles)
|
for (auto & line : usedTiles)
|
||||||
line.resize(width);
|
line.resize(width, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ObjectTemplate::isVisitable() const
|
bool ObjectTemplate::isVisitable() const
|
||||||
@ -345,14 +349,19 @@ CDefObjInfoHandler::CDefObjInfoHandler()
|
|||||||
readTextFile("Data/Objects.txt");
|
readTextFile("Data/Objects.txt");
|
||||||
readTextFile("Data/Heroes.txt");
|
readTextFile("Data/Heroes.txt");
|
||||||
|
|
||||||
const JsonNode node = JsonUtils::assembleFromFiles("config/objectTemplates.json");
|
// TODO: merge into modding system
|
||||||
|
JsonNode node = JsonUtils::assembleFromFiles("config/objectTemplates.json");
|
||||||
|
node.setMeta("core");
|
||||||
std::vector<ObjectTemplate> newTemplates;
|
std::vector<ObjectTemplate> newTemplates;
|
||||||
newTemplates.reserve(node.Struct().size());
|
newTemplates.reserve(node.Struct().size());
|
||||||
|
|
||||||
// load all new templates
|
// load all new templates
|
||||||
for (auto & entry : node.Struct())
|
for (auto & entry : node.Struct())
|
||||||
{
|
{
|
||||||
|
JsonUtils::validate(entry.second, "vcmi:objectTemplate", entry.first);
|
||||||
|
|
||||||
ObjectTemplate templ;
|
ObjectTemplate templ;
|
||||||
|
templ.stringID = entry.first;
|
||||||
templ.readJson(entry.second);
|
templ.readJson(entry.second);
|
||||||
newTemplates.push_back(templ);
|
newTemplates.push_back(templ);
|
||||||
}
|
}
|
||||||
@ -389,6 +398,7 @@ std::vector<ObjectTemplate> CDefObjInfoHandler::pickCandidates(Obj type, si32 su
|
|||||||
});
|
});
|
||||||
if (ret.empty())
|
if (ret.empty())
|
||||||
logGlobal->errorStream() << "Failed to find template for " << type << ":" << subtype;
|
logGlobal->errorStream() << "Failed to find template for " << type << ":" << subtype;
|
||||||
|
|
||||||
assert(!ret.empty()); // Can't create object of this type/subtype
|
assert(!ret.empty()); // Can't create object of this type/subtype
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -405,3 +415,13 @@ std::vector<ObjectTemplate> CDefObjInfoHandler::pickCandidates(Obj type, si32 su
|
|||||||
// it is possible that there are no templates usable on specific terrain. In this case - return list before filtering
|
// it is possible that there are no templates usable on specific terrain. In this case - return list before filtering
|
||||||
return filtered.empty() ? ret : filtered;
|
return filtered.empty() ? ret : filtered;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<ObjectTemplate> CDefObjInfoHandler::pickCandidates(Obj type, si32 subtype, ETerrainType terrain, std::function<bool(ObjectTemplate &)> filter) const
|
||||||
|
{
|
||||||
|
std::vector<ObjectTemplate> ret = pickCandidates(type, subtype, terrain);
|
||||||
|
std::vector<ObjectTemplate> filtered;
|
||||||
|
|
||||||
|
std::copy_if(ret.begin(), ret.end(), std::back_inserter(filtered), filter);
|
||||||
|
// it is possible that there are no templates usable on specific terrain. In this case - return list before filtering
|
||||||
|
return filtered.empty() ? ret : filtered;
|
||||||
|
}
|
||||||
|
@ -42,6 +42,9 @@ public:
|
|||||||
/// animation file that should be used to display object
|
/// animation file that should be used to display object
|
||||||
std::string animationFile;
|
std::string animationFile;
|
||||||
|
|
||||||
|
/// string ID, equals to def base name for h3m files (lower case, no extension) or specified in mod data
|
||||||
|
std::string stringID;
|
||||||
|
|
||||||
ui32 getWidth() const;
|
ui32 getWidth() const;
|
||||||
ui32 getHeight() const;
|
ui32 getHeight() const;
|
||||||
void setSize(ui32 width, ui32 height);
|
void setSize(ui32 width, ui32 height);
|
||||||
@ -70,7 +73,7 @@ public:
|
|||||||
|
|
||||||
template <typename Handler> void serialize(Handler &h, const int version)
|
template <typename Handler> void serialize(Handler &h, const int version)
|
||||||
{
|
{
|
||||||
h & usedTiles & allowedTerrains & animationFile;
|
h & usedTiles & allowedTerrains & animationFile & stringID;
|
||||||
h & id & subid & printPriority & visitDir;
|
h & id & subid & printPriority & visitDir;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -97,9 +100,8 @@ public:
|
|||||||
std::vector<ObjectTemplate> pickCandidates(Obj type, si32 subtype) const;
|
std::vector<ObjectTemplate> pickCandidates(Obj type, si32 subtype) const;
|
||||||
/// picks all candidates for <type, subtype> and of possible - also filters them by terrain
|
/// picks all candidates for <type, subtype> and of possible - also filters them by terrain
|
||||||
std::vector<ObjectTemplate> pickCandidates(Obj type, si32 subtype, ETerrainType terrain) const;
|
std::vector<ObjectTemplate> pickCandidates(Obj type, si32 subtype, ETerrainType terrain) const;
|
||||||
|
/// as above, but also filters out templates that are not applicable according to accepted test
|
||||||
// TODO: as above, but also filters out templates that are not applicable according to accepted test
|
std::vector<ObjectTemplate> pickCandidates(Obj type, si32 subtype, ETerrainType terrain, std::function<bool(ObjectTemplate &)> filter) const;
|
||||||
// std::vector<ObjectTemplate> pickCandidates(Obj type, si32 subtype, ETerrainType terrain, std::function<bool(ObjectTemplate &)> accept) const;
|
|
||||||
|
|
||||||
template <typename Handler> void serialize(Handler &h, const int version)
|
template <typename Handler> void serialize(Handler &h, const int version)
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user