1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-11-06 09:09:40 +02:00

Correct placement of non-overlapping objects.

This commit is contained in:
DjWarmonger
2014-07-08 20:13:51 +02:00
parent 8a8cda950d
commit 22d26db694
6 changed files with 163 additions and 61 deletions

View File

@@ -241,13 +241,7 @@ int CGObjectInstance::getSightRadious() const
int3 CGObjectInstance::getVisitableOffset() const int3 CGObjectInstance::getVisitableOffset() const
{ {
for(int y = 0; y < appearance.getHeight(); y++) return appearance.getVisitableOffset();
for (int x = 0; x < appearance.getWidth(); x++)
if (appearance.isVisitableAt(x, y))
return int3(x,y,0);
//logGlobal->warnStream() << "Warning: getVisitableOffset called on non-visitable obj!";
return int3(0,0,0);
} }
void CGObjectInstance::giveDummyBonus(ObjectInstanceID heroID, ui8 duration) const void CGObjectInstance::giveDummyBonus(ObjectInstanceID heroID, ui8 duration) const

View File

@@ -23,31 +23,34 @@
* *
*/ */
static bool isVisitableFromTop(int identifier, int type) namespace vstd
{ {
if(type == 2 || type == 3 || type == 4 || type == 5) //creature, hero, artifact, resource static bool isVisitableFromTop(int identifier, int type)
return true; {
if(type == 2 || type == 3 || type == 4 || type == 5) //creature, hero, artifact, resource
return true;
static const Obj visitableFromTop[] = static const Obj visitableFromTop[] =
{Obj::FLOTSAM, {Obj::FLOTSAM,
Obj::SEA_CHEST, Obj::SEA_CHEST,
Obj::SHIPWRECK_SURVIVOR, Obj::SHIPWRECK_SURVIVOR,
Obj::BUOY, Obj::BUOY,
Obj::OCEAN_BOTTLE, Obj::OCEAN_BOTTLE,
Obj::BOAT, Obj::BOAT,
Obj::WHIRLPOOL, Obj::WHIRLPOOL,
Obj::GARRISON, Obj::GARRISON,
Obj::GARRISON2, Obj::GARRISON2,
Obj::SCHOLAR, Obj::SCHOLAR,
Obj::CAMPFIRE, Obj::CAMPFIRE,
Obj::BORDERGUARD, Obj::BORDERGUARD,
Obj::BORDER_GATE, Obj::BORDER_GATE,
Obj::QUEST_GUARD, Obj::QUEST_GUARD,
Obj::CORPSE Obj::CORPSE
}; };
if (vstd::find_pos(visitableFromTop, identifier) != -1) if (vstd::find_pos(visitableFromTop, identifier) != -1)
return true; return true;
return false; return false;
}
} }
ObjectTemplate::ObjectTemplate(): ObjectTemplate::ObjectTemplate():
@@ -106,7 +109,7 @@ void ObjectTemplate::readTxt(CLegacyConfigParser & parser)
int type = boost::lexical_cast<int>(strings[7]); int type = boost::lexical_cast<int>(strings[7]);
printPriority = boost::lexical_cast<int>(strings[8]) * 100; // to have some space in future printPriority = boost::lexical_cast<int>(strings[8]) * 100; // to have some space in future
if (isVisitableFromTop(id, type)) if (vstd::isVisitableFromTop(id, type))
visitDir = 0xff; visitDir = 0xff;
else else
visitDir = (8|16|32|64|128); visitDir = (8|16|32|64|128);
@@ -168,7 +171,7 @@ void ObjectTemplate::readMap(CBinaryReader & reader)
int type = reader.readUInt8(); int type = reader.readUInt8();
printPriority = reader.readUInt8() * 100; // to have some space in future printPriority = reader.readUInt8() * 100; // to have some space in future
if (isVisitableFromTop(id, type)) if (vstd::isVisitableFromTop(id, type))
visitDir = 0xff; visitDir = 0xff;
else else
visitDir = (8|16|32|64|128); visitDir = (8|16|32|64|128);
@@ -354,6 +357,22 @@ bool ObjectTemplate::isVisitableFrom(si8 X, si8 Y) const
return dirMap[dy][dx] != 0; return dirMap[dy][dx] != 0;
} }
int3 ObjectTemplate::getVisitableOffset() const
{
for(int y = 0; y < getHeight(); y++)
for (int x = 0; x < getWidth(); x++)
if (isVisitableAt(x, y))
return int3(x,y,0);
//logGlobal->warnStream() << "Warning: getVisitableOffset called on non-visitable obj!";
return int3(0,0,0);
}
bool ObjectTemplate::isVisitableFromTop() const
{
return isVisitableFrom (0, 1);
}
bool ObjectTemplate::canBePlacedAt(ETerrainType terrain) const bool ObjectTemplate::canBePlacedAt(ETerrainType terrain) const
{ {
return allowedTerrains.count(terrain) != 0; return allowedTerrains.count(terrain) != 0;

View File

@@ -62,6 +62,8 @@ public:
// Checks if object is visitable from certain direction. X and Y must be between -1..+1 // Checks if object is visitable from certain direction. X and Y must be between -1..+1
bool isVisitableFrom(si8 X, si8 Y) const; bool isVisitableFrom(si8 X, si8 Y) const;
int3 getVisitableOffset() const;
bool isVisitableFromTop() const;
// Checks if object can be placed on specific terrain // Checks if object can be placed on specific terrain
bool canBePlacedAt(ETerrainType terrain) const; bool canBePlacedAt(ETerrainType terrain) const;

View File

@@ -216,9 +216,9 @@ void CMapGenerator::fillZones()
logGlobal->infoStream() << "Started filling zones"; logGlobal->infoStream() << "Started filling zones";
createConnections(); createConnections();
//make sure all connections are passable before creating borders
for (auto it : zones) for (auto it : zones)
{ {
//make sure all connections are passable before creating borders
it.second->createBorder(this); it.second->createBorder(this);
it.second->fill(this); it.second->fill(this);
} }

View File

@@ -625,10 +625,12 @@ bool CRmgTemplateZone::addMonster(CMapGenerator* gen, int3 &pos, si32 strength)
bool CRmgTemplateZone::createTreasurePile (CMapGenerator* gen, int3 &pos) bool CRmgTemplateZone::createTreasurePile (CMapGenerator* gen, int3 &pos)
{ {
CTreasurePileInfo info;
std::map<int3, CGObjectInstance *> treasures; std::map<int3, CGObjectInstance *> treasures;
std::set<int3> boundary; std::set<int3> boundary;
int3 guardPos (-1,-1,-1); int3 guardPos (-1,-1,-1);
int3 nextTreasurePos = pos; info.nextTreasurePos = pos;
//default values //default values
int maxValue = 5000; int maxValue = 5000;
@@ -654,9 +656,8 @@ bool CRmgTemplateZone::createTreasurePile (CMapGenerator* gen, int3 &pos)
CGObjectInstance * object = nullptr; CGObjectInstance * object = nullptr;
while (currentValue < minValue) while (currentValue < minValue)
{ {
//TODO: this works only for 1-tile objects
//make sure our shape is consistent //make sure our shape is consistent
treasures[nextTreasurePos] = nullptr; treasures[info.nextTreasurePos] = nullptr;
for (auto treasurePos : treasures) for (auto treasurePos : treasures)
{ {
gen->foreach_neighbour (treasurePos.first, [gen, &boundary](int3 pos) gen->foreach_neighbour (treasurePos.first, [gen, &boundary](int3 pos)
@@ -678,14 +679,30 @@ bool CRmgTemplateZone::createTreasurePile (CMapGenerator* gen, int3 &pos)
int remaining = maxValue - currentValue; int remaining = maxValue - currentValue;
auto oi = getRandomObject(gen, remaining); ObjectInfo oi = getRandomObject(gen, info, remaining);
object = oi.generateObject(); object = oi.generateObject();
if (!object) if (!object)
{
vstd::erase_if_present(treasures, info.nextTreasurePos);
break; break;
}
else
{
//update treasure pile area
int3 visitablePos = oi.templ.getVisitableOffset() + info.nextTreasurePos;
info.visitablePositions.insert(visitablePos); //can be accessed only from bottom or side
if (oi.templ.isVisitableFromTop())
info.visitableFromTopPositions.insert(visitablePos); //can be accessed from any direction
for (auto blockedOffset : oi.templ.getBlockedOffsets())
info.occupiedPositions.insert(info.nextTreasurePos + blockedOffset);
info.occupiedPositions.insert(visitablePos);
}
currentValue += oi.value; currentValue += oi.value;
treasures[nextTreasurePos] = object; treasures[info.nextTreasurePos] = object;
//now find place for next object //now find place for next object
int3 placeFound(-1,-1,-1); int3 placeFound(-1,-1,-1);
@@ -712,8 +729,9 @@ bool CRmgTemplateZone::createTreasurePile (CMapGenerator* gen, int3 &pos)
} }
} }
if (placeFound.valid()) if (placeFound.valid())
nextTreasurePos = placeFound; info.nextTreasurePos = placeFound;
} }
if (treasures.size()) if (treasures.size())
{ {
//find object closest to zone center, then con nect it to the middle of the zone //find object closest to zone center, then con nect it to the middle of the zone
@@ -752,21 +770,8 @@ bool CRmgTemplateZone::createTreasurePile (CMapGenerator* gen, int3 &pos)
{ {
for (auto treasure : treasures) for (auto treasure : treasures)
{ {
bool objectFitsHere = true; //temporary workaround
int3 visitableOffset = treasure.second->getVisitableOffset(); int3 visitableOffset = treasure.second->getVisitableOffset();
std::set<int3> blockedOffsets = treasure.second->getBlockedOffsets(); placeObject(gen, treasure.second, treasure.first + visitableOffset);
blockedOffsets.insert (visitableOffset);
for (auto blockingTile : blockedOffsets)
{
int3 t = treasure.first + visitableOffset + blockingTile;
if (!gen->map->isInTheMap(t))
{
objectFitsHere = false; //if at least one tile is not possible, object can't be placed here
break;
}
}
if (objectFitsHere)
placeObject(gen, treasure.second, treasure.first + visitableOffset);
} }
if (addMonster(gen, guardPos, currentValue)) if (addMonster(gen, guardPos, currentValue))
{//block only if the object is guarded {//block only if the object is guarded
@@ -1362,7 +1367,7 @@ bool CRmgTemplateZone::guardObject(CMapGenerator* gen, CGObjectInstance* object,
return true; return true;
} }
ObjectInfo CRmgTemplateZone::getRandomObject (CMapGenerator* gen, ui32 value) ObjectInfo CRmgTemplateZone::getRandomObject (CMapGenerator* gen, CTreasurePileInfo &info, ui32 value)
{ {
std::vector<std::pair<ui32, ObjectInfo>> tresholds; std::vector<std::pair<ui32, ObjectInfo>> tresholds;
ui32 total = 0; ui32 total = 0;
@@ -1374,17 +1379,73 @@ ObjectInfo CRmgTemplateZone::getRandomObject (CMapGenerator* gen, ui32 value)
{ {
if (oi.value >= minValue && oi.value <= value) if (oi.value >= minValue && oi.value <= value)
{ {
//TODO: check place for non-removable object int3 visitableOffset = oi.templ.getVisitableOffset(); //visitablePos assumes object will be shifter by visitableOffset
//problem: we need at least template for the object that does not yet exist if (info.visitablePositions.size()) //do not try to match first object in zone
{
bool fitsHere = false;
int3 visitablePos = info.nextTreasurePos;
if (oi.templ.isVisitableFromTop())
{
for (auto tile : info.visitablePositions)
{
int3 actualTile = tile + visitableOffset;
if (visitablePos.areNeighbours(actualTile)) //we access object from any position
{
fitsHere = true;
break;
}
}
}
else
{
//if object is not visitable from top, it must be accessible from below or side
for (auto tile : info.visitableFromTopPositions)
{
int3 actualTile = tile + visitableOffset;
if (visitablePos.areNeighbours(actualTile) && visitablePos.y >= actualTile.y) //we access object from below or side
{
fitsHere = true;
break;
}
}
}
if (!fitsHere)
continue;
}
//now check blockmap, including our already reserved pile area
bool fitsBlockmap = true;
std::set<int3> blockedOffsets = oi.templ.getBlockedOffsets();
blockedOffsets.insert (visitableOffset);
for (auto blockingTile : blockedOffsets)
{
int3 t = info.nextTreasurePos + visitableOffset + blockingTile;
if (!gen->map->isInTheMap(t) || vstd::contains(info.occupiedPositions, t))
{
fitsBlockmap = false; //if at least one tile is not possible, object can't be placed here
break;
}
if (!(gen->isPossible(t) || gen->isBlocked(t))) //blocked tiles of object may cover blocked tiles, but not used or free tiles
{
fitsBlockmap = false;
break;
}
}
if (!fitsBlockmap)
continue;
total += oi.probability; total += oi.probability;
tresholds.push_back (std::make_pair (total, oi)); tresholds.push_back (std::make_pair (total, oi));
} }
} }
//TODO: generate pandora box with gold if the value is very high //Generate pandora Box with gold if the value is extremely high
ObjectInfo oi;
if (tresholds.empty()) if (tresholds.empty())
{ {
ObjectInfo oi;
if (minValue > 20000) //we don't have object valuable enough if (minValue > 20000) //we don't have object valuable enough
{ {
oi.generateObject = [minValue]() -> CGObjectInstance * oi.generateObject = [minValue]() -> CGObjectInstance *
@@ -1395,6 +1456,7 @@ ObjectInfo CRmgTemplateZone::getRandomObject (CMapGenerator* gen, ui32 value)
obj->resources[Res::GOLD] = minValue; obj->resources[Res::GOLD] = minValue;
return obj; return obj;
}; };
oi.setTemplate(Obj::PANDORAS_BOX, 0, terrainType);
oi.value = minValue; oi.value = minValue;
oi.probability = 0; oi.probability = 0;
} }
@@ -1404,6 +1466,7 @@ ObjectInfo CRmgTemplateZone::getRandomObject (CMapGenerator* gen, ui32 value)
{ {
return nullptr; return nullptr;
}; };
oi.setTemplate(Obj::PANDORAS_BOX, 0, terrainType); //TODO: null template or something? should be never used, but hell knows
oi.value = 0; oi.value = 0;
oi.probability = 0; oi.probability = 0;
} }
@@ -1422,8 +1485,6 @@ ObjectInfo CRmgTemplateZone::getRandomObject (CMapGenerator* gen, ui32 value)
void CRmgTemplateZone::addAllPossibleObjects (CMapGenerator* gen) void CRmgTemplateZone::addAllPossibleObjects (CMapGenerator* gen)
{ {
//TODO: move typical objects to config
ObjectInfo oi; ObjectInfo oi;
for (auto primaryID : VLC->objtypeh->knownObjects()) for (auto primaryID : VLC->objtypeh->knownObjects())
@@ -1443,6 +1504,7 @@ void CRmgTemplateZone::addAllPossibleObjects (CMapGenerator* gen)
}; };
oi.value = handler->getRMGInfo().value; oi.value = handler->getRMGInfo().value;
oi.probability = handler->getRMGInfo().rarity; oi.probability = handler->getRMGInfo().rarity;
oi.templ = temp;
possibleObjects.push_back (oi); possibleObjects.push_back (oi);
} }
} }
@@ -1484,6 +1546,7 @@ void CRmgTemplateZone::addAllPossibleObjects (CMapGenerator* gen)
return obj; return obj;
}; };
oi.templ = temp;
possibleObjects.push_back (oi); possibleObjects.push_back (oi);
} }
} }
@@ -1515,6 +1578,7 @@ void CRmgTemplateZone::addAllPossibleObjects (CMapGenerator* gen)
obj->storedArtifact = a; obj->storedArtifact = a;
return obj; return obj;
}; };
oi.setTemplate (Obj::SPELL_SCROLL, 0, terrainType);
oi.value = scrollValues[i]; oi.value = scrollValues[i];
oi.probability = 30; oi.probability = 30;
possibleObjects.push_back (oi); possibleObjects.push_back (oi);
@@ -1531,6 +1595,7 @@ void CRmgTemplateZone::addAllPossibleObjects (CMapGenerator* gen)
obj->resources[Res::GOLD] = i * 5000; obj->resources[Res::GOLD] = i * 5000;
return obj; return obj;
}; };
oi.setTemplate (Obj::PANDORAS_BOX, 0, terrainType);
oi.value = i * 5000;; oi.value = i * 5000;;
oi.probability = 5; oi.probability = 5;
possibleObjects.push_back (oi); possibleObjects.push_back (oi);
@@ -1547,6 +1612,7 @@ void CRmgTemplateZone::addAllPossibleObjects (CMapGenerator* gen)
obj->gainedExp = i * 5000; obj->gainedExp = i * 5000;
return obj; return obj;
}; };
oi.setTemplate (Obj::PANDORAS_BOX, 0, terrainType);
oi.value = i * 6000;; oi.value = i * 6000;;
oi.probability = 20; oi.probability = 20;
possibleObjects.push_back (oi); possibleObjects.push_back (oi);
@@ -1586,6 +1652,7 @@ void CRmgTemplateZone::addAllPossibleObjects (CMapGenerator* gen)
obj->creatures.putStack(SlotID(0), stack); obj->creatures.putStack(SlotID(0), stack);
return obj; return obj;
}; };
oi.setTemplate (Obj::PANDORAS_BOX, 0, terrainType);
oi.value = (2 * (creature->AIValue) * creaturesAmount * (1 + (float)(gen->getZoneCount(creature->faction)) / gen->getTotalZoneCount()))/3; //TODO: count number of towns on the map oi.value = (2 * (creature->AIValue) * creaturesAmount * (1 + (float)(gen->getZoneCount(creature->faction)) / gen->getTotalZoneCount()))/3; //TODO: count number of towns on the map
oi.probability = 3; oi.probability = 3;
possibleObjects.push_back (oi); possibleObjects.push_back (oi);
@@ -1616,6 +1683,7 @@ void CRmgTemplateZone::addAllPossibleObjects (CMapGenerator* gen)
return obj; return obj;
}; };
oi.setTemplate (Obj::PANDORAS_BOX, 0, terrainType);
oi.value = (i + 1) * 2500; //5000 - 15000 oi.value = (i + 1) * 2500; //5000 - 15000
oi.probability = 2; oi.probability = 2;
possibleObjects.push_back (oi); possibleObjects.push_back (oi);
@@ -1660,6 +1728,7 @@ void CRmgTemplateZone::addAllPossibleObjects (CMapGenerator* gen)
return obj; return obj;
}; };
oi.setTemplate (Obj::PANDORAS_BOX, 0, terrainType);
oi.value = 15000; oi.value = 15000;
oi.probability = 2; oi.probability = 2;
possibleObjects.push_back (oi); possibleObjects.push_back (oi);
@@ -1688,7 +1757,13 @@ void CRmgTemplateZone::addAllPossibleObjects (CMapGenerator* gen)
return obj; return obj;
}; };
oi.setTemplate (Obj::PANDORAS_BOX, 0, terrainType);
oi.value = 3000; oi.value = 3000;
oi.probability = 2; oi.probability = 2;
possibleObjects.push_back (oi); possibleObjects.push_back (oi);
} }
void ObjectInfo::setTemplate (si32 type, si32 subtype, ETerrainType terrainType)
{
templ = VLC->objtypeh->getHandlerFor(type, subtype)->getTemplates(terrainType).front();
}

View File

@@ -16,6 +16,7 @@
#include "float3.h" #include "float3.h"
#include "../int3.h" #include "../int3.h"
#include "../ResourceSet.h" //for TResource (?) #include "../ResourceSet.h" //for TResource (?)
#include "../mapObjects/ObjectTemplate.h"
class CMapGenerator; class CMapGenerator;
class CTileInfo; class CTileInfo;
@@ -67,9 +68,20 @@ public:
struct DLL_LINKAGE ObjectInfo struct DLL_LINKAGE ObjectInfo
{ {
ObjectTemplate templ;
ui32 value; ui32 value;
ui16 probability; ui16 probability;
std::function<CGObjectInstance *()> generateObject; std::function<CGObjectInstance *()> generateObject;
void setTemplate (si32 type, si32 subtype, ETerrainType terrain);
};
struct DLL_LINKAGE CTreasurePileInfo
{
std::set<int3> visitablePositions; //can be visited only from bottom or side
std::set<int3> visitableFromTopPositions; //they can be visited from any direction
std::set<int3> occupiedPositions;
int3 nextTreasurePos;
}; };
/// The CRmgTemplateZone describes a zone in a template. /// The CRmgTemplateZone describes a zone in a template.
@@ -155,7 +167,7 @@ public:
std::vector<CTreasureInfo> getTreasureInfo(); std::vector<CTreasureInfo> getTreasureInfo();
std::set<int3>* getFreePaths(); std::set<int3>* getFreePaths();
ObjectInfo getRandomObject (CMapGenerator* gen, ui32 value); ObjectInfo getRandomObject (CMapGenerator* gen, CTreasurePileInfo &info, ui32 value);
void placeAndGuardObject(CMapGenerator* gen, CGObjectInstance* object, const int3 &pos, si32 str); void placeAndGuardObject(CMapGenerator* gen, CGObjectInstance* object, const int3 &pos, si32 str);