From 464ffc4afad3d447495bb016f09d63c11a078706 Mon Sep 17 00:00:00 2001 From: DjWarmonger Date: Wed, 4 Jun 2014 19:08:04 +0200 Subject: [PATCH] Implemented (hopefully) original treasure randomization algorithm. --- lib/rmg/CRmgTemplateZone.cpp | 249 +++++++++++++++++++++++------------ lib/rmg/CRmgTemplateZone.h | 11 ++ 2 files changed, 173 insertions(+), 87 deletions(-) diff --git a/lib/rmg/CRmgTemplateZone.cpp b/lib/rmg/CRmgTemplateZone.cpp index 027789651..4f62b2d5f 100644 --- a/lib/rmg/CRmgTemplateZone.cpp +++ b/lib/rmg/CRmgTemplateZone.cpp @@ -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(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(restype); - obj->amount = 0; - object = obj; - currentValue += 1400; - } - else if (nextValue >= 1000) - { - auto obj = new CGResource(); - auto restype = static_cast(woodOre[gen->rand.nextInt (0,1)]); - obj->ID = Obj::RESOURCE; - obj->subID = static_cast(restype); - obj->amount = 0; - object = obj; - currentValue += 1000; - } - else if (nextValue >= 750) - { - auto obj = new CGResource(); - obj->ID = Obj::RESOURCE; - obj->subID = static_cast(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> 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(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(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(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)) diff --git a/lib/rmg/CRmgTemplateZone.h b/lib/rmg/CRmgTemplateZone.h index 2d5f19e8d..d5217f78e 100644 --- a/lib/rmg/CRmgTemplateZone.h +++ b/lib/rmg/CRmgTemplateZone.h @@ -61,6 +61,13 @@ public: ui16 density; }; +struct DLL_LINKAGE ObjectInfo +{ + ui32 value; + ui16 probability; + std::function generateObject; +}; + /// The CRmgTemplateZone describes a zone in a template. class DLL_LINKAGE CRmgTemplateZone { @@ -132,6 +139,8 @@ public: void addTreasureInfo(CTreasureInfo & info); std::vector getTreasureInfo(); + ObjectInfo getRandomObject (CMapGenerator* gen, ui32 value); + private: //template info TRmgTemplateZoneId id; @@ -146,6 +155,7 @@ private: boost::optional terrainTypeLikeZone, townTypeLikeZone; std::vector treasureInfo; + std::vector possibleObjects; //content info std::vector shape; //TODO: remove @@ -160,6 +170,7 @@ private: std::map 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);