mirror of
https://github.com/vcmi/vcmi.git
synced 2025-08-08 22:26:51 +02:00
Fixes for handling of oversized map dwellings
- Marked large version of H3 Unicorn's Glade as not usable for random dwelling replacement - Shifted oversized dwellings - that have at most 2x2 as blocked tile, but have non-blocked tile column will now be placed correctly - This fixes incorrect random dwelling replacement of the only oversized H3 dwelling - Portal of Glory - Game will now detect & report invalid dwelling templates from mods - Updated docs to clarify dwellings format
This commit is contained in:
@ -377,6 +377,7 @@
|
||||
},
|
||||
"unicornGladeBig": {
|
||||
"index": 51,
|
||||
"bannedForRandomDwelling" : true,
|
||||
"creatures": [["unicorn"]],
|
||||
"sounds": {
|
||||
"ambient": ["LOOPUNIC"]
|
||||
|
@ -9,6 +9,11 @@
|
||||
[ "airElemental", "stormElemental" ],
|
||||
[ "waterElemental" ]
|
||||
],
|
||||
|
||||
/// If set to true, this dwelling will not be selected as a replacement for random dwelling on map
|
||||
/// Such dwellings have no restrictions on which tiles are visitable or blocked
|
||||
/// For dwelling to be usable as a replacement, it must follow some additional restrictions (see below)
|
||||
"bannedForRandomDwelling" : true,
|
||||
|
||||
/// List of guards for this dwelling. Can have two possible values:
|
||||
/// Boolean true/false - If set to "true", guards will be generated using H3 formula:
|
||||
@ -20,3 +25,44 @@
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Replacement of random dwellings
|
||||
|
||||
Existing maps may contain random dwellings that will be replaced with concrete dwellings on map loading.
|
||||
|
||||
For dwelling to be a valid replacement for such random dwelling it must be:
|
||||
|
||||
- block at most 2x2 tile square
|
||||
- one tile in bottom row must be visitable, and another - blocked
|
||||
|
||||
Visible tiles (`V` in map object template mask) don't have any restrictions and can have any layout
|
||||
|
||||
It is possible to make dwellings that don't fulfill this requirements, however such dwellings should only be used for custom maps or random maps. Mod that adds a new faction need to also provide a set of valid dwellings that can be used for replacement of random dwellings.
|
||||
|
||||
Examples of valid dwellings:
|
||||
|
||||
- minimal - bottom row contains one blocked and one visitable tile, second row fully passable
|
||||
|
||||
```json
|
||||
"mask":[
|
||||
"AB"
|
||||
],
|
||||
```
|
||||
|
||||
- maximal - bottom row contains one blocked and one visitable tile, both tiles on second row are blocked
|
||||
|
||||
```json
|
||||
"mask":[
|
||||
"BB"
|
||||
"BA"
|
||||
],
|
||||
```
|
||||
|
||||
- extended visual - similar to maximal, but right-most column is fully passable. Note that blocked tiles still fit into 2x2 square
|
||||
|
||||
```json
|
||||
"mask":[
|
||||
"BBV"
|
||||
"BAV"
|
||||
],
|
||||
```
|
||||
|
@ -175,6 +175,7 @@ void AObjectTypeHandler::clearTemplates()
|
||||
void AObjectTypeHandler::addTemplate(const std::shared_ptr<const ObjectTemplate> & templ)
|
||||
{
|
||||
templates.push_back(templ);
|
||||
onTemplateAdded(templ);
|
||||
}
|
||||
|
||||
void AObjectTypeHandler::addTemplate(JsonNode config)
|
||||
|
@ -57,6 +57,8 @@ protected:
|
||||
|
||||
/// initialization for classes that inherit this one
|
||||
virtual void initTypeData(const JsonNode & input);
|
||||
|
||||
virtual void onTemplateAdded(const std::shared_ptr<const ObjectTemplate>) {}
|
||||
public:
|
||||
|
||||
AObjectTypeHandler();
|
||||
|
@ -15,7 +15,9 @@
|
||||
#include "../json/JsonRandom.h"
|
||||
#include "../GameLibrary.h"
|
||||
#include "../mapObjects/CGDwelling.h"
|
||||
#include "../mapObjects/ObjectTemplate.h"
|
||||
#include "../modding/IdentifierStorage.h"
|
||||
#include "../CConfigHandler.h"
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
@ -52,6 +54,30 @@ void DwellingInstanceConstructor::initTypeData(const JsonNode & input)
|
||||
}
|
||||
guards = input["guards"];
|
||||
bannedForRandomDwelling = input["bannedForRandomDwelling"].Bool();
|
||||
|
||||
for (const auto & mapTemplate : getTemplates())
|
||||
onTemplateAdded(mapTemplate);
|
||||
}
|
||||
|
||||
void DwellingInstanceConstructor::onTemplateAdded(const std::shared_ptr<const ObjectTemplate> mapTemplate)
|
||||
{
|
||||
if (bannedForRandomDwelling || settings["mods"]["validation"].String() == "off")
|
||||
return;
|
||||
|
||||
bool invalidForRandomDwelling = false;
|
||||
int3 corner = mapTemplate->getCornerOffset();
|
||||
|
||||
for (const auto & tile : mapTemplate->getBlockedOffsets())
|
||||
invalidForRandomDwelling |= (tile.x != -corner.x && tile.x != -corner.x-1) || (tile.y != -corner.y && tile.y != -corner.y-1);
|
||||
|
||||
for (const auto & tile : {mapTemplate->getVisitableOffset()})
|
||||
invalidForRandomDwelling |= (tile.x != corner.x && tile.x != corner.x+1) || tile.y != corner.y;
|
||||
|
||||
invalidForRandomDwelling |= !mapTemplate->isBlockedAt(corner.x+0, corner.y) && !mapTemplate->isVisibleAt(corner.x+0, corner.y);
|
||||
invalidForRandomDwelling |= !mapTemplate->isBlockedAt(corner.x+1, corner.y) && !mapTemplate->isVisibleAt(corner.x+1, corner.y);
|
||||
|
||||
if (invalidForRandomDwelling)
|
||||
logMod->warn("Dwelling %s has template %s which is not valid for a random dwelling! Dwellings must not block tiles outside 2x2 range and must be visitable in bottom row. Change dwelling mask or mark dwelling as 'bannedForRandomDwelling'", getJsonKey(), mapTemplate->animationFile.getOriginalName());
|
||||
}
|
||||
|
||||
bool DwellingInstanceConstructor::isBannedForRandomDwelling() const
|
||||
@ -152,5 +178,4 @@ std::vector<const CCreature *> DwellingInstanceConstructor::getProducedCreatures
|
||||
return creatures;
|
||||
}
|
||||
|
||||
|
||||
VCMI_LIB_NAMESPACE_END
|
||||
|
@ -28,6 +28,7 @@ class DwellingInstanceConstructor : public CDefaultObjectTypeHandler<CGDwelling>
|
||||
protected:
|
||||
bool objectFilter(const CGObjectInstance * obj, std::shared_ptr<const ObjectTemplate> tmpl) const override;
|
||||
void initTypeData(const JsonNode & input) override;
|
||||
void onTemplateAdded(const std::shared_ptr<const ObjectTemplate>) override;
|
||||
|
||||
public:
|
||||
bool hasNameTextID() const override;
|
||||
|
@ -121,7 +121,7 @@ const std::set<int3> & CGObjectInstance::getBlockedOffsets() const
|
||||
void CGObjectInstance::setType(MapObjectID newID, MapObjectSubID newSubID)
|
||||
{
|
||||
auto position = visitablePos();
|
||||
auto oldOffset = getVisitableOffset();
|
||||
auto oldOffset = appearance->getCornerOffset();
|
||||
auto &tile = cb->gameState().getMap().getTile(position);
|
||||
|
||||
//recalculate blockvis tiles - new appearance might have different blockmap than before
|
||||
@ -144,11 +144,12 @@ void CGObjectInstance::setType(MapObjectID newID, MapObjectSubID newSubID)
|
||||
// instead, appearance update & pos adjustment occurs in GiveHero::applyGs
|
||||
needToAdjustOffset |= this->ID == Obj::PRISON && newID == Obj::HERO;
|
||||
needToAdjustOffset |= newID == Obj::MONSTER;
|
||||
needToAdjustOffset |= newID == Obj::CREATURE_GENERATOR1 || newID == Obj::CREATURE_GENERATOR2 || newID == Obj::CREATURE_GENERATOR3 || newID == Obj::CREATURE_GENERATOR4;
|
||||
|
||||
if(needToAdjustOffset)
|
||||
{
|
||||
// adjust position since object visitable offset might have changed
|
||||
auto newOffset = getVisitableOffset();
|
||||
auto newOffset = appearance->getCornerOffset();
|
||||
pos = pos - oldOffset + newOffset;
|
||||
}
|
||||
|
||||
|
@ -500,6 +500,23 @@ void ObjectTemplate::calculateVisitableOffset()
|
||||
visitableOffset = int3(0, 0, 0);
|
||||
}
|
||||
|
||||
int3 ObjectTemplate::getCornerOffset() const
|
||||
{
|
||||
assert(isVisitable());
|
||||
|
||||
int3 ret = visitableOffset;
|
||||
for (const auto & tile : blockedOffsets)
|
||||
{
|
||||
ret = {
|
||||
std::min(-tile.x, ret.x),
|
||||
std::min(-tile.y, ret.y),
|
||||
ret.z
|
||||
};
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool ObjectTemplate::canBePlacedAt(TerrainId terrainID) const
|
||||
{
|
||||
if (anyLandTerrain)
|
||||
|
@ -124,6 +124,11 @@ public:
|
||||
// Checks if object can be placed on specific terrain
|
||||
bool canBePlacedAt(TerrainId terrain) const;
|
||||
|
||||
/// Returns number of completely empty rows & columns in template
|
||||
/// Such as shifted wandering monster def's from hota, or Portal of Glory dwelling from H3
|
||||
/// object must be visitable
|
||||
int3 getCornerOffset() const;
|
||||
|
||||
CompoundMapObjectID getCompoundID() const;
|
||||
|
||||
ObjectTemplate();
|
||||
|
Reference in New Issue
Block a user