1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-01-26 03:52:01 +02:00

Implemented (hopefully) original treasure randomization algorithm.

This commit is contained in:
DjWarmonger 2014-06-04 19:08:04 +02:00
parent fe05de9aff
commit 464ffc4afa
2 changed files with 173 additions and 87 deletions

View File

@ -472,102 +472,19 @@ bool CRmgTemplateZone::createTreasurePile (CMapGenerator* gen, int3 &pos)
minValue = treasureInfo.front().min;
}
static const Res::ERes woodOre[] = {Res::ERes::WOOD, Res::ERes::ORE};
static const Res::ERes preciousRes[] = {Res::ERes::CRYSTAL, Res::ERes::GEMS, Res::ERes::MERCURY, Res::ERes::SULFUR};
static auto res_gen = gen->rand.getIntRange(Res::ERes::WOOD, Res::ERes::GOLD);
int currentValue = 0;
CGObjectInstance * object = nullptr;
while (currentValue < minValue)
{
int remaining = maxValue - currentValue;
int nextValue = gen->rand.nextInt (0.25f * remaining, remaining);
if (nextValue >= 20000)
{
auto obj = new CGArtifact();
obj->ID = Obj::RANDOM_RELIC_ART;
obj->subID = 0;
auto a = new CArtifactInstance(); //TODO: probably some refactoring could help here
gen->map->addNewArtifactInstance(a);
obj->storedArtifact = a;
object = obj;
currentValue += 20000;
}
else if (nextValue >= 10000)
{
auto obj = new CGArtifact();
obj->ID = Obj::RANDOM_MAJOR_ART;
obj->subID = 0;
auto a = new CArtifactInstance();
gen->map->addNewArtifactInstance(a);
obj->storedArtifact = a;
object = obj;
currentValue += 10000;
}
else if (nextValue >= 5000)
{
auto obj = new CGArtifact();
obj->ID = Obj::RANDOM_MINOR_ART;
obj->subID = 0;
auto a = new CArtifactInstance();
gen->map->addNewArtifactInstance(a);
obj->storedArtifact = a;
object = obj;
currentValue += 5000;
}
else if (nextValue >= 2000)
{
auto obj = new CGArtifact();
obj->ID = Obj::RANDOM_TREASURE_ART;
obj->subID = 0;
auto a = new CArtifactInstance();
gen->map->addNewArtifactInstance(a);
obj->storedArtifact = a;
object = obj;
currentValue += 2000;
}
else if (nextValue >= 1500)
{
auto obj = new CGPickable();
obj->ID = Obj::TREASURE_CHEST;
obj->subID = 0;
object = obj;
currentValue += 1500;
}
else if (nextValue >= 1400)
{
auto obj = new CGResource();
auto restype = static_cast<Res::ERes>(preciousRes[gen->rand.nextInt (0,3)]); //TODO: how about dedicated function to pick random element of array?
obj->ID = Obj::RESOURCE;
obj->subID = static_cast<si32>(restype);
obj->amount = 0;
object = obj;
currentValue += 1400;
}
else if (nextValue >= 1000)
{
auto obj = new CGResource();
auto restype = static_cast<Res::ERes>(woodOre[gen->rand.nextInt (0,1)]);
obj->ID = Obj::RESOURCE;
obj->subID = static_cast<si32>(restype);
obj->amount = 0;
object = obj;
currentValue += 1000;
}
else if (nextValue >= 750)
{
auto obj = new CGResource();
obj->ID = Obj::RESOURCE;
obj->subID = static_cast<si32>(Res::ERes::GOLD);
obj->amount = 0;
object = obj;
currentValue += 750;
}
else //no possible treasure left (should not happen)
auto oi = getRandomObject(gen, remaining);
object = oi.generateObject();
if (!object)
break;
//TODO: generate actual zone and not just all objects on a pile
currentValue += oi.value;
placeObject(gen, object, pos);
}
if (object)
@ -579,8 +496,166 @@ bool CRmgTemplateZone::createTreasurePile (CMapGenerator* gen, int3 &pos)
return false;
}
ObjectInfo CRmgTemplateZone::getRandomObject (CMapGenerator* gen, ui32 value)
{
std::vector<std::pair<ui32, ObjectInfo>> tresholds;
ui32 total = 0;
ui32 minValue = 0.25f * value;
//roulette wheel
for (auto oi : possibleObjects)
{
if (oi.value >= minValue && oi.value <= value)
{
total += oi.probability;
tresholds.push_back (std::make_pair (total, oi));
}
}
//TODO: generate pandora box with gold if the value is very high
if (tresholds.empty())
{
ObjectInfo oi;
oi.generateObject = [gen]() -> CGObjectInstance *
{
return nullptr;
};
oi.value = 0;
oi.probability = 0;
}
int r = gen->rand.nextInt (1, total);
for (auto t : tresholds)
{
if (r <= t.first)
return t.second;
}
}
void CRmgTemplateZone::addAllPossibleObjects (CMapGenerator* gen)
{
//TODO: move typical objects to config
ObjectInfo oi;
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);
}
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);
}
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);
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);
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);
oi.generateObject = [gen]() -> CGObjectInstance *
{
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);
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);
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);
}
bool CRmgTemplateZone::fill(CMapGenerator* gen)
{
addAllPossibleObjects (gen);
int townId = 0;
if ((type == ETemplateZoneType::CPU_START) || (type == ETemplateZoneType::PLAYER_START))

View File

@ -61,6 +61,13 @@ public:
ui16 density;
};
struct DLL_LINKAGE ObjectInfo
{
ui32 value;
ui16 probability;
std::function<CGObjectInstance *()> generateObject;
};
/// The CRmgTemplateZone describes a zone in a template.
class DLL_LINKAGE CRmgTemplateZone
{
@ -132,6 +139,8 @@ public:
void addTreasureInfo(CTreasureInfo & info);
std::vector<CTreasureInfo> getTreasureInfo();
ObjectInfo getRandomObject (CMapGenerator* gen, ui32 value);
private:
//template info
TRmgTemplateZoneId id;
@ -146,6 +155,7 @@ private:
boost::optional<TRmgTemplateZoneId> terrainTypeLikeZone, townTypeLikeZone;
std::vector<CTreasureInfo> treasureInfo;
std::vector<ObjectInfo> possibleObjects;
//content info
std::vector<int3> shape; //TODO: remove
@ -160,6 +170,7 @@ private:
std::map<TRmgTemplateZoneId, bool> alreadyConnected; //TODO: allow multiple connections between two zones?
bool pointIsIn(int x, int y);
void addAllPossibleObjects (CMapGenerator* gen); //add objects, including zone-specific, to possibleObjects
bool findPlaceForObject(CMapGenerator* gen, CGObjectInstance* obj, si32 min_dist, int3 &pos);
void checkAndPlaceObject(CMapGenerator* gen, CGObjectInstance* object, const int3 &pos);
void placeObject(CMapGenerator* gen, CGObjectInstance* object, const int3 &pos);