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:
parent
8a8cda950d
commit
22d26db694
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
@ -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);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user