Support per-floor variable maze sizes

Add configuration to vary floor sizes: FloorSizeMode enum, varyFloorSize, floorSizeMode and manualFloorSizes in MazeReworkConfig. MazeReworkGenerator now accepts explicit width/depth via an overload, threads width/depth through generation, and adds optional endBounds to placement logic; dead-end/furthest searches respect bounds. MazeReworkManager computes per-floor seeds and dynamic sizes (manual or randomized), instantiates per-floor generators, and passes end bounds between floors for both animated and instantaneous generation. Also includes minor IDE workspace.xml metadata formatting changes.
This commit is contained in:
2026-07-04 16:39:29 +07:00
parent 0745556ae6
commit 315c14c651
4 changed files with 157 additions and 40 deletions

View File

@@ -12,7 +12,9 @@
<component name="ChangeListManager"> <component name="ChangeListManager">
<list default="true" id="d308d1cb-09fc-4331-ba20-00f7b43d1576" name="Changes" comment=""> <list default="true" id="d308d1cb-09fc-4331-ba20-00f7b43d1576" name="Changes" comment="">
<change beforePath="$PROJECT_DIR$/.idea/.idea.BABA_YAGA/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/.idea.BABA_YAGA/.idea/workspace.xml" afterDir="false" /> <change beforePath="$PROJECT_DIR$/.idea/.idea.BABA_YAGA/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/.idea.BABA_YAGA/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Assets/Scripts/Baba_yaga/GameSetup/MazeRework/MazeReworkSpawner.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Assets/Scripts/Baba_yaga/GameSetup/MazeRework/MazeReworkSpawner.cs" afterDir="false" /> <change beforePath="$PROJECT_DIR$/Assets/Scripts/Baba_yaga/GameSetup/MazeRework/MazeReworkConfig.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Assets/Scripts/Baba_yaga/GameSetup/MazeRework/MazeReworkConfig.cs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Assets/Scripts/Baba_yaga/GameSetup/MazeRework/MazeReworkGenerator.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Assets/Scripts/Baba_yaga/GameSetup/MazeRework/MazeReworkGenerator.cs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Assets/Scripts/Baba_yaga/GameSetup/MazeRework/MazeReworkManager.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Assets/Scripts/Baba_yaga/GameSetup/MazeRework/MazeReworkManager.cs" afterDir="false" />
</list> </list>
<option name="SHOW_DIALOG" value="false" /> <option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" /> <option name="HIGHLIGHT_CONFLICTS" value="true" />
@@ -36,10 +38,10 @@
<setting file="file://$PROJECT_DIR$/Library/PackageCache/com.wooshii.foldericons@201a18f355d3/FolderIcons/Editor/FolderIcons.cs" root0="SKIP_HIGHLIGHTING" /> <setting file="file://$PROJECT_DIR$/Library/PackageCache/com.wooshii.foldericons@201a18f355d3/FolderIcons/Editor/FolderIcons.cs" root0="SKIP_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Library/PackageCache/com.wooshii.foldericons@201a18f355d3/FolderIcons/Editor/FolderIconsEditor.asmdef" root0="SKIP_HIGHLIGHTING" /> <setting file="file://$PROJECT_DIR$/Library/PackageCache/com.wooshii.foldericons@201a18f355d3/FolderIcons/Editor/FolderIconsEditor.asmdef" root0="SKIP_HIGHLIGHTING" />
</component> </component>
<component name="KubernetesApiPersistence"><![CDATA[{}]]></component> <component name="KubernetesApiPersistence">{}</component>
<component name="KubernetesApiProvider"><![CDATA[{ <component name="KubernetesApiProvider">{
"isMigrated": true &quot;isMigrated&quot;: true
}]]></component> }</component>
<component name="McpProjectServerCommands"> <component name="McpProjectServerCommands">
<commands /> <commands />
<urls /> <urls />
@@ -146,6 +148,8 @@
<workItem from="1781765548760" duration="8210000" /> <workItem from="1781765548760" duration="8210000" />
<workItem from="1782445122703" duration="1948000" /> <workItem from="1782445122703" duration="1948000" />
<workItem from="1782825955934" duration="1783000" /> <workItem from="1782825955934" duration="1783000" />
<workItem from="1783149945807" duration="606000" />
<workItem from="1783156047890" duration="1863000" />
</task> </task>
<servers /> <servers />
</component> </component>

View File

@@ -18,6 +18,12 @@ namespace Baba_yaga.GameSetup.MazeRework
/// <summary> /// <summary>
/// Configuration asset containing parameters for the reworked maze generation. /// Configuration asset containing parameters for the reworked maze generation.
/// </summary> /// </summary>
public enum FloorSizeMode
{
Manual,
Random
}
[CreateAssetMenu(fileName = "MazeReworkConfig", menuName = "BABA_YAGA/MazeRework/Config")] [CreateAssetMenu(fileName = "MazeReworkConfig", menuName = "BABA_YAGA/MazeRework/Config")]
public class MazeReworkConfig : ScriptableObject public class MazeReworkConfig : ScriptableObject
{ {
@@ -36,6 +42,22 @@ namespace Baba_yaga.GameSetup.MazeRework
[MinValue(5)] [MinValue(5)]
public int depth = 21; public int depth = 21;
[FoldoutGroup("Grid Dimensions")]
[Tooltip("If true, allows you to vary the floor size across multiple floors.")]
public bool varyFloorSize = false;
[FoldoutGroup("Grid Dimensions")]
[ShowIf("varyFloorSize")]
[Tooltip("Random will pick random sizes up to the max width/depth. Manual uses the list below.")]
[EnumToggleButtons]
public FloorSizeMode floorSizeMode = FloorSizeMode.Random;
[FoldoutGroup("Grid Dimensions")]
[ShowIf("@varyFloorSize && floorSizeMode == FloorSizeMode.Manual")]
[ListDrawerSettings(ShowIndexLabels = true)]
[Tooltip("List of manual sizes for each floor (Index 0 = Floor 0, etc). Must be odd numbers >= 5.")]
public System.Collections.Generic.List<Vector2Int> manualFloorSizes = new System.Collections.Generic.List<Vector2Int>();
[FoldoutGroup("Seeding")] [FoldoutGroup("Seeding")]
[Tooltip("If true, a random seed will be generated at runtime.")] [Tooltip("If true, a random seed will be generated at runtime.")]
public bool useRandomSeed = true; public bool useRandomSeed = true;

View File

@@ -49,10 +49,21 @@ namespace Baba_yaga.GameSetup.MazeRework
} }
private readonly MazeReworkConfig _config; private readonly MazeReworkConfig _config;
private readonly int _width;
private readonly int _depth;
public MazeReworkGenerator(MazeReworkConfig config) public MazeReworkGenerator(MazeReworkConfig config)
{ {
_config = config; _config = config;
_width = config != null ? config.width : 21;
_depth = config != null ? config.depth : 21;
}
public MazeReworkGenerator(MazeReworkConfig config, int width, int depth)
{
_config = config;
_width = width;
_depth = depth;
} }
/// <summary> /// <summary>
@@ -69,12 +80,12 @@ namespace Baba_yaga.GameSetup.MazeRework
/// Generates a 2D layout of the maze of size config.width by config.depth using a specific seed. /// Generates a 2D layout of the maze of size config.width by config.depth using a specific seed.
/// Generates the maze grid. /// Generates the maze grid.
/// </summary> /// </summary>
public MazeReworkCellType[,] Generate(int seed, Vector2Int? forcedStart = null, Vector2Int? forcedDirection = null) public MazeReworkCellType[,] Generate(int seed, Vector2Int? forcedStart = null, Vector2Int? forcedDirection = null, Vector2Int? endBounds = null)
{ {
if (_config == null) return null; if (_config == null) return null;
int width = _config.width; int width = _width;
int depth = _config.depth; int depth = _depth;
var grid = new MazeReworkCellType[width, depth]; var grid = new MazeReworkCellType[width, depth];
// 1. Initialize all cells as Wall // 1. Initialize all cells as Wall
@@ -129,7 +140,7 @@ namespace Baba_yaga.GameSetup.MazeRework
CarveLoops(grid, rng, width, depth); CarveLoops(grid, rng, width, depth);
// 8. Place Start & End points // 8. Place Start & End points
PlaceStartAndEnd(grid, rooms, width, depth, rng, forcedStart, forcedDirection, null); PlaceStartAndEnd(grid, rooms, width, depth, rng, forcedStart, forcedDirection, null, endBounds);
return grid; return grid;
} }
@@ -149,12 +160,12 @@ namespace Baba_yaga.GameSetup.MazeRework
return GenerateAnimated(seed); return GenerateAnimated(seed);
} }
public (MazeReworkCellType[,] grid, List<MazeCellChange> history) GenerateAnimated(int seed, Vector2Int? forcedStart = null, Vector2Int? forcedDirection = null) public (MazeReworkCellType[,] grid, List<MazeCellChange> history) GenerateAnimated(int seed, Vector2Int? forcedStart = null, Vector2Int? forcedDirection = null, Vector2Int? endBounds = null)
{ {
if (_config == null) return (null, null); if (_config == null) return (null, null);
int width = _config.width; int width = _width;
int depth = _config.depth; int depth = _depth;
var grid = new MazeReworkCellType[width, depth]; var grid = new MazeReworkCellType[width, depth];
var history = new List<MazeCellChange>(width * depth * 2); var history = new List<MazeCellChange>(width * depth * 2);
@@ -219,7 +230,7 @@ namespace Baba_yaga.GameSetup.MazeRework
// 8. Place Start & End points (no highlighting needed for this step in animation) // 8. Place Start & End points (no highlighting needed for this step in animation)
currentPhase = MazeAnimationPhase.StartEnd; currentPhase = MazeAnimationPhase.StartEnd;
PlaceStartAndEnd(grid, rooms, width, depth, rng, forcedStart, forcedDirection, recordNoHighlight); PlaceStartAndEnd(grid, rooms, width, depth, rng, forcedStart, forcedDirection, history, endBounds);
return (grid, history); return (grid, history);
} }
@@ -469,9 +480,12 @@ namespace Baba_yaga.GameSetup.MazeRework
} }
} }
private void PlaceStartAndEnd(MazeReworkCellType[,] grid, List<Room> rooms, int width, int depth, System.Random rng, Vector2Int? forcedStart, Vector2Int? forcedDirection, private void PlaceStartAndEnd(MazeReworkCellType[,] grid, List<Room> rooms, int width, int depth, System.Random rng, Vector2Int? forcedStart, Vector2Int? forcedDirection, List<MazeCellChange> history = null, Vector2Int? endBounds = null)
Action<int, int, MazeReworkCellType> onCellChanged = null)
{ {
Action<int, int, MazeReworkCellType> onCellChanged = history != null
? (x, z, type) => history.Add(new MazeCellChange(x, z, type, MazeCellHighlight.None, MazeAnimationPhase.StartEnd))
: (Action<int, int, MazeReworkCellType>)null;
Vector2Int startPt = new Vector2Int(-1, -1); Vector2Int startPt = new Vector2Int(-1, -1);
if (forcedStart.HasValue) if (forcedStart.HasValue)
@@ -480,7 +494,7 @@ namespace Baba_yaga.GameSetup.MazeRework
} }
else else
{ {
startPt = FindDeadEnd(grid, width, depth, rng, new Vector2Int(-1, -1)); startPt = FindDeadEnd(grid, width, depth, rng, new Vector2Int(-1, -1), endBounds);
if (startPt.x == -1) if (startPt.x == -1)
{ {
// Fallback to config start if no corridors exist // Fallback to config start if no corridors exist
@@ -493,17 +507,22 @@ namespace Baba_yaga.GameSetup.MazeRework
grid[startPt.x, startPt.y] = MazeReworkCellType.Start; grid[startPt.x, startPt.y] = MazeReworkCellType.Start;
onCellChanged?.Invoke(startPt.x, startPt.y, MazeReworkCellType.Start); onCellChanged?.Invoke(startPt.x, startPt.y, MazeReworkCellType.Start);
Vector2Int endPt = FindDeadEnd(grid, width, depth, rng, startPt); Vector2Int endPt = FindDeadEnd(grid, width, depth, rng, startPt, endBounds);
if (endPt.x == -1) if (endPt.x == -1)
{ {
// Fallback: just pick the furthest corridor cell from startPt // Fallback: just pick the furthest corridor cell from startPt
endPt = FindFurthestCorridor(grid, width, depth, startPt); endPt = FindFurthestCorridor(grid, width, depth, startPt, endBounds);
if (endPt.x == -1) if (endPt.x == -1)
{ {
// Extreme fallback // Extreme fallback
int ex = width - 2, ez = depth - 2; int ex = width - 2, ez = depth - 2;
if (endBounds.HasValue)
{
ex = endBounds.Value.x - 2;
ez = endBounds.Value.y - 2;
}
EnsureValidOddCoordinates(width, depth, ref ex, ref ez); EnsureValidOddCoordinates(width, depth, ref ex, ref ez);
endPt = new Vector2Int(ex, ez); endPt = new Vector2Int(ex, ez);
} }
@@ -518,14 +537,17 @@ namespace Baba_yaga.GameSetup.MazeRework
EnforceSingleConnection(grid, endPt.x, endPt.y, null); EnforceSingleConnection(grid, endPt.x, endPt.y, null);
} }
private Vector2Int FindDeadEnd(MazeReworkCellType[,] grid, int width, int depth, System.Random rng, Vector2Int exclude) private Vector2Int FindDeadEnd(MazeReworkCellType[,] grid, int width, int depth, System.Random rng, Vector2Int exclude, Vector2Int? bounds = null)
{ {
List<Vector2Int> deadEnds = new List<Vector2Int>(); List<Vector2Int> deadEnds = new List<Vector2Int>();
List<Vector2Int> corridors = new List<Vector2Int>(); List<Vector2Int> corridors = new List<Vector2Int>();
for (int z = 1; z < depth - 1; z++) int maxW = bounds.HasValue ? Mathf.Min(width, bounds.Value.x) : width;
int maxD = bounds.HasValue ? Mathf.Min(depth, bounds.Value.y) : depth;
for (int z = 1; z < maxD - 1; z++)
{ {
for (int x = 1; x < width - 1; x++) for (int x = 1; x < maxW - 1; x++)
{ {
if (grid[x, z] == MazeReworkCellType.Corridor) if (grid[x, z] == MazeReworkCellType.Corridor)
{ {
@@ -551,14 +573,17 @@ namespace Baba_yaga.GameSetup.MazeRework
return new Vector2Int(-1, -1); return new Vector2Int(-1, -1);
} }
private Vector2Int FindFurthestCorridor(MazeReworkCellType[,] grid, int width, int depth, Vector2Int from) private Vector2Int FindFurthestCorridor(MazeReworkCellType[,] grid, int width, int depth, Vector2Int from, Vector2Int? bounds = null)
{ {
Vector2Int best = new Vector2Int(-1, -1); Vector2Int best = new Vector2Int(-1, -1);
float maxDist = -1f; float maxDist = -1f;
for (int z = 1; z < depth - 1; z++) int maxW = bounds.HasValue ? Mathf.Min(width, bounds.Value.x) : width;
int maxD = bounds.HasValue ? Mathf.Min(depth, bounds.Value.y) : depth;
for (int z = 1; z < maxD - 1; z++)
{ {
for (int x = 1; x < width - 1; x++) for (int x = 1; x < maxW - 1; x++)
{ {
if (grid[x, z] == MazeReworkCellType.Corridor) if (grid[x, z] == MazeReworkCellType.Corridor)
{ {

View File

@@ -122,34 +122,83 @@ namespace Baba_yaga.GameSetup.MazeRework
ClearMaze(); ClearMaze();
if (animator != null) animator.StopAnimation(); if (animator != null) animator.StopAnimation();
var generator = new MazeReworkGenerator(config);
if (animateGeneration && animator != null) if (animateGeneration && animator != null)
{ {
if (Application.isPlaying) if (Application.isPlaying)
{ {
StartCoroutine(GenerateAndSpawnAnimatedRoutine(generator)); StartCoroutine(GenerateAndSpawnAnimatedRoutine());
} }
else else
{ {
Debug.LogWarning("[MazeReworkManager] Animation is only supported in Play Mode. Falling back to instantaneous generation."); Debug.LogWarning("[MazeReworkManager] Animation is only supported in Play Mode. Falling back to instantaneous generation.");
GenerateInstantaneous(generator); GenerateInstantaneous();
} }
} }
else else
{ {
GenerateInstantaneous(generator); GenerateInstantaneous();
} }
} }
private IEnumerator GenerateAndSpawnAnimatedRoutine(MazeReworkGenerator generator) private void GetDynamicFloorSize(int floorIndex, int seed, out int width, out int depth)
{ {
width = config.width;
depth = config.depth;
if (config.varyFloorSize)
{
if (config.floorSizeMode == FloorSizeMode.Manual)
{
if (config.manualFloorSizes != null && floorIndex < config.manualFloorSizes.Count)
{
width = config.manualFloorSizes[floorIndex].x;
depth = config.manualFloorSizes[floorIndex].y;
}
}
else // Random
{
System.Random rng = new System.Random(seed);
// Generate random odd sizes between 5 and config.width/depth
int wRange = (config.width - 5) / 2;
int dRange = (config.depth - 5) / 2;
width = 5 + rng.Next(0, wRange + 1) * 2;
depth = 5 + rng.Next(0, dRange + 1) * 2;
}
// Ensure they are odd and at least 5
if (width % 2 == 0) width--;
if (depth % 2 == 0) depth--;
if (width < 5) width = 5;
if (depth < 5) depth = 5;
}
}
private IEnumerator GenerateAndSpawnAnimatedRoutine()
{
int[] floorSeeds = new int[floorCount];
Vector2Int[] floorSizes = new Vector2Int[floorCount];
for (int i = 0; i < floorCount; i++)
{
floorSeeds[i] = config.useRandomSeed ? System.Guid.NewGuid().GetHashCode() : config.seed + i * 1000;
GetDynamicFloorSize(i, floorSeeds[i], out int fW, out int fD);
floorSizes[i] = new Vector2Int(fW, fD);
}
Vector2Int? nextStartPos = null; Vector2Int? nextStartPos = null;
Vector2Int? nextStartDir = null; Vector2Int? nextStartDir = null;
for (int i = 0; i < floorCount; i++) for (int i = 0; i < floorCount; i++)
{ {
int seed = config.useRandomSeed ? System.Guid.NewGuid().GetHashCode() : config.seed + i * 1000; int fWidth = floorSizes[i].x;
var (grid, history) = generator.GenerateAnimated(seed, nextStartPos, nextStartDir); int fDepth = floorSizes[i].y;
var generator = new MazeReworkGenerator(config, fWidth, fDepth);
Vector2Int? endBounds = null;
if (i < floorCount - 1)
{
endBounds = floorSizes[i + 1];
}
var (grid, history) = generator.GenerateAnimated(floorSeeds[i], nextStartPos, nextStartDir, endBounds);
if (i > 0 && nextStartPos.HasValue) if (i > 0 && nextStartPos.HasValue)
{ {
@@ -159,9 +208,9 @@ namespace Baba_yaga.GameSetup.MazeRework
if (i < floorCount - 1) if (i < floorCount - 1)
{ {
Vector2Int endPos = new Vector2Int(-1, -1); Vector2Int endPos = new Vector2Int(-1, -1);
for (int z = 0; z < config.depth; z++) for (int z = 0; z < fDepth; z++)
{ {
for (int x = 0; x < config.width; x++) for (int x = 0; x < fWidth; x++)
{ {
if (grid[x, z] == MazeReworkCellType.End) { endPos = new Vector2Int(x, z); break; } if (grid[x, z] == MazeReworkCellType.End) { endPos = new Vector2Int(x, z); break; }
} }
@@ -192,14 +241,32 @@ namespace Baba_yaga.GameSetup.MazeRework
Debug.Log($"[MazeReworkManager] All floors animated! Beginning world position: {BeginningWorldPosition}"); Debug.Log($"[MazeReworkManager] All floors animated! Beginning world position: {BeginningWorldPosition}");
} }
private void GenerateInstantaneous(MazeReworkGenerator generator) private void GenerateInstantaneous()
{ {
int[] floorSeeds = new int[floorCount];
Vector2Int[] floorSizes = new Vector2Int[floorCount];
for (int i = 0; i < floorCount; i++)
{
floorSeeds[i] = config.useRandomSeed ? System.Guid.NewGuid().GetHashCode() : config.seed + i * 1000;
GetDynamicFloorSize(i, floorSeeds[i], out int fW, out int fD);
floorSizes[i] = new Vector2Int(fW, fD);
}
Vector2Int? nextStartPos = null; Vector2Int? nextStartPos = null;
Vector2Int? nextStartDir = null; Vector2Int? nextStartDir = null;
for (int i = 0; i < floorCount; i++) for (int i = 0; i < floorCount; i++)
{ {
int seed = config.useRandomSeed ? System.Guid.NewGuid().GetHashCode() : config.seed + i * 1000; int fWidth = floorSizes[i].x;
var grid = generator.Generate(seed, nextStartPos, nextStartDir); int fDepth = floorSizes[i].y;
var generator = new MazeReworkGenerator(config, fWidth, fDepth);
Vector2Int? endBounds = null;
if (i < floorCount - 1)
{
endBounds = floorSizes[i + 1];
}
var grid = generator.Generate(floorSeeds[i], nextStartPos, nextStartDir, endBounds);
// If this is not the first floor, replace the Start with StairsDown to connect to the floor below // If this is not the first floor, replace the Start with StairsDown to connect to the floor below
if (i > 0 && nextStartPos.HasValue) if (i > 0 && nextStartPos.HasValue)
@@ -207,13 +274,12 @@ namespace Baba_yaga.GameSetup.MazeRework
grid[nextStartPos.Value.x, nextStartPos.Value.y] = MazeReworkCellType.StairsDown; grid[nextStartPos.Value.x, nextStartPos.Value.y] = MazeReworkCellType.StairsDown;
} }
// If there is a floor above this one, convert the End cell to a StairsUp
if (i < floorCount - 1) if (i < floorCount - 1)
{ {
Vector2Int endPos = new Vector2Int(-1, -1); Vector2Int endPos = new Vector2Int(-1, -1);
for (int z = 0; z < config.depth; z++) for (int z = 0; z < fDepth; z++)
{ {
for (int x = 0; x < config.width; x++) for (int x = 0; x < fWidth; x++)
{ {
if (grid[x, z] == MazeReworkCellType.End) if (grid[x, z] == MazeReworkCellType.End)
{ {