|
@@ -19,12 +19,14 @@
|
|
|
#include "../CCreatureHandler.h"
|
|
#include "../CCreatureHandler.h"
|
|
|
#include "../CSpellHandler.h" //for choosing random spells
|
|
#include "../CSpellHandler.h" //for choosing random spells
|
|
|
|
|
|
|
|
-#include "../mapObjects/CObjectClassesHandler.h"
|
|
|
|
|
|
|
+#include "../mapObjects/CommonConstructors.h"
|
|
|
|
|
+#include "../mapObjects/MapObjects.h" //needed to resolve templates for CommonConstructors.h
|
|
|
#include "../mapObjects/CGPandoraBox.h"
|
|
#include "../mapObjects/CGPandoraBox.h"
|
|
|
#include "../mapObjects/CRewardableObject.h"
|
|
#include "../mapObjects/CRewardableObject.h"
|
|
|
|
|
|
|
|
class CMap;
|
|
class CMap;
|
|
|
class CMapEditManager;
|
|
class CMapEditManager;
|
|
|
|
|
+//class CGObjectInstance;
|
|
|
|
|
|
|
|
CRmgTemplateZone::CTownInfo::CTownInfo() : townCount(0), castleCount(0), townDensity(0), castleDensity(0)
|
|
CRmgTemplateZone::CTownInfo::CTownInfo() : townCount(0), castleCount(0), townDensity(0), castleDensity(0)
|
|
|
{
|
|
{
|
|
@@ -109,6 +111,10 @@ bool CTileInfo::isFree() const
|
|
|
{
|
|
{
|
|
|
return occupied == ETileType::FREE;
|
|
return occupied == ETileType::FREE;
|
|
|
}
|
|
}
|
|
|
|
|
+bool CTileInfo::isUsed() const
|
|
|
|
|
+{
|
|
|
|
|
+ return occupied == ETileType::USED;
|
|
|
|
|
+}
|
|
|
void CTileInfo::setOccupied(ETileType::ETileType value)
|
|
void CTileInfo::setOccupied(ETileType::ETileType value)
|
|
|
{
|
|
{
|
|
|
occupied = value;
|
|
occupied = value;
|
|
@@ -354,6 +360,24 @@ std::set<int3> CRmgTemplateZone::getTileInfo () const
|
|
|
return tileinfo;
|
|
return tileinfo;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+void CRmgTemplateZone::discardDistantTiles (CMapGenerator* gen, float distance)
|
|
|
|
|
+{
|
|
|
|
|
+ //TODO: mark tiles beyond zone as unavailable, but allow to connect with adjacent zones
|
|
|
|
|
+
|
|
|
|
|
+ //for (auto tile : tileinfo)
|
|
|
|
|
+ //{
|
|
|
|
|
+ // if (tile.dist2d(this->pos) > distance)
|
|
|
|
|
+ // {
|
|
|
|
|
+ // gen->setOccupied(tile, ETileType::USED);
|
|
|
|
|
+ // //gen->setOccupied(tile, ETileType::BLOCKED); //fixme: crash at rendering?
|
|
|
|
|
+ // }
|
|
|
|
|
+ //}
|
|
|
|
|
+ vstd::erase_if (tileinfo, [distance, this](const int3 &tile) -> bool
|
|
|
|
|
+ {
|
|
|
|
|
+ return tile.dist2d(this->pos) > distance;
|
|
|
|
|
+ });
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
void CRmgTemplateZone::createBorder(CMapGenerator* gen)
|
|
void CRmgTemplateZone::createBorder(CMapGenerator* gen)
|
|
|
{
|
|
{
|
|
|
for (auto tile : tileinfo)
|
|
for (auto tile : tileinfo)
|
|
@@ -374,12 +398,13 @@ void CRmgTemplateZone::createBorder(CMapGenerator* gen)
|
|
|
|
|
|
|
|
void CRmgTemplateZone::fractalize(CMapGenerator* gen)
|
|
void CRmgTemplateZone::fractalize(CMapGenerator* gen)
|
|
|
{
|
|
{
|
|
|
- std::vector<int3> clearedTiles;
|
|
|
|
|
|
|
+ std::vector<int3> clearedTiles (freePaths.begin(), freePaths.end());
|
|
|
std::set<int3> possibleTiles;
|
|
std::set<int3> possibleTiles;
|
|
|
std::set<int3> tilesToClear; //will be set clear
|
|
std::set<int3> tilesToClear; //will be set clear
|
|
|
std::set<int3> tilesToIgnore; //will be erased in this iteration
|
|
std::set<int3> tilesToIgnore; //will be erased in this iteration
|
|
|
|
|
|
|
|
- const float minDistance = std::sqrt(totalDensity);
|
|
|
|
|
|
|
+ //the more treasure density, the greater distance between paths. Scaling is experimental.
|
|
|
|
|
+ const float minDistance = std::sqrt(totalDensity * 3);
|
|
|
for (auto tile : tileinfo)
|
|
for (auto tile : tileinfo)
|
|
|
{
|
|
{
|
|
|
if (gen->isFree(tile))
|
|
if (gen->isFree(tile))
|
|
@@ -387,10 +412,7 @@ void CRmgTemplateZone::fractalize(CMapGenerator* gen)
|
|
|
else if (gen->isPossible(tile))
|
|
else if (gen->isPossible(tile))
|
|
|
possibleTiles.insert(tile);
|
|
possibleTiles.insert(tile);
|
|
|
}
|
|
}
|
|
|
- if (clearedTiles.empty()) //this should come from zone connections
|
|
|
|
|
- {
|
|
|
|
|
- clearedTiles.push_back(pos); //zone center should be always clear
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ assert (clearedTiles.size()); //this should come from zone connections
|
|
|
|
|
|
|
|
while (possibleTiles.size())
|
|
while (possibleTiles.size())
|
|
|
{
|
|
{
|
|
@@ -440,6 +462,34 @@ void CRmgTemplateZone::fractalize(CMapGenerator* gen)
|
|
|
tilesToClear.clear(); //empty this container
|
|
tilesToClear.clear(); //empty this container
|
|
|
tilesToIgnore.clear();
|
|
tilesToIgnore.clear();
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ for (auto tile : clearedTiles)
|
|
|
|
|
+ {
|
|
|
|
|
+ freePaths.insert(tile);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ if (0) //enable to debug
|
|
|
|
|
+ {
|
|
|
|
|
+ std::ofstream out(boost::to_string(boost::format("zone %d") % id));
|
|
|
|
|
+ int levels = gen->map->twoLevel ? 2 : 1;
|
|
|
|
|
+ int width = gen->map->width;
|
|
|
|
|
+ int height = gen->map->height;
|
|
|
|
|
+ for (int k = 0; k < levels; k++)
|
|
|
|
|
+ {
|
|
|
|
|
+ for(int j=0; j<height; j++)
|
|
|
|
|
+ {
|
|
|
|
|
+ for (int i=0; i<width; i++)
|
|
|
|
|
+ {
|
|
|
|
|
+ out << (int)vstd::contains(freePaths, int3(i,j,k));
|
|
|
|
|
+ }
|
|
|
|
|
+ out << std::endl;
|
|
|
|
|
+ }
|
|
|
|
|
+ out << std::endl;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
logGlobal->infoStream() << boost::format ("Zone %d subdivided fractally") %id;
|
|
logGlobal->infoStream() << boost::format ("Zone %d subdivided fractally") %id;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -514,13 +564,13 @@ void CRmgTemplateZone::addRequiredObject(CGObjectInstance * obj, si32 strength)
|
|
|
requiredObjects.push_back(std::make_pair(obj, strength));
|
|
requiredObjects.push_back(std::make_pair(obj, strength));
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-bool CRmgTemplateZone::addMonster(CMapGenerator* gen, int3 &pos, si32 strength)
|
|
|
|
|
|
|
+bool CRmgTemplateZone::addMonster(CMapGenerator* gen, int3 &pos, si32 strength, bool clearSurroundingTiles, bool zoneGuard)
|
|
|
{
|
|
{
|
|
|
//precalculate actual (randomized) monster strength based on this post
|
|
//precalculate actual (randomized) monster strength based on this post
|
|
|
//http://forum.vcmi.eu/viewtopic.php?p=12426#12426
|
|
//http://forum.vcmi.eu/viewtopic.php?p=12426#12426
|
|
|
|
|
|
|
|
int mapMonsterStrength = gen->mapGenOptions->getMonsterStrength();
|
|
int mapMonsterStrength = gen->mapGenOptions->getMonsterStrength();
|
|
|
- int monsterStrength = zoneMonsterStrength + mapMonsterStrength - 1; //array index from 0 to 4
|
|
|
|
|
|
|
+ int monsterStrength = (zoneGuard ? 0 : zoneMonsterStrength) + mapMonsterStrength - 1; //array index from 0 to 4
|
|
|
static const int value1[] = {2500, 1500, 1000, 500, 0};
|
|
static const int value1[] = {2500, 1500, 1000, 500, 0};
|
|
|
static const int value2[] = {7500, 7500, 7500, 5000, 5000};
|
|
static const int value2[] = {7500, 7500, 7500, 5000, 5000};
|
|
|
static const float multiplier1[] = {0.5, 0.75, 1.0, 1.5, 1.5};
|
|
static const float multiplier1[] = {0.5, 0.75, 1.0, 1.5, 1.5};
|
|
@@ -566,16 +616,32 @@ bool CRmgTemplateZone::addMonster(CMapGenerator* gen, int3 &pos, si32 strength)
|
|
|
//will be set during initialization
|
|
//will be set during initialization
|
|
|
guard->putStack(SlotID(0), hlp);
|
|
guard->putStack(SlotID(0), hlp);
|
|
|
|
|
|
|
|
|
|
+ //logGlobal->traceStream() << boost::format ("Adding stack of %d %s. Map monster strenght %d, zone monster strength %d, base monster value %d")
|
|
|
|
|
+ // % amount % VLC->creh->creatures[creId]->namePl % mapMonsterStrength % zoneMonsterStrength % strength;
|
|
|
|
|
+
|
|
|
placeObject(gen, guard, pos);
|
|
placeObject(gen, guard, pos);
|
|
|
|
|
+
|
|
|
|
|
+ if (clearSurroundingTiles)
|
|
|
|
|
+ {
|
|
|
|
|
+ //do not spawn anything near monster
|
|
|
|
|
+ gen->foreach_neighbour (pos, [gen](int3 pos)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (gen->isPossible(pos))
|
|
|
|
|
+ gen->setOccupied(pos, ETileType::FREE);
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
return true;
|
|
return true;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
@@ -601,9 +667,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
|
|
|
|
|
- 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)
|
|
@@ -616,6 +681,7 @@ bool CRmgTemplateZone::createTreasurePile (CMapGenerator* gen, int3 &pos)
|
|
|
//leaving only boundary around objects
|
|
//leaving only boundary around objects
|
|
|
vstd::erase_if_present (boundary, treasurePos.first);
|
|
vstd::erase_if_present (boundary, treasurePos.first);
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
for (auto tile : boundary)
|
|
for (auto tile : boundary)
|
|
|
{
|
|
{
|
|
|
//we can't extend boundary anymore
|
|
//we can't extend boundary anymore
|
|
@@ -625,22 +691,46 @@ 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
|
|
|
|
|
+ {
|
|
|
|
|
+ //remove from possible objects
|
|
|
|
|
+ auto oiptr = std::find(possibleObjects.begin(), possibleObjects.end(), oi);
|
|
|
|
|
+ assert (oiptr != possibleObjects.end());
|
|
|
|
|
+ oiptr->maxPerZone--;
|
|
|
|
|
+ //TODO
|
|
|
|
|
+
|
|
|
|
|
+ //update treasure pile area
|
|
|
|
|
+ int3 visitablePos = info.nextTreasurePos;
|
|
|
|
|
+
|
|
|
|
|
+ //TODO: actually we need to check is object is either !blockVisit or removable after visit - this means object below can be accessed
|
|
|
|
|
+ if (oi.templ.isVisitableFromTop())
|
|
|
|
|
+ info.visitableFromTopPositions.insert(visitablePos); //can be accessed from any direction
|
|
|
|
|
+ else
|
|
|
|
|
+ info.visitableFromBottomPositions.insert(visitablePos); //can be accessed only from bottom or side
|
|
|
|
|
+
|
|
|
|
|
+ for (auto blockedOffset : oi.templ.getBlockedOffsets())
|
|
|
|
|
+ {
|
|
|
|
|
+ int3 blockPos = info.nextTreasurePos + blockedOffset + oi.templ.getVisitableOffset(); //object will be moved to align vistable pos to treasure pos
|
|
|
|
|
+ info.occupiedPositions.insert(blockPos);
|
|
|
|
|
+ info.blockedPositions.insert(blockPos);
|
|
|
|
|
+ }
|
|
|
|
|
+ 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);
|
|
|
|
|
|
|
|
- //FIXME: find out why teh following code causes crashes
|
|
|
|
|
- //std::vector<int3> boundaryVec(boundary.begin(), boundary.end());
|
|
|
|
|
- //RandomGeneratorUtil::randomShuffle(boundaryVec, gen->rand);
|
|
|
|
|
- //for (auto tile : boundaryVec)
|
|
|
|
|
for (auto tile : boundary)
|
|
for (auto tile : boundary)
|
|
|
{
|
|
{
|
|
|
if (gen->isPossible(tile)) //we can place new treasure only on possible tile
|
|
if (gen->isPossible(tile)) //we can place new treasure only on possible tile
|
|
@@ -659,25 +749,50 @@ 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
|
|
|
|
|
- int3 zoneCenter = getPos();
|
|
|
|
|
|
|
+ //find object closest to zone center, then connect it to the middle of the zone
|
|
|
|
|
+ int3 closestFreeTile (-1,-1,-1);
|
|
|
|
|
+ if (info.visitableFromBottomPositions.size()) //get random treasure tile, starting from objects accessible only from bottom
|
|
|
|
|
+ closestFreeTile = findClosestTile (freePaths, *RandomGeneratorUtil::nextItem(info.visitableFromBottomPositions, gen->rand));
|
|
|
|
|
+ else
|
|
|
|
|
+ closestFreeTile = findClosestTile (freePaths, *RandomGeneratorUtil::nextItem(info.visitableFromTopPositions, gen->rand));
|
|
|
|
|
+
|
|
|
int3 closestTile = int3(-1,-1,-1);
|
|
int3 closestTile = int3(-1,-1,-1);
|
|
|
float minDistance = 1e10;
|
|
float minDistance = 1e10;
|
|
|
- for (auto treasure : treasures)
|
|
|
|
|
|
|
+ for (auto visitablePos : info.visitableFromBottomPositions) //objects that are not visitable from top must be accessible from bottom or side
|
|
|
{
|
|
{
|
|
|
- if (zoneCenter.dist2d(treasure.first) < minDistance)
|
|
|
|
|
|
|
+ if (closestFreeTile.dist2d(visitablePos) < minDistance)
|
|
|
{
|
|
{
|
|
|
- closestTile = treasure.first;
|
|
|
|
|
- minDistance = zoneCenter.dist2d(treasure.first);
|
|
|
|
|
|
|
+ closestTile = visitablePos + int3 (0, 1, 0); //start below object (y+1), possibly even outside the map (?)
|
|
|
|
|
+ minDistance = closestFreeTile.dist2d(visitablePos);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!closestTile.valid())
|
|
|
|
|
+ {
|
|
|
|
|
+ for (auto visitablePos : info.visitableFromTopPositions) //all objects are accessible from any direction
|
|
|
|
|
+ {
|
|
|
|
|
+ if (closestFreeTile.dist2d(visitablePos) < minDistance)
|
|
|
|
|
+ {
|
|
|
|
|
+ closestTile = visitablePos;
|
|
|
|
|
+ minDistance = closestFreeTile.dist2d(visitablePos);
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
assert (closestTile.valid());
|
|
assert (closestTile.valid());
|
|
|
- if (!crunchPath (gen, closestTile, getPos(), id)) //make sure pile is connected to the middle of zone
|
|
|
|
|
|
|
+
|
|
|
|
|
+ for (auto tile : info.occupiedPositions)
|
|
|
{
|
|
{
|
|
|
|
|
+ if (gen->map->isInTheMap(tile)) //pile boundary may reach map border
|
|
|
|
|
+ gen->setOccupied(tile, ETileType::BLOCKED); //so that crunch path doesn't cut through objects
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (!crunchPath (gen, closestTile, closestFreeTile, id))
|
|
|
|
|
+ {
|
|
|
|
|
+ //we can't connect this pile, just block it off and start over
|
|
|
for (auto treasure : treasures)
|
|
for (auto treasure : treasures)
|
|
|
{
|
|
{
|
|
|
if (gen->isPossible(treasure.first))
|
|
if (gen->isPossible(treasure.first))
|
|
@@ -686,6 +801,25 @@ bool CRmgTemplateZone::createTreasurePile (CMapGenerator* gen, int3 &pos)
|
|
|
return true;
|
|
return true;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ //update boundary around our objects, including knowledge about objects visitable from bottom
|
|
|
|
|
+
|
|
|
|
|
+ boundary.clear();
|
|
|
|
|
+ for (auto tile : info.visitableFromBottomPositions)
|
|
|
|
|
+ {
|
|
|
|
|
+ gen->foreach_neighbour (tile, [tile, &boundary](int3 pos)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (pos.y >= tile.y) //don't block these objects from above
|
|
|
|
|
+ boundary.insert(pos);
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+ for (auto tile : info.visitableFromTopPositions)
|
|
|
|
|
+ {
|
|
|
|
|
+ gen->foreach_neighbour (tile, [&boundary](int3 pos)
|
|
|
|
|
+ {
|
|
|
|
|
+ boundary.insert(pos);
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
for (auto tile : boundary) //guard must be standing there
|
|
for (auto tile : boundary) //guard must be standing there
|
|
|
{
|
|
{
|
|
|
if (gen->isFree(tile)) //this tile could be already blocked, don't place a monster here
|
|
if (gen->isFree(tile)) //this tile could be already blocked, don't place a monster here
|
|
@@ -699,15 +833,22 @@ bool CRmgTemplateZone::createTreasurePile (CMapGenerator* gen, int3 &pos)
|
|
|
{
|
|
{
|
|
|
for (auto treasure : treasures)
|
|
for (auto treasure : treasures)
|
|
|
{
|
|
{
|
|
|
- placeObject(gen, treasure.second, treasure.first - treasure.second->getVisitableOffset());
|
|
|
|
|
|
|
+ int3 visitableOffset = treasure.second->getVisitableOffset();
|
|
|
|
|
+ placeObject(gen, treasure.second, treasure.first + visitableOffset);
|
|
|
}
|
|
}
|
|
|
- if (addMonster(gen, guardPos, currentValue))
|
|
|
|
|
|
|
+ if (addMonster(gen, guardPos, currentValue, false))
|
|
|
{//block only if the object is guarded
|
|
{//block only if the object is guarded
|
|
|
for (auto tile : boundary)
|
|
for (auto tile : boundary)
|
|
|
{
|
|
{
|
|
|
if (gen->isPossible(tile))
|
|
if (gen->isPossible(tile))
|
|
|
gen->setOccupied (tile, ETileType::BLOCKED);
|
|
gen->setOccupied (tile, ETileType::BLOCKED);
|
|
|
}
|
|
}
|
|
|
|
|
+ //do not spawn anything near monster
|
|
|
|
|
+ gen->foreach_neighbour (guardPos, [gen](int3 pos)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (gen->isPossible(pos))
|
|
|
|
|
+ gen->setOccupied(pos, ETileType::FREE);
|
|
|
|
|
+ });
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
else //we couldn't make a connection to this location, block it
|
|
else //we couldn't make a connection to this location, block it
|
|
@@ -746,8 +887,15 @@ void CRmgTemplateZone::initTownType (CMapGenerator* gen)
|
|
|
town->builtBuildings.insert(BuildingID::FORT);
|
|
town->builtBuildings.insert(BuildingID::FORT);
|
|
|
town->builtBuildings.insert(BuildingID::DEFAULT);
|
|
town->builtBuildings.insert(BuildingID::DEFAULT);
|
|
|
|
|
|
|
|
- if (!totalTowns) //first town in zone goes in the middle
|
|
|
|
|
- placeObject(gen, town, getPos() + town->getVisitableOffset());
|
|
|
|
|
|
|
+ if (!totalTowns)
|
|
|
|
|
+ {
|
|
|
|
|
+ //first town in zone sets the facton of entire zone
|
|
|
|
|
+ town->subID = townType;
|
|
|
|
|
+ //register MAIN town of zone
|
|
|
|
|
+ gen->registerZone(town->subID);
|
|
|
|
|
+ //first town in zone goes in the middle
|
|
|
|
|
+ placeAndGuardObject(gen, town, getPos() + town->getVisitableOffset(), 0);
|
|
|
|
|
+ }
|
|
|
else
|
|
else
|
|
|
addRequiredObject (town);
|
|
addRequiredObject (town);
|
|
|
totalTowns++;
|
|
totalTowns++;
|
|
@@ -776,9 +924,12 @@ void CRmgTemplateZone::initTownType (CMapGenerator* gen)
|
|
|
town->tempOwner = player;
|
|
town->tempOwner = player;
|
|
|
town->builtBuildings.insert(BuildingID::FORT);
|
|
town->builtBuildings.insert(BuildingID::FORT);
|
|
|
town->builtBuildings.insert(BuildingID::DEFAULT);
|
|
town->builtBuildings.insert(BuildingID::DEFAULT);
|
|
|
- placeObject(gen, town, getPos() + town->getVisitableOffset()); //towns are big objects and should be centered around visitable position
|
|
|
|
|
|
|
+ //towns are big objects and should be centered around visitable position
|
|
|
|
|
+ placeAndGuardObject(gen, town, getPos() + town->getVisitableOffset(), 0); //generate no guards, but free path to entrance
|
|
|
|
|
|
|
|
totalTowns++;
|
|
totalTowns++;
|
|
|
|
|
+ //register MAIN town of zone only
|
|
|
|
|
+ gen->registerZone (town->subID);
|
|
|
|
|
|
|
|
logGlobal->traceStream() << "Fill player info " << player_id;
|
|
logGlobal->traceStream() << "Fill player info " << player_id;
|
|
|
|
|
|
|
@@ -819,7 +970,23 @@ void CRmgTemplateZone::initTerrainType (CMapGenerator* gen)
|
|
|
else
|
|
else
|
|
|
terrainType = *RandomGeneratorUtil::nextItem(terrainTypes, gen->rand);
|
|
terrainType = *RandomGeneratorUtil::nextItem(terrainTypes, gen->rand);
|
|
|
|
|
|
|
|
- //paint zone with matching terrain
|
|
|
|
|
|
|
+ //TODO: allow new types of terrain?
|
|
|
|
|
+ if (pos.z)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (terrainType != ETerrainType::LAVA)
|
|
|
|
|
+ terrainType = ETerrainType::SUBTERRANEAN;
|
|
|
|
|
+ }
|
|
|
|
|
+ else
|
|
|
|
|
+ {
|
|
|
|
|
+ if (terrainType == ETerrainType::SUBTERRANEAN)
|
|
|
|
|
+ terrainType = ETerrainType::DIRT;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ paintZoneTerrain (gen, terrainType);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+void CRmgTemplateZone::paintZoneTerrain (CMapGenerator* gen, ETerrainType terrainType)
|
|
|
|
|
+{
|
|
|
std::vector<int3> tiles;
|
|
std::vector<int3> tiles;
|
|
|
for (auto tile : tileinfo)
|
|
for (auto tile : tileinfo)
|
|
|
{
|
|
{
|
|
@@ -893,8 +1060,8 @@ bool CRmgTemplateZone::createRequiredObjects(CMapGenerator* gen)
|
|
|
}
|
|
}
|
|
|
logGlobal->traceStream() << "Place found";
|
|
logGlobal->traceStream() << "Place found";
|
|
|
|
|
|
|
|
- placeObject(gen, obj.first, pos);
|
|
|
|
|
- guardObject (gen, obj.first, obj.second);
|
|
|
|
|
|
|
+ placeObject (gen, obj.first, pos);
|
|
|
|
|
+ guardObject (gen, obj.first, obj.second, (obj.first->ID == Obj::MONOLITH_TWO_WAY));
|
|
|
}
|
|
}
|
|
|
return true;
|
|
return true;
|
|
|
}
|
|
}
|
|
@@ -916,7 +1083,40 @@ void CRmgTemplateZone::createTreasures(CMapGenerator* gen)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void CRmgTemplateZone::createObstacles(CMapGenerator* gen)
|
|
void CRmgTemplateZone::createObstacles(CMapGenerator* gen)
|
|
|
-{
|
|
|
|
|
|
|
+{
|
|
|
|
|
+ if (pos.z) //underground
|
|
|
|
|
+ {
|
|
|
|
|
+ std::vector<int3> rockTiles;
|
|
|
|
|
+
|
|
|
|
|
+ for (auto tile : tileinfo)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (gen->shouldBeBlocked(tile))
|
|
|
|
|
+ {
|
|
|
|
|
+ bool placeRock = true;
|
|
|
|
|
+ gen->foreach_neighbour (tile, [gen, &placeRock](int3 &pos)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (!(gen->shouldBeBlocked(pos) || gen->isPossible(pos)))
|
|
|
|
|
+ placeRock = false;
|
|
|
|
|
+ });
|
|
|
|
|
+ if (placeRock)
|
|
|
|
|
+ {
|
|
|
|
|
+ rockTiles.push_back(tile);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ gen->editManager->getTerrainSelection().setSelection(rockTiles);
|
|
|
|
|
+ gen->editManager->drawTerrain(ETerrainType::ROCK, &gen->rand);
|
|
|
|
|
+ //for (auto tile : rockTiles)
|
|
|
|
|
+ //{
|
|
|
|
|
+ // gen->setOccupied (tile, ETileType::USED);
|
|
|
|
|
+ // gen->foreach_neighbour (tile, [gen](int3 &pos)
|
|
|
|
|
+ // {
|
|
|
|
|
+ // if (!gen->isUsed(pos))
|
|
|
|
|
+ // gen->setOccupied (pos, ETileType::BLOCKED);
|
|
|
|
|
+ // });
|
|
|
|
|
+ //}
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
//get all possible obstacles for this terrain
|
|
//get all possible obstacles for this terrain
|
|
|
for (auto primaryID : VLC->objtypeh->knownObjects())
|
|
for (auto primaryID : VLC->objtypeh->knownObjects())
|
|
|
{
|
|
{
|
|
@@ -940,7 +1140,7 @@ void CRmgTemplateZone::createObstacles(CMapGenerator* gen)
|
|
|
auto tryToPlaceObstacleHere = [this, gen](int3& tile)-> bool
|
|
auto tryToPlaceObstacleHere = [this, gen](int3& tile)-> bool
|
|
|
{
|
|
{
|
|
|
auto temp = *RandomGeneratorUtil::nextItem(possibleObstacles, gen->rand);
|
|
auto temp = *RandomGeneratorUtil::nextItem(possibleObstacles, gen->rand);
|
|
|
- int3 obstaclePos = tile + temp.getBlockMapOffset();
|
|
|
|
|
|
|
+ int3 obstaclePos = tile - temp.getBlockMapOffset();
|
|
|
if (canObstacleBePlacedHere(gen, temp, obstaclePos)) //can be placed here
|
|
if (canObstacleBePlacedHere(gen, temp, obstaclePos)) //can be placed here
|
|
|
{
|
|
{
|
|
|
auto obj = VLC->objtypeh->getHandlerFor(temp.id, temp.subid)->create(temp);
|
|
auto obj = VLC->objtypeh->getHandlerFor(temp.id, temp.subid)->create(temp);
|
|
@@ -966,9 +1166,12 @@ void CRmgTemplateZone::createObstacles(CMapGenerator* gen)
|
|
|
|
|
|
|
|
bool CRmgTemplateZone::fill(CMapGenerator* gen)
|
|
bool CRmgTemplateZone::fill(CMapGenerator* gen)
|
|
|
{
|
|
{
|
|
|
- addAllPossibleObjects (gen);
|
|
|
|
|
initTownType(gen);
|
|
initTownType(gen);
|
|
|
initTerrainType(gen);
|
|
initTerrainType(gen);
|
|
|
|
|
+
|
|
|
|
|
+ freePaths.insert(pos); //zone center should be always clear to allow other tiles to connect
|
|
|
|
|
+
|
|
|
|
|
+ addAllPossibleObjects (gen);
|
|
|
placeMines(gen);
|
|
placeMines(gen);
|
|
|
createRequiredObjects(gen);
|
|
createRequiredObjects(gen);
|
|
|
fractalize(gen); //after required objects are created and linked with their own paths
|
|
fractalize(gen); //after required objects are created and linked with their own paths
|
|
@@ -1017,19 +1220,45 @@ bool CRmgTemplateZone::findPlaceForTreasurePile(CMapGenerator* gen, si32 min_dis
|
|
|
|
|
|
|
|
bool CRmgTemplateZone::canObstacleBePlacedHere(CMapGenerator* gen, ObjectTemplate &temp, int3 &pos)
|
|
bool CRmgTemplateZone::canObstacleBePlacedHere(CMapGenerator* gen, ObjectTemplate &temp, int3 &pos)
|
|
|
{
|
|
{
|
|
|
|
|
+ if (!gen->map->isInTheMap(pos)) //blockmap may fit in the map, but botom-right corner does not
|
|
|
|
|
+ return false;
|
|
|
|
|
+
|
|
|
auto tilesBlockedByObject = temp.getBlockedOffsets();
|
|
auto tilesBlockedByObject = temp.getBlockedOffsets();
|
|
|
|
|
|
|
|
- bool allTilesAvailable = true;
|
|
|
|
|
for (auto blockingTile : tilesBlockedByObject)
|
|
for (auto blockingTile : tilesBlockedByObject)
|
|
|
{
|
|
{
|
|
|
int3 t = pos + blockingTile;
|
|
int3 t = pos + blockingTile;
|
|
|
if (!gen->map->isInTheMap(t) || !(gen->isPossible(t) || gen->shouldBeBlocked(t)))
|
|
if (!gen->map->isInTheMap(t) || !(gen->isPossible(t) || gen->shouldBeBlocked(t)))
|
|
|
{
|
|
{
|
|
|
- allTilesAvailable = false; //if at least one tile is not possible, object can't be placed here
|
|
|
|
|
- break;
|
|
|
|
|
|
|
+ return false; //if at least one tile is not possible, object can't be placed here
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
- return allTilesAvailable;
|
|
|
|
|
|
|
+ return true;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+bool CRmgTemplateZone::isAccessibleFromAnywhere (CMapGenerator* gen, ObjectTemplate &appearance, int3 &tile, const std::set<int3> &tilesBlockedByObject) const
|
|
|
|
|
+{
|
|
|
|
|
+ bool accessible = false;
|
|
|
|
|
+ for (int x = -1; x < 2; x++)
|
|
|
|
|
+ {
|
|
|
|
|
+ for (int y = -1; y <2; y++)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (x && y) //check only if object is visitable from another tile
|
|
|
|
|
+ {
|
|
|
|
|
+ int3 offset = appearance.getVisitableOffset() + int3(x, y, 0);
|
|
|
|
|
+ if (!vstd::contains(tilesBlockedByObject, offset))
|
|
|
|
|
+ {
|
|
|
|
|
+ int3 nearbyPos = tile + offset;
|
|
|
|
|
+ if (gen->map->isInTheMap(nearbyPos))
|
|
|
|
|
+ {
|
|
|
|
|
+ if (appearance.isVisitableFrom(x, y) && !gen->isBlocked(nearbyPos))
|
|
|
|
|
+ accessible = true;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ };
|
|
|
|
|
+ }
|
|
|
|
|
+ return accessible;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
bool CRmgTemplateZone::findPlaceForObject(CMapGenerator* gen, CGObjectInstance* obj, si32 min_dist, int3 &pos)
|
|
bool CRmgTemplateZone::findPlaceForObject(CMapGenerator* gen, CGObjectInstance* obj, si32 min_dist, int3 &pos)
|
|
@@ -1057,25 +1286,7 @@ bool CRmgTemplateZone::findPlaceForObject(CMapGenerator* gen, CGObjectInstance*
|
|
|
for (auto tile : tileinfo)
|
|
for (auto tile : tileinfo)
|
|
|
{
|
|
{
|
|
|
//object must be accessible from at least one surounding tile
|
|
//object must be accessible from at least one surounding tile
|
|
|
- bool accessible = false;
|
|
|
|
|
- for (int x = -1; x < 2; x++)
|
|
|
|
|
- for (int y = -1; y <2; y++)
|
|
|
|
|
- {
|
|
|
|
|
- if (x && y) //check only if object is visitable from another tile
|
|
|
|
|
- {
|
|
|
|
|
- int3 offset = obj->getVisitableOffset() + int3(x, y, 0);
|
|
|
|
|
- if (!vstd::contains(tilesBlockedByObject, offset))
|
|
|
|
|
- {
|
|
|
|
|
- int3 nearbyPos = tile + offset;
|
|
|
|
|
- if (gen->map->isInTheMap(nearbyPos))
|
|
|
|
|
- {
|
|
|
|
|
- if (obj->appearance.isVisitableFrom(x, y) && !gen->isBlocked(nearbyPos))
|
|
|
|
|
- accessible = true;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- };
|
|
|
|
|
- if (!accessible)
|
|
|
|
|
|
|
+ if (!isAccessibleFromAnywhere(gen, obj->appearance, tile, tilesBlockedByObject))
|
|
|
continue;
|
|
continue;
|
|
|
|
|
|
|
|
auto ti = gen->getTile(tile);
|
|
auto ti = gen->getTile(tile);
|
|
@@ -1111,7 +1322,7 @@ bool CRmgTemplateZone::findPlaceForObject(CMapGenerator* gen, CGObjectInstance*
|
|
|
void CRmgTemplateZone::checkAndPlaceObject(CMapGenerator* gen, CGObjectInstance* object, const int3 &pos)
|
|
void CRmgTemplateZone::checkAndPlaceObject(CMapGenerator* gen, CGObjectInstance* object, const int3 &pos)
|
|
|
{
|
|
{
|
|
|
if (!gen->map->isInTheMap(pos))
|
|
if (!gen->map->isInTheMap(pos))
|
|
|
- throw rmgException(boost::to_string(boost::format("Position of object %d at %s is outside the map") % object->id % object->pos()));
|
|
|
|
|
|
|
+ throw rmgException(boost::to_string(boost::format("Position of object %d at %s is outside the map") % object->id % pos));
|
|
|
object->pos = pos;
|
|
object->pos = pos;
|
|
|
|
|
|
|
|
if (object->isVisitable() && !gen->map->isInTheMap(object->visitablePos()))
|
|
if (object->isVisitable() && !gen->map->isInTheMap(object->visitablePos()))
|
|
@@ -1124,9 +1335,10 @@ void CRmgTemplateZone::checkAndPlaceObject(CMapGenerator* gen, CGObjectInstance*
|
|
|
|
|
|
|
|
if (object->appearance.id == Obj::NO_OBJ)
|
|
if (object->appearance.id == Obj::NO_OBJ)
|
|
|
{
|
|
{
|
|
|
- auto templates = VLC->objtypeh->getHandlerFor(object->ID, object->subID)->getTemplates(gen->map->getTile(pos).terType);
|
|
|
|
|
|
|
+ auto terrainType = gen->map->getTile(pos).terType;
|
|
|
|
|
+ auto templates = VLC->objtypeh->getHandlerFor(object->ID, object->subID)->getTemplates(terrainType);
|
|
|
if (templates.empty())
|
|
if (templates.empty())
|
|
|
- throw rmgException(boost::to_string(boost::format("Did not find graphics for object (%d,%d) at %s") %object->ID %object->subID %pos));
|
|
|
|
|
|
|
+ throw rmgException(boost::to_string(boost::format("Did not find graphics for object (%d,%d) at %s (terrain %d)") %object->ID %object->subID %pos %terrainType));
|
|
|
|
|
|
|
|
object->appearance = templates.front();
|
|
object->appearance = templates.front();
|
|
|
}
|
|
}
|
|
@@ -1160,6 +1372,12 @@ void CRmgTemplateZone::placeObject(CMapGenerator* gen, CGObjectInstance* object,
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+void CRmgTemplateZone::placeAndGuardObject(CMapGenerator* gen, CGObjectInstance* object, const int3 &pos, si32 str, bool zoneGuard)
|
|
|
|
|
+{
|
|
|
|
|
+ placeObject(gen, object, pos);
|
|
|
|
|
+ guardObject(gen, object, str, zoneGuard);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
std::vector<int3> CRmgTemplateZone::getAccessibleOffsets (CMapGenerator* gen, CGObjectInstance* object)
|
|
std::vector<int3> CRmgTemplateZone::getAccessibleOffsets (CMapGenerator* gen, CGObjectInstance* object)
|
|
|
{
|
|
{
|
|
|
//get all tiles from which this object can be accessed
|
|
//get all tiles from which this object can be accessed
|
|
@@ -1186,7 +1404,7 @@ std::vector<int3> CRmgTemplateZone::getAccessibleOffsets (CMapGenerator* gen, CG
|
|
|
return tiles;
|
|
return tiles;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-bool CRmgTemplateZone::guardObject(CMapGenerator* gen, CGObjectInstance* object, si32 str)
|
|
|
|
|
|
|
+bool CRmgTemplateZone::guardObject(CMapGenerator* gen, CGObjectInstance* object, si32 str, bool zoneGuard)
|
|
|
{
|
|
{
|
|
|
logGlobal->traceStream() << boost::format("Guard object at %s") % object->pos();
|
|
logGlobal->traceStream() << boost::format("Guard object at %s") % object->pos();
|
|
|
|
|
|
|
@@ -1197,7 +1415,7 @@ bool CRmgTemplateZone::guardObject(CMapGenerator* gen, CGObjectInstance* object,
|
|
|
for (auto tile : tiles)
|
|
for (auto tile : tiles)
|
|
|
{
|
|
{
|
|
|
//crunching path may fail if center of teh zone is dirrectly over wide object
|
|
//crunching path may fail if center of teh zone is dirrectly over wide object
|
|
|
- if (crunchPath (gen, tile, getPos(), id)) //make sure object is accessible before surrounding it with blocked tiles
|
|
|
|
|
|
|
+ if (crunchPath (gen, tile, findClosestTile(freePaths, tile), id)) //make sure object is accessible before surrounding it with blocked tiles
|
|
|
{
|
|
{
|
|
|
guardTile = tile;
|
|
guardTile = tile;
|
|
|
break;
|
|
break;
|
|
@@ -1209,7 +1427,7 @@ bool CRmgTemplateZone::guardObject(CMapGenerator* gen, CGObjectInstance* object,
|
|
|
return false;
|
|
return false;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- if (addMonster (gen, guardTile, str)) //do not place obstacles around unguarded object
|
|
|
|
|
|
|
+ if (addMonster (gen, guardTile, str, false, zoneGuard)) //do not place obstacles around unguarded object
|
|
|
{
|
|
{
|
|
|
for (auto pos : tiles)
|
|
for (auto pos : tiles)
|
|
|
{
|
|
{
|
|
@@ -1222,14 +1440,17 @@ bool CRmgTemplateZone::guardObject(CMapGenerator* gen, CGObjectInstance* object,
|
|
|
else //allow no guard or other object in front of this object
|
|
else //allow no guard or other object in front of this object
|
|
|
{
|
|
{
|
|
|
for (auto tile : tiles)
|
|
for (auto tile : tiles)
|
|
|
- gen->setOccupied (tile, ETileType::FREE);
|
|
|
|
|
|
|
+ if (gen->isPossible(pos))
|
|
|
|
|
+ gen->setOccupied (tile, ETileType::FREE);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
return true;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-ObjectInfo CRmgTemplateZone::getRandomObject (CMapGenerator* gen, ui32 value)
|
|
|
|
|
|
|
+ObjectInfo CRmgTemplateZone::getRandomObject (CMapGenerator* gen, CTreasurePileInfo &info, ui32 value)
|
|
|
{
|
|
{
|
|
|
|
|
+ //int objectsVisitableFromBottom = 0; //for debug
|
|
|
|
|
+
|
|
|
std::vector<std::pair<ui32, ObjectInfo>> tresholds;
|
|
std::vector<std::pair<ui32, ObjectInfo>> tresholds;
|
|
|
ui32 total = 0;
|
|
ui32 total = 0;
|
|
|
|
|
|
|
@@ -1238,19 +1459,106 @@ ObjectInfo CRmgTemplateZone::getRandomObject (CMapGenerator* gen, ui32 value)
|
|
|
//roulette wheel
|
|
//roulette wheel
|
|
|
for (auto oi : possibleObjects)
|
|
for (auto oi : possibleObjects)
|
|
|
{
|
|
{
|
|
|
- if (oi.value >= minValue && oi.value <= value)
|
|
|
|
|
|
|
+ if (oi.value >= minValue && oi.value <= value && oi.maxPerZone > 0)
|
|
|
{
|
|
{
|
|
|
- //TODO: check place for non-removable object
|
|
|
|
|
- //problem: we need at least template for the object that does not yet exist
|
|
|
|
|
|
|
+ int3 newVisitableOffset = oi.templ.getVisitableOffset(); //visitablePos assumes object will be shifter by visitableOffset
|
|
|
|
|
+ int3 newVisitablePos = info.nextTreasurePos;
|
|
|
|
|
+
|
|
|
|
|
+ if (!oi.templ.isVisitableFromTop())
|
|
|
|
|
+ {
|
|
|
|
|
+ //objectsVisitableFromBottom++;
|
|
|
|
|
+ //there must be free tiles under object
|
|
|
|
|
+ auto blockedOffsets = oi.templ.getBlockedOffsets();
|
|
|
|
|
+ if (!isAccessibleFromAnywhere(gen, oi.templ, newVisitablePos, blockedOffsets))
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ //NOTE: y coordinate grows downwards
|
|
|
|
|
+ if (info.visitableFromBottomPositions.size() + info.visitableFromTopPositions.size()) //do not try to match first object in zone
|
|
|
|
|
+ {
|
|
|
|
|
+ bool fitsHere = false;
|
|
|
|
|
+
|
|
|
|
|
+ if (oi.templ.isVisitableFromTop()) //new can be accessed from any direction
|
|
|
|
|
+ {
|
|
|
|
|
+ for (auto tile : info.visitableFromTopPositions)
|
|
|
|
|
+ {
|
|
|
|
|
+ int3 actualTile = tile + newVisitableOffset;
|
|
|
|
|
+ if (newVisitablePos.areNeighbours(actualTile)) //we access other removable object from any position
|
|
|
|
|
+ {
|
|
|
|
|
+ fitsHere = true;
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ for (auto tile : info.visitableFromBottomPositions)
|
|
|
|
|
+ {
|
|
|
|
|
+ int3 actualTile = tile + newVisitableOffset;
|
|
|
|
|
+ if (newVisitablePos.areNeighbours(actualTile) && newVisitablePos.y >= actualTile.y) //we access existing static object from side or bottom only
|
|
|
|
|
+ {
|
|
|
|
|
+ fitsHere = true;
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ else //if new object is not visitable from top, it must be accessible from below or side
|
|
|
|
|
+ {
|
|
|
|
|
+ for (auto tile : info.visitableFromTopPositions)
|
|
|
|
|
+ {
|
|
|
|
|
+ int3 actualTile = tile + newVisitableOffset;
|
|
|
|
|
+ if (newVisitablePos.areNeighbours(actualTile) && newVisitablePos.y <= actualTile.y) //we access existing removable object from top or side only
|
|
|
|
|
+ {
|
|
|
|
|
+ fitsHere = true;
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ for (auto tile : info.visitableFromBottomPositions)
|
|
|
|
|
+ {
|
|
|
|
|
+ int3 actualTile = tile + newVisitableOffset;
|
|
|
|
|
+ if (newVisitablePos.areNeighbours(actualTile) && newVisitablePos.y == actualTile.y) //we access other static object from side only
|
|
|
|
|
+ {
|
|
|
|
|
+ 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 (newVisitableOffset);
|
|
|
|
|
+ for (auto blockingTile : blockedOffsets)
|
|
|
|
|
+ {
|
|
|
|
|
+ int3 t = info.nextTreasurePos + newVisitableOffset + 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
|
|
|
|
|
|
|
+ //logGlobal->infoStream() << boost::format ("Number of objects visitable from bottom: %d") % objectsVisitableFromBottom;
|
|
|
|
|
+
|
|
|
|
|
+ //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 *
|
|
@@ -1261,6 +1569,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;
|
|
|
}
|
|
}
|
|
@@ -1270,6 +1579,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;
|
|
|
}
|
|
}
|
|
@@ -1288,120 +1598,78 @@ 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;
|
|
|
|
|
+ oi.maxPerMap = std::numeric_limits<ui32>().max();
|
|
|
|
|
|
|
|
- static const Res::ERes preciousRes[] = {Res::ERes::CRYSTAL, Res::ERes::GEMS, Res::ERes::MERCURY, Res::ERes::SULFUR};
|
|
|
|
|
- for (int i = 0; i < 4; i++)
|
|
|
|
|
- {
|
|
|
|
|
- oi.generateObject = [i, gen]() -> CGObjectInstance *
|
|
|
|
|
- {
|
|
|
|
|
- auto obj = new CGResource();
|
|
|
|
|
- obj->ID = Obj::RESOURCE;
|
|
|
|
|
- obj->subID = static_cast<si32>(preciousRes[i]);
|
|
|
|
|
- obj->amount = 0;
|
|
|
|
|
- return obj;
|
|
|
|
|
- };
|
|
|
|
|
- oi.value = 1400;
|
|
|
|
|
- oi.probability = 300;
|
|
|
|
|
- possibleObjects.push_back (oi);
|
|
|
|
|
|
|
+ for (auto primaryID : VLC->objtypeh->knownObjects())
|
|
|
|
|
+ {
|
|
|
|
|
+ for (auto secondaryID : VLC->objtypeh->knownSubObjects(primaryID))
|
|
|
|
|
+ {
|
|
|
|
|
+ auto handler = VLC->objtypeh->getHandlerFor(primaryID, secondaryID);
|
|
|
|
|
+ if (!handler->isStaticObject() && handler->getRMGInfo().value)
|
|
|
|
|
+ {
|
|
|
|
|
+ for (auto temp : handler->getTemplates())
|
|
|
|
|
+ {
|
|
|
|
|
+ if (temp.canBePlacedAt(terrainType))
|
|
|
|
|
+ {
|
|
|
|
|
+ oi.generateObject = [gen, temp]() -> CGObjectInstance *
|
|
|
|
|
+ {
|
|
|
|
|
+ return VLC->objtypeh->getHandlerFor(temp.id, temp.subid)->create(temp);
|
|
|
|
|
+ };
|
|
|
|
|
+ oi.value = handler->getRMGInfo().value;
|
|
|
|
|
+ oi.probability = handler->getRMGInfo().rarity;
|
|
|
|
|
+ oi.templ = temp;
|
|
|
|
|
+ oi.maxPerZone = handler->getRMGInfo().zoneLimit;
|
|
|
|
|
+ possibleObjects.push_back (oi);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- static const Res::ERes woodOre[] = {Res::ERes::WOOD, Res::ERes::ORE};
|
|
|
|
|
- for (int i = 0; i < 2; i++)
|
|
|
|
|
- {
|
|
|
|
|
- oi.generateObject = [i, gen]() -> CGObjectInstance *
|
|
|
|
|
- {
|
|
|
|
|
- auto obj = new CGResource();
|
|
|
|
|
- obj->ID = Obj::RESOURCE;
|
|
|
|
|
- obj->subID = static_cast<si32>(woodOre[i]);
|
|
|
|
|
- obj->amount = 0;
|
|
|
|
|
- return obj;
|
|
|
|
|
- };
|
|
|
|
|
- oi.value = 1400;
|
|
|
|
|
- oi.probability = 300;
|
|
|
|
|
- possibleObjects.push_back (oi);
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ //all following objects are unlimited
|
|
|
|
|
+ oi.maxPerZone = std::numeric_limits<ui32>().max();
|
|
|
|
|
|
|
|
- oi.generateObject = [gen]() -> CGObjectInstance *
|
|
|
|
|
- {
|
|
|
|
|
- auto obj = new CGResource();
|
|
|
|
|
- obj->ID = Obj::RESOURCE;
|
|
|
|
|
- obj->subID = static_cast<si32>(Res::ERes::GOLD);
|
|
|
|
|
- obj->amount = 0;
|
|
|
|
|
- return obj;
|
|
|
|
|
- };
|
|
|
|
|
- oi.value = 750;
|
|
|
|
|
- oi.probability = 300;
|
|
|
|
|
- possibleObjects.push_back (oi);
|
|
|
|
|
|
|
+ //dwellings
|
|
|
|
|
|
|
|
- oi.generateObject = [gen]() -> CGObjectInstance *
|
|
|
|
|
- {
|
|
|
|
|
- auto obj = new CGPickable();
|
|
|
|
|
- obj->ID = Obj::TREASURE_CHEST;
|
|
|
|
|
- obj->subID = 0;
|
|
|
|
|
- return obj;
|
|
|
|
|
- };
|
|
|
|
|
- oi.value = 1500;
|
|
|
|
|
- oi.probability = 1000;
|
|
|
|
|
- possibleObjects.push_back (oi);
|
|
|
|
|
|
|
+ auto subObjects = VLC->objtypeh->knownSubObjects(Obj::CREATURE_GENERATOR1);
|
|
|
|
|
|
|
|
- oi.generateObject = [gen]() -> CGObjectInstance *
|
|
|
|
|
- {
|
|
|
|
|
- auto obj = new CGArtifact();
|
|
|
|
|
- obj->ID = Obj::RANDOM_TREASURE_ART;
|
|
|
|
|
- obj->subID = 0;
|
|
|
|
|
- auto a = new CArtifactInstance();
|
|
|
|
|
- gen->map->addNewArtifactInstance(a);
|
|
|
|
|
- obj->storedArtifact = a;
|
|
|
|
|
- return obj;
|
|
|
|
|
- };
|
|
|
|
|
- oi.value = 2000;
|
|
|
|
|
- oi.probability = 150;
|
|
|
|
|
- possibleObjects.push_back (oi);
|
|
|
|
|
|
|
+ //don't spawn original "neutral" dwellings that got replaced by Conflux dwellings in AB
|
|
|
|
|
+ static int elementalConfluxROE[] = {7, 13, 16, 47};
|
|
|
|
|
+ for (int i = 0; i < 4; i++)
|
|
|
|
|
+ vstd::erase_if_present(subObjects, elementalConfluxROE[i]);
|
|
|
|
|
|
|
|
- oi.generateObject = [gen]() -> CGObjectInstance *
|
|
|
|
|
|
|
+ for (auto secondaryID : subObjects)
|
|
|
{
|
|
{
|
|
|
- auto obj = new CGArtifact();
|
|
|
|
|
- obj->ID = Obj::RANDOM_MINOR_ART;
|
|
|
|
|
- obj->subID = 0;
|
|
|
|
|
- auto a = new CArtifactInstance();
|
|
|
|
|
- gen->map->addNewArtifactInstance(a);
|
|
|
|
|
- obj->storedArtifact = a;
|
|
|
|
|
- return obj;
|
|
|
|
|
- };
|
|
|
|
|
- oi.value = 5000;
|
|
|
|
|
- oi.probability = 150;
|
|
|
|
|
- possibleObjects.push_back (oi);
|
|
|
|
|
|
|
+ auto dwellingHandler = dynamic_cast<const CDwellingInstanceConstructor*>(VLC->objtypeh->getHandlerFor(Obj::CREATURE_GENERATOR1, secondaryID).get());
|
|
|
|
|
+ auto creatures = dwellingHandler->getProducedCreatures();
|
|
|
|
|
+ if (creatures.empty())
|
|
|
|
|
+ continue;
|
|
|
|
|
|
|
|
- oi.generateObject = [gen]() -> CGObjectInstance *
|
|
|
|
|
- {
|
|
|
|
|
- auto obj = new CGArtifact();
|
|
|
|
|
- obj->ID = Obj::RANDOM_MAJOR_ART;
|
|
|
|
|
- obj->subID = 0;
|
|
|
|
|
- auto a = new CArtifactInstance();
|
|
|
|
|
- gen->map->addNewArtifactInstance(a);
|
|
|
|
|
- obj->storedArtifact = a;
|
|
|
|
|
- return obj;
|
|
|
|
|
- };
|
|
|
|
|
- oi.value = 10000;
|
|
|
|
|
- oi.probability = 150;
|
|
|
|
|
- possibleObjects.push_back (oi);
|
|
|
|
|
|
|
+ auto cre = creatures.front();
|
|
|
|
|
+ if (cre->faction == townType)
|
|
|
|
|
+ {
|
|
|
|
|
+ oi.value = cre->AIValue * cre->growth * (1 + (float)(gen->getZoneCount(cre->faction)) / gen->getTotalZoneCount() + (float)(gen->getZoneCount(cre->faction) / 2)); //TODO: include town count in formula
|
|
|
|
|
+ oi.probability = 40;
|
|
|
|
|
|
|
|
- oi.generateObject = [gen]() -> CGObjectInstance *
|
|
|
|
|
- {
|
|
|
|
|
- auto obj = new CGArtifact();
|
|
|
|
|
- obj->ID = Obj::RANDOM_RELIC_ART;
|
|
|
|
|
- obj->subID = 0;
|
|
|
|
|
- auto a = new CArtifactInstance();
|
|
|
|
|
- gen->map->addNewArtifactInstance(a);
|
|
|
|
|
- obj->storedArtifact = a;
|
|
|
|
|
- return obj;
|
|
|
|
|
- };
|
|
|
|
|
- oi.value = 20000;
|
|
|
|
|
- oi.probability = 150;
|
|
|
|
|
- possibleObjects.push_back (oi);
|
|
|
|
|
|
|
+ for (auto temp : dwellingHandler->getTemplates())
|
|
|
|
|
+ {
|
|
|
|
|
+ if (temp.canBePlacedAt(terrainType))
|
|
|
|
|
+ {
|
|
|
|
|
+ oi.generateObject = [gen, temp, secondaryID, dwellingHandler]() -> CGObjectInstance *
|
|
|
|
|
+ {
|
|
|
|
|
+ auto obj = VLC->objtypeh->getHandlerFor(Obj::CREATURE_GENERATOR1, secondaryID)->create(temp);
|
|
|
|
|
+ //dwellingHandler->configureObject(obj, gen->rand);
|
|
|
|
|
+ obj->tempOwner = PlayerColor::NEUTRAL;
|
|
|
|
|
+ return obj;
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ oi.templ = temp;
|
|
|
|
|
+ possibleObjects.push_back (oi);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
static const int scrollValues[] = {500, 2000, 3000, 4000, 5000};
|
|
static const int scrollValues[] = {500, 2000, 3000, 4000, 5000};
|
|
|
|
|
|
|
@@ -1428,43 +1696,192 @@ 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);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- //non-removable object for test
|
|
|
|
|
- //oi.generateObject = [gen]() -> CGObjectInstance *
|
|
|
|
|
- //{
|
|
|
|
|
- // auto obj = new CGMagicWell();
|
|
|
|
|
- // obj->ID = Obj::MAGIC_WELL;
|
|
|
|
|
- // obj->subID = 0;
|
|
|
|
|
- // return obj;
|
|
|
|
|
- //};
|
|
|
|
|
- //oi.value = 250;
|
|
|
|
|
- //oi.probability = 100;
|
|
|
|
|
- //possibleObjects.push_back (oi);
|
|
|
|
|
-
|
|
|
|
|
- //oi.generateObject = [gen]() -> CGObjectInstance *
|
|
|
|
|
- //{
|
|
|
|
|
- // auto obj = new CGObelisk();
|
|
|
|
|
- // obj->ID = Obj::OBELISK;
|
|
|
|
|
- // obj->subID = 0;
|
|
|
|
|
- // return obj;
|
|
|
|
|
- //};
|
|
|
|
|
- //oi.value = 3500;
|
|
|
|
|
- //oi.probability = 200;
|
|
|
|
|
- //possibleObjects.push_back (oi);
|
|
|
|
|
-
|
|
|
|
|
- //oi.generateObject = [gen]() -> CGObjectInstance *
|
|
|
|
|
- //{
|
|
|
|
|
- // auto obj = new CBank();
|
|
|
|
|
- // obj->ID = Obj::CREATURE_BANK;
|
|
|
|
|
- // obj->subID = 5; //naga bank
|
|
|
|
|
- // return obj;
|
|
|
|
|
- //};
|
|
|
|
|
- //oi.value = 3000;
|
|
|
|
|
- //oi.probability = 100;
|
|
|
|
|
- //possibleObjects.push_back (oi);
|
|
|
|
|
-
|
|
|
|
|
|
|
+ //pandora box with gold
|
|
|
|
|
+ for (int i = 1; i < 5; i++)
|
|
|
|
|
+ {
|
|
|
|
|
+ oi.generateObject = [i]() -> CGObjectInstance *
|
|
|
|
|
+ {
|
|
|
|
|
+ auto obj = new CGPandoraBox();
|
|
|
|
|
+ obj->ID = Obj::PANDORAS_BOX;
|
|
|
|
|
+ obj->subID = 0;
|
|
|
|
|
+ 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);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ //pandora box with experience
|
|
|
|
|
+ for (int i = 1; i < 5; i++)
|
|
|
|
|
+ {
|
|
|
|
|
+ oi.generateObject = [i]() -> CGObjectInstance *
|
|
|
|
|
+ {
|
|
|
|
|
+ auto obj = new CGPandoraBox();
|
|
|
|
|
+ obj->ID = Obj::PANDORAS_BOX;
|
|
|
|
|
+ obj->subID = 0;
|
|
|
|
|
+ obj->gainedExp = i * 5000;
|
|
|
|
|
+ return obj;
|
|
|
|
|
+ };
|
|
|
|
|
+ oi.setTemplate (Obj::PANDORAS_BOX, 0, terrainType);
|
|
|
|
|
+ oi.value = i * 6000;;
|
|
|
|
|
+ oi.probability = 20;
|
|
|
|
|
+ possibleObjects.push_back (oi);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ //pandora box with creatures
|
|
|
|
|
+ static const int tierValues[] = {5000, 7000, 9000, 12000, 16000, 21000, 27000};
|
|
|
|
|
+
|
|
|
|
|
+ for (auto creature : VLC->creh->creatures)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (!creature->special && VLC->townh->factions[creature->faction]->nativeTerrain == terrainType)
|
|
|
|
|
+ {
|
|
|
|
|
+ int actualTier = creature->level > 7 ? 6 : creature->level-1;
|
|
|
|
|
+ int creaturesAmount = tierValues[actualTier] / creature->AIValue;
|
|
|
|
|
+ if (creaturesAmount <= 5)
|
|
|
|
|
+ {
|
|
|
|
|
+ }
|
|
|
|
|
+ else if (creaturesAmount <= 12)
|
|
|
|
|
+ {
|
|
|
|
|
+ (creaturesAmount /= 2) *= 2;
|
|
|
|
|
+ }
|
|
|
|
|
+ else if (creaturesAmount <= 50)
|
|
|
|
|
+ {
|
|
|
|
|
+ creaturesAmount = boost::math::round((float)creaturesAmount / 5) * 5;
|
|
|
|
|
+ }
|
|
|
|
|
+ else if (creaturesAmount <= 12)
|
|
|
|
|
+ {
|
|
|
|
|
+ creaturesAmount = boost::math::round((float)creaturesAmount / 10) * 10;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ oi.generateObject = [creature, creaturesAmount]() -> CGObjectInstance *
|
|
|
|
|
+ {
|
|
|
|
|
+ auto obj = new CGPandoraBox();
|
|
|
|
|
+ obj->ID = Obj::PANDORAS_BOX;
|
|
|
|
|
+ obj->subID = 0;
|
|
|
|
|
+ auto stack = new CStackInstance(creature, creaturesAmount);
|
|
|
|
|
+ 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);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ //Pandora with 12 spells of certain level
|
|
|
|
|
+ for (int i = 1; i <= GameConstants::SPELL_LEVELS; i++)
|
|
|
|
|
+ {
|
|
|
|
|
+ oi.generateObject = [i, gen]() -> CGObjectInstance *
|
|
|
|
|
+ {
|
|
|
|
|
+ auto obj = new CGPandoraBox();
|
|
|
|
|
+ obj->ID = Obj::PANDORAS_BOX;
|
|
|
|
|
+ obj->subID = 0;
|
|
|
|
|
+
|
|
|
|
|
+ std::vector <CSpell *> spells;
|
|
|
|
|
+ for (auto spell : VLC->spellh->objects)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (!spell->isSpecialSpell() && spell->level == i)
|
|
|
|
|
+ spells.push_back(spell);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ RandomGeneratorUtil::randomShuffle(spells, gen->rand);
|
|
|
|
|
+ for (int j = 0; j < std::min<int>(12, spells.size()); j++)
|
|
|
|
|
+ {
|
|
|
|
|
+ obj->spells.push_back (spells[j]->id);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return obj;
|
|
|
|
|
+ };
|
|
|
|
|
+ oi.setTemplate (Obj::PANDORAS_BOX, 0, terrainType);
|
|
|
|
|
+ oi.value = (i + 1) * 2500; //5000 - 15000
|
|
|
|
|
+ oi.probability = 2;
|
|
|
|
|
+ possibleObjects.push_back (oi);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ //Pandora with 15 spells of certain school
|
|
|
|
|
+ for (int i = 1; i <= 4; i++)
|
|
|
|
|
+ {
|
|
|
|
|
+ oi.generateObject = [i, gen]() -> CGObjectInstance *
|
|
|
|
|
+ {
|
|
|
|
|
+ auto obj = new CGPandoraBox();
|
|
|
|
|
+ obj->ID = Obj::PANDORAS_BOX;
|
|
|
|
|
+ obj->subID = 0;
|
|
|
|
|
+
|
|
|
|
|
+ std::vector <CSpell *> spells;
|
|
|
|
|
+ for (auto spell : VLC->spellh->objects)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (!spell->isSpecialSpell())
|
|
|
|
|
+ {
|
|
|
|
|
+ bool school = false; //TODO: we could have better interface for iterating schools
|
|
|
|
|
+ switch (i)
|
|
|
|
|
+ {
|
|
|
|
|
+ case 1:
|
|
|
|
|
+ school = spell->air;
|
|
|
|
|
+ case 2:
|
|
|
|
|
+ school = spell->earth;
|
|
|
|
|
+ case 3:
|
|
|
|
|
+ school = spell->fire;
|
|
|
|
|
+ case 4:
|
|
|
|
|
+ school = spell->water;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (school)
|
|
|
|
|
+ spells.push_back(spell);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ RandomGeneratorUtil::randomShuffle(spells, gen->rand);
|
|
|
|
|
+ for (int j = 0; j < std::min<int>(15, spells.size()); j++)
|
|
|
|
|
+ {
|
|
|
|
|
+ obj->spells.push_back (spells[j]->id);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return obj;
|
|
|
|
|
+ };
|
|
|
|
|
+ oi.setTemplate (Obj::PANDORAS_BOX, 0, terrainType);
|
|
|
|
|
+ oi.value = 15000;
|
|
|
|
|
+ oi.probability = 2;
|
|
|
|
|
+ possibleObjects.push_back (oi);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Pandora box with 60 random spells
|
|
|
|
|
+
|
|
|
|
|
+ oi.generateObject = [gen]() -> CGObjectInstance *
|
|
|
|
|
+ {
|
|
|
|
|
+ auto obj = new CGPandoraBox();
|
|
|
|
|
+ obj->ID = Obj::PANDORAS_BOX;
|
|
|
|
|
+ obj->subID = 0;
|
|
|
|
|
+
|
|
|
|
|
+ std::vector <CSpell *> spells;
|
|
|
|
|
+ for (auto spell : VLC->spellh->objects)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (!spell->isSpecialSpell())
|
|
|
|
|
+ spells.push_back(spell);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ RandomGeneratorUtil::randomShuffle(spells, gen->rand);
|
|
|
|
|
+ for (int j = 0; j < std::min<int>(60, spells.size()); j++)
|
|
|
|
|
+ {
|
|
|
|
|
+ obj->spells.push_back (spells[j]->id);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ 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();
|
|
|
|
|
+}
|