Переглянути джерело

Implemented (hopefully) original treasure randomization algorithm.

DjWarmonger 11 роки тому
батько
коміт
464ffc4afa
2 змінених файлів з 172 додано та 86 видалено
  1. 161 86
      lib/rmg/CRmgTemplateZone.cpp
  2. 11 0
      lib/rmg/CRmgTemplateZone.h

+ 161 - 86
lib/rmg/CRmgTemplateZone.cpp

@@ -472,115 +472,190 @@ 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 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)
+	{
+		guardObject (gen, object, currentValue);
+		return true;
+	}
+	else //we did not place eveyrthing successfully
+		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)
 		{
-			auto obj = new CGPickable();
-			obj->ID = Obj::TREASURE_CHEST;
-			obj->subID = 0;
-			object = obj;
-			currentValue += 1500;
+			total += oi.probability;
+			tresholds.push_back (std::make_pair (total, oi));
 		}
-		else if (nextValue >= 1400)
+	}
+
+	//TODO: generate pandora box with gold if the value is very high
+	if (tresholds.empty())
+	{
+		ObjectInfo oi;
+		oi.generateObject = [gen]() -> CGObjectInstance *
 		{
-			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)
+			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();
-			auto restype = static_cast<Res::ERes>(woodOre[gen->rand.nextInt (0,1)]);
 			obj->ID = Obj::RESOURCE;
-			obj->subID = static_cast<si32>(restype);
+			obj->subID = static_cast<si32>(preciousRes[i]);
 			obj->amount = 0;
-			object = obj;
-			currentValue += 1000;
-		}
-		else if (nextValue >= 750)
+			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>(Res::ERes::GOLD);
+			obj->subID = static_cast<si32>(woodOre[i]);
 			obj->amount = 0;
-			object = obj;
-			currentValue += 750;
-		}
-		else //no possible treasure left (should not happen)
-			break;
-
-		//TODO: generate actual zone and not just all objects on a pile
-		placeObject(gen, object, pos);
+			return obj;
+		};
+		oi.value = 1400;
+		oi.probability = 300;
+		possibleObjects.push_back (oi);
 	}
-	if (object)
+
+	oi.generateObject = [gen]() -> CGObjectInstance *
 	{
-		guardObject (gen, object, currentValue);
-		return true;
-	}
-	else //we did not place eveyrthing successfully
-		return false;
+		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))

+ 11 - 0
lib/rmg/CRmgTemplateZone.h

@@ -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);