1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-01-26 03:52:01 +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
{
for(int y = 0; y < appearance.getHeight(); y++)
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);
return appearance.getVisitableOffset();
}
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
return true;
static bool isVisitableFromTop(int identifier, int type)
{
if(type == 2 || type == 3 || type == 4 || type == 5) //creature, hero, artifact, resource
return true;
static const Obj visitableFromTop[] =
{Obj::FLOTSAM,
Obj::SEA_CHEST,
Obj::SHIPWRECK_SURVIVOR,
Obj::BUOY,
Obj::OCEAN_BOTTLE,
Obj::BOAT,
Obj::WHIRLPOOL,
Obj::GARRISON,
Obj::GARRISON2,
Obj::SCHOLAR,
Obj::CAMPFIRE,
Obj::BORDERGUARD,
Obj::BORDER_GATE,
Obj::QUEST_GUARD,
Obj::CORPSE
};
if (vstd::find_pos(visitableFromTop, identifier) != -1)
return true;
return false;
static const Obj visitableFromTop[] =
{Obj::FLOTSAM,
Obj::SEA_CHEST,
Obj::SHIPWRECK_SURVIVOR,
Obj::BUOY,
Obj::OCEAN_BOTTLE,
Obj::BOAT,
Obj::WHIRLPOOL,
Obj::GARRISON,
Obj::GARRISON2,
Obj::SCHOLAR,
Obj::CAMPFIRE,
Obj::BORDERGUARD,
Obj::BORDER_GATE,
Obj::QUEST_GUARD,
Obj::CORPSE
};
if (vstd::find_pos(visitableFromTop, identifier) != -1)
return true;
return false;
}
}
ObjectTemplate::ObjectTemplate():
@ -106,7 +109,7 @@ void ObjectTemplate::readTxt(CLegacyConfigParser & parser)
int type = boost::lexical_cast<int>(strings[7]);
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;
else
visitDir = (8|16|32|64|128);
@ -168,7 +171,7 @@ void ObjectTemplate::readMap(CBinaryReader & reader)
int type = reader.readUInt8();
printPriority = reader.readUInt8() * 100; // to have some space in future
if (isVisitableFromTop(id, type))
if (vstd::isVisitableFromTop(id, type))
visitDir = 0xff;
else
visitDir = (8|16|32|64|128);
@ -354,6 +357,22 @@ bool ObjectTemplate::isVisitableFrom(si8 X, si8 Y) const
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
{
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
bool isVisitableFrom(si8 X, si8 Y) const;
int3 getVisitableOffset() const;
bool isVisitableFromTop() const;
// Checks if object can be placed on specific terrain
bool canBePlacedAt(ETerrainType terrain) const;

View File

@ -216,9 +216,9 @@ void CMapGenerator::fillZones()
logGlobal->infoStream() << "Started filling zones";
createConnections();
//make sure all connections are passable before creating borders
for (auto it : zones)
{
//make sure all connections are passable before creating borders
it.second->createBorder(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)
{
CTreasurePileInfo info;
std::map<int3, CGObjectInstance *> treasures;
std::set<int3> boundary;
int3 guardPos (-1,-1,-1);
int3 nextTreasurePos = pos;
info.nextTreasurePos = pos;
//default values
int maxValue = 5000;
@ -654,9 +656,8 @@ bool CRmgTemplateZone::createTreasurePile (CMapGenerator* gen, int3 &pos)
CGObjectInstance * object = nullptr;
while (currentValue < minValue)
{
//TODO: this works only for 1-tile objects
//make sure our shape is consistent
treasures[nextTreasurePos] = nullptr;
treasures[info.nextTreasurePos] = nullptr;
for (auto treasurePos : treasures)
{
gen->foreach_neighbour (treasurePos.first, [gen, &boundary](int3 pos)
@ -678,14 +679,30 @@ bool CRmgTemplateZone::createTreasurePile (CMapGenerator* gen, int3 &pos)
int remaining = maxValue - currentValue;
auto oi = getRandomObject(gen, remaining);
ObjectInfo oi = getRandomObject(gen, info, remaining);
object = oi.generateObject();
if (!object)
{
vstd::erase_if_present(treasures, info.nextTreasurePos);
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;
treasures[nextTreasurePos] = object;
treasures[info.nextTreasurePos] = object;
//now find place for next object
int3 placeFound(-1,-1,-1);
@ -712,8 +729,9 @@ bool CRmgTemplateZone::createTreasurePile (CMapGenerator* gen, int3 &pos)
}
}
if (placeFound.valid())
nextTreasurePos = placeFound;
info.nextTreasurePos = placeFound;
}
if (treasures.size())
{
//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)
{
bool objectFitsHere = true; //temporary workaround
int3 visitableOffset = treasure.second->getVisitableOffset();
std::set<int3> blockedOffsets = treasure.second->getBlockedOffsets();
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);
placeObject(gen, treasure.second, treasure.first + visitableOffset);
}
if (addMonster(gen, guardPos, currentValue))
{//block only if the object is guarded
@ -1362,7 +1367,7 @@ bool CRmgTemplateZone::guardObject(CMapGenerator* gen, CGObjectInstance* object,
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;
ui32 total = 0;
@ -1374,17 +1379,73 @@ ObjectInfo CRmgTemplateZone::getRandomObject (CMapGenerator* gen, ui32 value)
{
if (oi.value >= minValue && oi.value <= value)
{
//TODO: check place for non-removable object
//problem: we need at least template for the object that does not yet exist
int3 visitableOffset = oi.templ.getVisitableOffset(); //visitablePos assumes object will be shifter by visitableOffset
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;
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())
{
ObjectInfo oi;
if (minValue > 20000) //we don't have object valuable enough
{
oi.generateObject = [minValue]() -> CGObjectInstance *
@ -1395,6 +1456,7 @@ ObjectInfo CRmgTemplateZone::getRandomObject (CMapGenerator* gen, ui32 value)
obj->resources[Res::GOLD] = minValue;
return obj;
};
oi.setTemplate(Obj::PANDORAS_BOX, 0, terrainType);
oi.value = minValue;
oi.probability = 0;
}
@ -1404,6 +1466,7 @@ ObjectInfo CRmgTemplateZone::getRandomObject (CMapGenerator* gen, ui32 value)
{
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.probability = 0;
}
@ -1422,8 +1485,6 @@ ObjectInfo CRmgTemplateZone::getRandomObject (CMapGenerator* gen, ui32 value)
void CRmgTemplateZone::addAllPossibleObjects (CMapGenerator* gen)
{
//TODO: move typical objects to config
ObjectInfo oi;
for (auto primaryID : VLC->objtypeh->knownObjects())
@ -1443,6 +1504,7 @@ void CRmgTemplateZone::addAllPossibleObjects (CMapGenerator* gen)
};
oi.value = handler->getRMGInfo().value;
oi.probability = handler->getRMGInfo().rarity;
oi.templ = temp;
possibleObjects.push_back (oi);
}
}
@ -1484,6 +1546,7 @@ void CRmgTemplateZone::addAllPossibleObjects (CMapGenerator* gen)
return obj;
};
oi.templ = temp;
possibleObjects.push_back (oi);
}
}
@ -1515,6 +1578,7 @@ void CRmgTemplateZone::addAllPossibleObjects (CMapGenerator* gen)
obj->storedArtifact = a;
return obj;
};
oi.setTemplate (Obj::SPELL_SCROLL, 0, terrainType);
oi.value = scrollValues[i];
oi.probability = 30;
possibleObjects.push_back (oi);
@ -1531,6 +1595,7 @@ void CRmgTemplateZone::addAllPossibleObjects (CMapGenerator* gen)
obj->resources[Res::GOLD] = i * 5000;
return obj;
};
oi.setTemplate (Obj::PANDORAS_BOX, 0, terrainType);
oi.value = i * 5000;;
oi.probability = 5;
possibleObjects.push_back (oi);
@ -1547,6 +1612,7 @@ void CRmgTemplateZone::addAllPossibleObjects (CMapGenerator* gen)
obj->gainedExp = i * 5000;
return obj;
};
oi.setTemplate (Obj::PANDORAS_BOX, 0, terrainType);
oi.value = i * 6000;;
oi.probability = 20;
possibleObjects.push_back (oi);
@ -1586,6 +1652,7 @@ void CRmgTemplateZone::addAllPossibleObjects (CMapGenerator* gen)
obj->creatures.putStack(SlotID(0), stack);
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.probability = 3;
possibleObjects.push_back (oi);
@ -1616,6 +1683,7 @@ void CRmgTemplateZone::addAllPossibleObjects (CMapGenerator* gen)
return obj;
};
oi.setTemplate (Obj::PANDORAS_BOX, 0, terrainType);
oi.value = (i + 1) * 2500; //5000 - 15000
oi.probability = 2;
possibleObjects.push_back (oi);
@ -1660,6 +1728,7 @@ void CRmgTemplateZone::addAllPossibleObjects (CMapGenerator* gen)
return obj;
};
oi.setTemplate (Obj::PANDORAS_BOX, 0, terrainType);
oi.value = 15000;
oi.probability = 2;
possibleObjects.push_back (oi);
@ -1688,7 +1757,13 @@ void CRmgTemplateZone::addAllPossibleObjects (CMapGenerator* gen)
return obj;
};
oi.setTemplate (Obj::PANDORAS_BOX, 0, terrainType);
oi.value = 3000;
oi.probability = 2;
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 "../int3.h"
#include "../ResourceSet.h" //for TResource (?)
#include "../mapObjects/ObjectTemplate.h"
class CMapGenerator;
class CTileInfo;
@ -67,9 +68,20 @@ public:
struct DLL_LINKAGE ObjectInfo
{
ObjectTemplate templ;
ui32 value;
ui16 probability;
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.
@ -155,7 +167,7 @@ public:
std::vector<CTreasureInfo> getTreasureInfo();
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);