2
0
Эх сурвалжийг харах

- Implemented guard generation formula following this post http://forum.vcmi.eu/viewtopic.php?p=12426&sid=09f5fac8992dc880eb6a720615787ca0#12426
- Some primitive way to randomize treasures

DjWarmonger 11 жил өмнө
parent
commit
62e9f13b08

+ 1 - 1
client/CPreGame.cpp

@@ -1709,7 +1709,7 @@ CRandomMapTab::CRandomMapTab()
 	addButtonsWithRandToGroup(monsterStrengthGroup, monsterStrengthBtns, 0, 2, WIDE_BTN_WIDTH, 248, 251);
 	monsterStrengthGroup->onChange = [&](int btnId)
 	{
-		mapGenOptions.setMonsterStrength(static_cast<EMonsterStrength::EMonsterStrength>(btnId));
+		mapGenOptions.setMonsterStrength(static_cast<EMonsterStrength::EMonsterStrength>(btnId + EMonsterStrength::GLOBAL_WEAK)); //value 2 to 4
 	};
 
 	// Show random maps btn

+ 12 - 12
config/rmg.json

@@ -70,12 +70,12 @@
 		},
 		"connections" :
 		[
-			{ "a" : "1", "b" : "3", "guard" : 2000 },
-			{ "a" : "1", "b" : "5", "guard" : 8000 },
-			{ "a" : "2", "b" : "4", "guard" : 2000 },
-			{ "a" : "2", "b" : "5", "guard" : 8000 },
-			{ "a" : "3", "b" : "5", "guard" : 4000 },
-			{ "a" : "4", "b" : "5", "guard" : 4000 }
+			{ "a" : "1", "b" : "3", "guard" : 3000 },
+			{ "a" : "1", "b" : "5", "guard" : 9000 },
+			{ "a" : "2", "b" : "4", "guard" : 3000 },
+			{ "a" : "2", "b" : "5", "guard" : 9000 },
+			{ "a" : "3", "b" : "5", "guard" : 6000 },
+			{ "a" : "4", "b" : "5", "guard" : 6000 }
 		]
 	},
 	"Golden Ring" :
@@ -111,12 +111,12 @@
 		},
 		"connections" :
 		[
-			{ "a" : "1", "b" : "4", "guard" : 2000 },
-			{ "a" : "1", "b" : "5", "guard" : 2000 },
-			{ "a" : "2", "b" : "6", "guard" : 2000 },
-			{ "a" : "2", "b" : "7", "guard" : 2000 },
-			{ "a" : "3", "b" : "8", "guard" : 2000 },
-			{ "a" : "3", "b" : "9", "guard" : 2000 },
+			{ "a" : "1", "b" : "4", "guard" : 2500 },
+			{ "a" : "1", "b" : "5", "guard" : 2500 },
+			{ "a" : "2", "b" : "6", "guard" : 2500 },
+			{ "a" : "2", "b" : "7", "guard" : 2500 },
+			{ "a" : "3", "b" : "8", "guard" : 2500 },
+			{ "a" : "3", "b" : "9", "guard" : 2500 },
 			{ "a" : "4", "b" : "10", "guard" : 20000 },
 			{ "a" : "5", "b" : "12", "guard" : 20000 },
 			{ "a" : "6", "b" : "10", "guard" : 20000 },

+ 2 - 2
lib/rmg/CMapGenOptions.cpp

@@ -216,11 +216,11 @@ void CMapGenOptions::finalize(CRandomGenerator & rand)
 
 	if(waterContent == EWaterContent::RANDOM)
 	{
-		waterContent = static_cast<EWaterContent::EWaterContent>(rand.nextInt(EWaterContent::LAST_ITEM));
+		waterContent = static_cast<EWaterContent::EWaterContent>(rand.nextInt(EWaterContent::NONE, EWaterContent::ISLANDS));
 	}
 	if(monsterStrength == EMonsterStrength::RANDOM)
 	{
-		monsterStrength = static_cast<EMonsterStrength::EMonsterStrength>(rand.nextInt(EMonsterStrength::LAST_ITEM));
+		monsterStrength = static_cast<EMonsterStrength::EMonsterStrength>(rand.nextInt(EMonsterStrength::GLOBAL_WEAK, EMonsterStrength::GLOBAL_STRONG));
 	}
 
 	//rectangular maps are the future of gaming

+ 8 - 7
lib/rmg/CMapGenOptions.h

@@ -23,8 +23,7 @@ namespace EWaterContent
 		RANDOM = -1,
 		NONE,
 		NORMAL,
-		ISLANDS,
-		LAST_ITEM = ISLANDS
+		ISLANDS
 	};
 }
 
@@ -32,11 +31,13 @@ namespace EMonsterStrength
 {
 	enum EMonsterStrength
 	{
-		RANDOM = -1,
-		WEAK,
-		NORMAL,
-		STRONG,
-		LAST_ITEM = STRONG
+		RANDOM = -2,
+		ZONE_WEAK = -1,
+		ZONE_NORMAL = 0,
+		ZONE_STRONG = 1,
+		GLOBAL_WEAK = 2,
+		GLOBAL_NORMAL = 3,
+		GLOBAL_STRONG = 4
 	};
 }
 

+ 1 - 0
lib/rmg/CMapGenerator.cpp

@@ -258,6 +258,7 @@ void CMapGenerator::createConnections()
 			});
 			if (guardPos.valid())
 			{
+				setOccupied (guardPos, ETileType::FREE); //just in case monster is too weak to spawn
 				zoneA->addMonster (this, guardPos, connection.getGuardStrength()); //TODO: set value according to template
 				//zones can make paths only in their own area
 				zoneA->crunchPath (this, guardPos, zoneA->getPos(), zoneA->getId()); //make connection towards our zone center

+ 144 - 22
lib/rmg/CRmgTemplateZone.cpp

@@ -403,15 +403,37 @@ void CRmgTemplateZone::addRequiredObject(CGObjectInstance * obj, si32 strength)
 
 void CRmgTemplateZone::addMonster(CMapGenerator* gen, int3 &pos, si32 strength)
 {
+	//precalculate actual (randomized) monster strength based on this post
+	//http://forum.vcmi.eu/viewtopic.php?p=12426#12426
+
+	int zoneMonsterStrength = 0; //TODO: range -1..1 based on template settings
+	int mapMonsterStrength = gen->mapGenOptions->getMonsterStrength();
+	int monsterStrength = zoneMonsterStrength + mapMonsterStrength - 1; //array index from 0 to 4
+	static const int value1[] = {2500, 1500, 1000, 500, 0};
+	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 multiplier2[] = {0.5, 0.75, 1.0, 1.0, 1.5};
+
+	int strength1 = std::max(0.f, (strength - value1[monsterStrength]) * multiplier1[monsterStrength]); 
+	int strength2 = std::max(0.f, (strength - value2[monsterStrength]) * multiplier2[monsterStrength]); 
+
+	strength = strength1 + strength2;
+	if (strength < 2000)
+		return; //no guard at all
+
 	CreatureID creId = CreatureID::NONE;
 	int amount = 0;
 	while (true)
 	{
 		creId = VLC->creh->pickRandomMonster(gen->rand);
 		auto cre = VLC->creh->creatures[creId];
-		amount = std::ceil((float)strength / cre->fightValue);
-		if (strength >= cre->fightValue && amount >= cre->ammMin && amount <= 100) //at leats one full monster. size between minimum size of given stack and 100
+		if ((cre->AIValue * (cre->ammMin + cre->ammMax) / 2 < strength) && (strength < cre->AIValue * 100)) //at leats one full monster. size between minimum size of given stack and 100
+		{
+			amount = strength / cre->AIValue;
+			if (amount >= 4)
+				amount *= gen->rand.nextDouble(0.75, 1.25);
 			break;
+		}
 	}
 
 	auto guard = new CGCreature();
@@ -424,6 +446,119 @@ void CRmgTemplateZone::addMonster(CMapGenerator* gen, int3 &pos, si32 strength)
 	placeObject(gen, guard, pos);
 }
 
+bool CRmgTemplateZone::createTreasurePile (CMapGenerator* gen, int3 &pos)
+{
+	//TODO: read treasure values from template
+	const int maxValue = 5000;
+	const int minValue = 1500;
+
+	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)
+			break;
+
+		//TODO: generate actual zone and not just all objects on a pile
+		placeObject(gen, object, pos);
+	}
+	if (object)
+	{
+		guardObject (gen, object, currentValue);
+		return true;
+	}
+	else //we did not place eveyrthing successfully
+		return false;
+}
+
 bool CRmgTemplateZone::fill(CMapGenerator* gen)
 {
 	int townId = 0;
@@ -515,15 +650,14 @@ bool CRmgTemplateZone::fill(CMapGenerator* gen)
 			guardObject (gen, obj.first, obj.second);
 		}
 	}
-	std::vector<CGObjectInstance*> guarded_objects;
-	static auto res_gen = gen->rand.getIntRange(Res::ERes::WOOD, Res::ERes::GOLD);
 	const double res_mindist = 5;
+
+	//TODO: just placeholder to chekc for possible locations
+	auto obj = new CGResource();
+	obj->ID = Obj::RESOURCE;
+	obj->subID = static_cast<si32>(Res::ERes::GOLD);
+	obj->amount = 0;
 	do {
-		auto obj = new CGResource();
-		auto restype = static_cast<Res::ERes>(res_gen());
-		obj->ID = Obj::RESOURCE;
-		obj->subID = static_cast<si32>(restype);
-		obj->amount = 0;
 		
 		int3 pos;
 		if ( ! findPlaceForObject(gen, obj, res_mindist, pos))		
@@ -531,22 +665,10 @@ bool CRmgTemplateZone::fill(CMapGenerator* gen)
 			delete obj;
 			break;
 		}
-		placeObject(gen, obj, pos);
+		createTreasurePile (gen, pos);
 
-		if ((restype != Res::ERes::WOOD) && (restype != Res::ERes::ORE))
-		{
-			guarded_objects.push_back(obj);
-		}
 	} while(true);
 
-	for(const auto &obj : guarded_objects)
-	{
-		if ( ! guardObject(gen, obj, 1000))
-		{
-			//TODO, DEL obj from map
-		}
-	}
-
 	auto sel = gen->editManager->getTerrainSelection();
 	sel.clearSelection();
 	for (auto tile : tileinfo)

+ 1 - 0
lib/rmg/CRmgTemplateZone.h

@@ -114,6 +114,7 @@ public:
 
 	void addRequiredObject(CGObjectInstance * obj, si32 guardStrength=0);
 	void addMonster(CMapGenerator* gen, int3 &pos, si32 strength);
+	bool createTreasurePile (CMapGenerator* gen, int3 &pos);
 	bool fill(CMapGenerator* gen);
 	void createBorder(CMapGenerator* gen);
 	bool crunchPath (CMapGenerator* gen, const int3 &src, const int3 &dst, TRmgTemplateZoneId zone);