clone

an offline map explorer where each world, from a game map to a city's open data, ships as one .atlas file

fix: keep colliding pyramids in directories of their own

Dami committed a2ee4b2
Two sources capturing the same game name the same map, and the shared
tile tree named their pyramids identically -- the second built would
replace the first, and both games' bundles then embedded whichever
survived. When names collide, every collider now takes its layer path
into its directory name, all of them, so the outcome does not depend on
the order the archive lists its games in.
commit
a2ee4b2179af3da2b6c40953f1bcdc8fd2f3c2a3
parent
ecce87aa1c51b67e377ea47c75ea0d1bb754fabc
author
Dami <dami.etoile@gmail.com>
1 file changed, +48 -19
Mtools/tiles/main.go67+++++++++++++++++++++---------
M tools/tiles/main.go +48 -19
@@ -308,7 +308,12 @@ func run(source, output string, force bool) error {
308308 var carried int309309 derived := make(map[string]bool)310310 311- out := manifest{TileSize: tileSize, Size: worldSize}311+ type pendingPlan struct {312+ gameTitle string313+ title string314+ plan tilePlan315+ }316+ var pending []pendingPlan312317 for _, game := range index.Games {313318 mapDirs, err := filepath.Glob(filepath.Join(source, game.Directory, "maps", "*"))314319 if err != nil {
@@ -332,27 +337,51 @@ func run(source, output string, force bool) error {
332337 continue333338 }334339 for _, plan := range plans {335- stamp := planStamp(plan)336- if kept, ok := carry(built, plan, stamp, output, force); ok {337- // The window is read from the archive rather than derived338- // from the pyramid, so it is answered from the plan even339- // for a layer nothing has changed under.340- kept.Grid = plan.Frame.grid()341- out.Variants = append(out.Variants, kept)342- carried++343- continue344- }345- fmt.Printf("tile %s / %s / %s\n", game.Title, title, plan.SourcePath)346- entry, err := buildPyramid(temp, plan)347- if err != nil {348- return fmt.Errorf("%s / %s: %w", title, plan.SourcePath, err)349- }350- entry.Stamp = stamp351- out.Variants = append(out.Variants, entry)352- derived[plan.AssetPath] = true340+ pending = append(pending, pendingPlan{gameTitle: game.Title, title: title, plan: plan})353341 }354342 }355343 }344+345+ // Two sources capturing the same game name the same directory for the346+ // same map. Every collider takes its layer path into its name -- all of347+ // them, so the outcome does not depend on the order the archive lists348+ // its games in.349+ byAssetPath := make(map[string][]int, len(pending))350+ for index := range pending {351+ byAssetPath[pending[index].plan.AssetPath] = append(352+ byAssetPath[pending[index].plan.AssetPath], index)353+ }354+ for _, colliders := range byAssetPath {355+ if len(colliders) < 2 {356+ continue357+ }358+ for _, at := range colliders {359+ pending[at].plan.AssetPath += "__" + slugify(pending[at].plan.SourcePath)360+ }361+ }362+363+ out := manifest{TileSize: tileSize, Size: worldSize}364+ for _, entry := range pending {365+ plan := entry.plan366+ stamp := planStamp(plan)367+ if kept, ok := carry(built, plan, stamp, output, force); ok {368+ // The window is read from the archive rather than derived369+ // from the pyramid, so it is answered from the plan even370+ // for a layer nothing has changed under.371+ kept.Grid = plan.Frame.grid()372+ out.Variants = append(out.Variants, kept)373+ carried++374+ continue375+ }376+ fmt.Printf("tile %s / %s / %s\n", entry.gameTitle, entry.title, plan.SourcePath)377+ variant, err := buildPyramid(temp, plan)378+ if err != nil {379+ return fmt.Errorf("%s / %s: %w", entry.title, plan.SourcePath, err)380+ }381+ variant.Stamp = stamp382+ out.Variants = append(out.Variants, variant)383+ derived[plan.AssetPath] = true384+ }356385 sort.Slice(out.Variants, func(i, j int) bool {357386 return out.Variants[i].SourcePath < out.Variants[j].SourcePath358387 })