Major pipeline changes to map building, introducing 3player map, and adding Shared build mode to level authoring options
This commit is contained in:
parent
749c9203de
commit
2b1aee3e03
37 changed files with 5418 additions and 30 deletions
|
|
@ -802,17 +802,33 @@ namespace TD.Levels.Editor
|
|||
}
|
||||
|
||||
// P5-8: zones with neither outgoing leak nor goal adjacency.
|
||||
var zonesWithLeaks = new HashSet<PlayerSlot>();
|
||||
foreach (var l in ctx.LeakExitVolumes) zonesWithLeaks.Add(l.sourceZone);
|
||||
|
||||
foreach (var owner in ctx.DeclaredOwners)
|
||||
//
|
||||
// OwnerOnly maps only. The rule asserts the lane-chain topology those maps are built
|
||||
// around: each player's zone either hands off to the next or is the final defender.
|
||||
//
|
||||
// On a Shared map that assertion is wrong, not merely unnecessary. Zones there are
|
||||
// typically directly adjacent with no gate between them, so most have no outgoing leak
|
||||
// and are not goal-adjacent — and that is the intended shape, because the whole map is
|
||||
// one contiguous maze. The property P5-8 actually protects ("enemies from this zone have
|
||||
// somewhere to go") is enforced more strictly by P5-4 above, which runs a real BFS from
|
||||
// every spawner to the exit set — and with no leak volumes present, that exit set is the
|
||||
// goal tiles, which is exactly the right requirement. Zones with no spawners can't
|
||||
// strand anything: WaveManager skips them.
|
||||
if (ctx.Authoring.buildAccess == BuildAccess.OwnerOnly)
|
||||
{
|
||||
bool hasLeak = zonesWithLeaks.Contains(owner);
|
||||
bool isGoalAdj = goalAdjacentZones.Contains(owner);
|
||||
if (!hasLeak && !isGoalAdj)
|
||||
var zonesWithLeaks = new HashSet<PlayerSlot>();
|
||||
foreach (var l in ctx.LeakExitVolumes) zonesWithLeaks.Add(l.sourceZone);
|
||||
|
||||
foreach (var owner in ctx.DeclaredOwners)
|
||||
{
|
||||
report.Error("P5-8", $"Player zone {owner} has no outgoing leak exit AND is not goal-adjacent. " +
|
||||
"Every zone must either lead somewhere or be a final defender.");
|
||||
bool hasLeak = zonesWithLeaks.Contains(owner);
|
||||
bool isGoalAdj = goalAdjacentZones.Contains(owner);
|
||||
if (!hasLeak && !isGoalAdj)
|
||||
{
|
||||
report.Error("P5-8", $"Player zone {owner} has no outgoing leak exit AND is not goal-adjacent. " +
|
||||
"Every zone must either lead somewhere or be a final defender. " +
|
||||
"(This rule is skipped on Shared-build maps.)");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -919,6 +935,7 @@ namespace TD.Levels.Editor
|
|||
data.PlayerCount = ctx.Authoring.playerCount;
|
||||
data.MapDescription = ctx.Authoring.mapDescription;
|
||||
data.Author = ctx.Authoring.author;
|
||||
data.BuildAccess = ctx.Authoring.buildAccess;
|
||||
data.GridOriginTile = ctx.GridOriginTile;
|
||||
data.GridSize = ctx.GridSize;
|
||||
|
||||
|
|
@ -1152,7 +1169,10 @@ namespace TD.Levels.Editor
|
|||
sb.Append("playerCount=").Append(ctx.Authoring.playerCount.ToString(inv)).Append('|');
|
||||
sb.Append("expectedGoalCount=").Append(ctx.Authoring.expectedGoalCount.ToString(inv)).Append('|');
|
||||
sb.Append("mapDescription=").Append(ctx.Authoring.mapDescription ?? "").Append('|');
|
||||
sb.Append("author=").Append(ctx.Authoring.author ?? "").Append('\n');
|
||||
sb.Append("author=").Append(ctx.Authoring.author ?? "").Append('|');
|
||||
// By NAME, matching the per-volume enum convention below — a future reordering of the
|
||||
// enum must not silently produce a matching hash for different rules.
|
||||
sb.Append("buildAccess=").Append(ctx.Authoring.buildAccess.ToString()).Append('\n');
|
||||
|
||||
// Volume blocks, ordered by canonical path.
|
||||
var rootTransform = ctx.Authoring.transform;
|
||||
|
|
@ -1354,6 +1374,7 @@ namespace TD.Levels.Editor
|
|||
target.PlayerCount = src.PlayerCount;
|
||||
target.MapDescription = src.MapDescription;
|
||||
target.Author = src.Author;
|
||||
target.BuildAccess = src.BuildAccess;
|
||||
target.ScenePath = src.ScenePath;
|
||||
target.AuthoringHash = src.AuthoringHash;
|
||||
target.LastBakeTimestamp = src.LastBakeTimestamp;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue