221 lines
No EOL
8.6 KiB
C#
221 lines
No EOL
8.6 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using DiceProbabilities;
|
|
using DungeonMapGenerator.Rooms;
|
|
|
|
namespace DungeonMapGenerator
|
|
{
|
|
public static class DungeonLockPopulator
|
|
{
|
|
private const float ACCEPTABLE_ERROR = .02f;
|
|
private const int MAX_HARD_LOCKS = 3;
|
|
public static void PopulateLocksOfDungeon(DungeonMap dungeon)
|
|
{
|
|
Random random = new Random();
|
|
float desiredSuccessChance = .995f;
|
|
|
|
List<Room> currentRooms = new List<Room>();
|
|
foreach (EntranceRoom entranceRoom in dungeon.GetEntranceRooms())
|
|
{
|
|
currentRooms.Add(entranceRoom);
|
|
}
|
|
HashSet<Room> seenRooms = new HashSet<Room>(currentRooms);
|
|
|
|
// Set entrance room locks to 2 - 12
|
|
Shuffle(currentRooms);
|
|
int entranceRoomLock = 1;
|
|
foreach (Room entranceRoom in currentRooms)
|
|
{
|
|
entranceRoomLock += 1;
|
|
entranceRoom.Lock = new Lock((entranceRoomLock.ToString()));
|
|
}
|
|
|
|
// Give the dead ends very hard locks
|
|
foreach (Room room in dungeon.GetAllRooms().Except(currentRooms))
|
|
{
|
|
if (room.GetAdjacentRooms().Count() == 1)
|
|
{
|
|
room.Lock = Lock.GetRandomVeryHardLock();
|
|
}
|
|
}
|
|
|
|
while (currentRooms.Count > 0)
|
|
{
|
|
List<Room> locklessRooms = new List<Room>();
|
|
List<Room> alreadyLockedRooms = new List<Room>();
|
|
List<Room> adjacentRooms = new List<Room>();
|
|
|
|
foreach (Room room in currentRooms.ToList())
|
|
{
|
|
foreach (Room adjacentRoom in room.GetAdjacentRooms())
|
|
{
|
|
if (!seenRooms.Contains(adjacentRoom))
|
|
{
|
|
adjacentRooms.Add(adjacentRoom);
|
|
seenRooms.Add(adjacentRoom);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Sort locked and not locked rooms
|
|
foreach (Room adjacentRoom in adjacentRooms.ToList())
|
|
{
|
|
if (adjacentRoom.Lock != null)
|
|
{
|
|
alreadyLockedRooms.Add(adjacentRoom);
|
|
}
|
|
else
|
|
{
|
|
locklessRooms.Add(adjacentRoom);
|
|
}
|
|
}
|
|
|
|
// Get locks of locked rooms
|
|
List<Lock> pregeneratedLocks = new List<Lock>();
|
|
foreach (Room room in alreadyLockedRooms)
|
|
{
|
|
pregeneratedLocks.Add(room.Lock);
|
|
}
|
|
|
|
if (locklessRooms.Count > 3)
|
|
{
|
|
// Add locks to the rooms with odds adding to the desired success chance
|
|
List<Lock> locks = GenerateLocks(pregeneratedLocks, locklessRooms.Count, desiredSuccessChance);
|
|
for (int i = 0; i < locklessRooms.Count; i++)
|
|
{
|
|
locklessRooms[i].Lock = locks[i];
|
|
}
|
|
}
|
|
else if (locklessRooms.Count > 0)
|
|
{
|
|
foreach (Room room in locklessRooms)
|
|
{
|
|
room.Lock = Lock.NormalLocks[random.Next(0, Lock.NormalLocks.Count)];
|
|
}
|
|
}
|
|
|
|
currentRooms = adjacentRooms;
|
|
}
|
|
|
|
int hardLocksAdded = 0;
|
|
foreach (var room in dungeon.GetAllRooms())
|
|
{
|
|
// Check if all locks were set
|
|
if (room.Lock == null)
|
|
{
|
|
Console.WriteLine($"Room at {room.GetCenterOfRoom()} wasn't seen whilst populating locks");
|
|
room.Lock = Lock.NormalLocks[random.Next(0, Lock.NormalLocks.Count)];
|
|
}
|
|
|
|
// Add two hard locks
|
|
if (hardLocksAdded < MAX_HARD_LOCKS
|
|
&& room.TypeOfRoom == RoomType.Normal
|
|
&& room.GetAdjacentRooms().All(adjacent => adjacent.TypeOfRoom == RoomType.Normal)
|
|
&& (room.Lock.GetLock() == "4" || room.Lock.GetLock() == "10"))
|
|
{
|
|
room.Lock = Lock.GetRandomHardLock();
|
|
hardLocksAdded++;
|
|
}
|
|
}
|
|
}
|
|
|
|
public static void AddExtraLocksToMonsterRooms(List<MonsterRoom> monsterRooms)
|
|
{
|
|
int maxExtraMonsterRoomLocks = 2;
|
|
foreach (MonsterRoom monsterRoom in monsterRooms)
|
|
{
|
|
int extraLockCounter = 0;
|
|
foreach (Room adjacentRoom in monsterRoom.GetAdjacentRooms())
|
|
{
|
|
if (Lock.VeryHardLocks.All(l => l.GetLock() != adjacentRoom.Lock.GetLock()))
|
|
{
|
|
if (extraLockCounter < maxExtraMonsterRoomLocks)
|
|
{
|
|
monsterRoom.ExtraLocks.Add(new Lock(adjacentRoom.Lock.GetLock()));
|
|
monsterRoom.AddBlockingRoom(adjacentRoom);
|
|
extraLockCounter++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public static void AddExtraLocksToBossRoom(BossRoom bossRoom)
|
|
{
|
|
int maxExtraBossRooms = 5;
|
|
int extraLockCounter = 0;
|
|
foreach (Room adjacentRoom in bossRoom.GetAdjacentRooms())
|
|
{
|
|
if (Lock.VeryHardLocks.All(l => l.GetLock() != adjacentRoom.Lock.GetLock()))
|
|
{
|
|
if (extraLockCounter < maxExtraBossRooms)
|
|
{
|
|
bossRoom.ExtraLocks.Add(new Lock(adjacentRoom.Lock.GetLock()));
|
|
bossRoom.AddBlockingRoom(adjacentRoom);
|
|
extraLockCounter++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private static List<Lock> GenerateLocks(List<Lock> pregeneratedLocks, int numLocks, float successChance)
|
|
{
|
|
List<Lock> locks = new List<Lock>();
|
|
if (FindCombination(pregeneratedLocks, locks, numLocks, successChance, Lock.NormalLocks))
|
|
{
|
|
return locks;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private static bool FindCombination(List<Lock> pregeneratedLocks, List<Lock> generatedLocks, int numLocks, float successChance, List<Lock> lockOptions)
|
|
{
|
|
if (generatedLocks.Count == numLocks)
|
|
{
|
|
List<Lock> allLocks = new List<Lock>(pregeneratedLocks);
|
|
allLocks.AddRange(generatedLocks);
|
|
List<string> locksAsStrings = allLocks.Select(_lock => _lock.GetLock()).ToList();
|
|
|
|
// Return true if given the current locks the chance of being able to open one is close to difficulty
|
|
float probalityOfAtleastOneUnlocking = RollFourSumTwo.GetProbalityOfAtleastOneUnlocking(locksAsStrings);
|
|
return probalityOfAtleastOneUnlocking >= successChance - ACCEPTABLE_ERROR
|
|
&& probalityOfAtleastOneUnlocking <= successChance + ACCEPTABLE_ERROR;
|
|
}
|
|
|
|
Shuffle(lockOptions);
|
|
|
|
foreach (Lock _lock in lockOptions.ToList())
|
|
{
|
|
// if adding the lock won't bring us over the threshold difficulty.
|
|
List<string> locksAsStrings = generatedLocks.Select(l => l.GetLock()).ToList();
|
|
locksAsStrings.Add(_lock.GetLock());
|
|
if (RollFourSumTwo.GetProbalityOfAtleastOneUnlocking(locksAsStrings) <= successChance)
|
|
{
|
|
generatedLocks.Add(new Lock(_lock.GetLock()));
|
|
if (FindCombination(pregeneratedLocks, generatedLocks, numLocks, successChance, lockOptions))
|
|
{
|
|
return true;
|
|
}
|
|
// This combination didn't work. Remove the last one and try the next lock.
|
|
generatedLocks.RemoveAt(generatedLocks.Count - 1);
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
private static void Shuffle<T>(List<T> list)
|
|
{
|
|
Random random = new Random();
|
|
|
|
int n = list.Count;
|
|
for (int i = n - 1; i > 0; i--)
|
|
{
|
|
int j = random.Next(i + 1);
|
|
(list[i], list[j]) = (list[j], list[i]);
|
|
}
|
|
}
|
|
}
|
|
} |