package d2mapgen // magic number suppression has been added because most of the work done here // is experiemental, and mapgen will likely change dramatically in the future. import ( "math/rand" "strings" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2ds1" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2geom" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2map/d2mapgen/d2wilderness" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2map/d2mapstamp" ) const ( presetA = iota presetB presetC presetD presetE presetF ) const ( wildernessDetailsRecordID = 2 ) const ( mapWidth = 150 mapHeight = mapWidth mapMargin = 9 autoFileIndex = -1 ) // GenerateAct1Overworld generates the map and entities for the first town and surrounding area. func (g *MapGenerator) GenerateAct1Overworld() { rand.Seed(g.engine.Seed()) wilderness1Details := g.asset.Records.GetLevelDetails(wildernessDetailsRecordID) g.engine.ResetMap(d2enum.RegionAct1Town, mapWidth, mapHeight) mapWidth := g.engine.Size().Width mapHeight := g.engine.Size().Height townStamp := g.engine.LoadStamp(d2enum.RegionAct1Town, presetB, autoFileIndex) townStamp.RegionPath() townSize := townStamp.Size() g.Infof("Region Path: %s", townStamp.RegionPath()) switch { case strings.Contains(townStamp.RegionPath(), "E1"): g.engine.PlaceStamp(townStamp, 0, 0) g.generateWilderness1TownEast(townSize.Width, 0) case strings.Contains(townStamp.RegionPath(), "S1"): g.engine.PlaceStamp(townStamp, mapWidth-townSize.Width, 0) rightWaterBorderStamp := g.loadPreset(d2wilderness.WaterBorderEast, 0) rightWaterBorderStamp2 := g.loadPreset(d2wilderness.WaterBorderWest, 0) mapInnerHeight := mapHeight - mapMargin mapInnerWidth := mapWidth - mapMargin for y := townSize.Height; y < mapInnerHeight; y += mapMargin { g.engine.PlaceStamp(rightWaterBorderStamp, mapInnerWidth-mapMargin, y) g.engine.PlaceStamp(rightWaterBorderStamp2, mapInnerWidth, y) } edgeOffset := 14 startX := mapWidth - wilderness1Details.SizeXNormal - edgeOffset startY := townSize.Height g.generateWilderness1TownSouth(startX, startY) case strings.Contains(townStamp.RegionPath(), "W1"): g.engine.PlaceStamp(townStamp, mapWidth-townSize.Width, mapHeight-townSize.Height) startX := mapWidth - townSize.Width - wilderness1Details.SizeXNormal startY := mapHeight - wilderness1Details.SizeYNormal g.generateWilderness1TownWest(startX, startY) default: g.engine.PlaceStamp(townStamp, mapWidth-townSize.Width, mapHeight-townSize.Height) } } // nolint:gosec,gomnd // we dont need crypto-strong randomness, mapgen will get a refactor soon func (g *MapGenerator) generateWilderness1TownEast(startX, startY int) { levelDetails := g.asset.Records.GetLevelDetails(wildernessDetailsRecordID) fenceNorthStamp := []*d2mapstamp.Stamp{ g.loadPreset(d2wilderness.TreeBorderNorth, presetA), g.loadPreset(d2wilderness.TreeBorderNorth, presetB), g.loadPreset(d2wilderness.TreeBorderNorth, presetC), } fenceSouthStamp := []*d2mapstamp.Stamp{ g.loadPreset(d2wilderness.TreeBorderSouth, presetA), g.loadPreset(d2wilderness.TreeBorderSouth, presetB), g.loadPreset(d2wilderness.TreeBorderSouth, presetC), } fenceWestStamp := []*d2mapstamp.Stamp{ g.loadPreset(d2wilderness.TreeBorderWest, presetA), g.loadPreset(d2wilderness.TreeBorderWest, presetB), g.loadPreset(d2wilderness.TreeBorderWest, presetC), } fenceEastStamp := []*d2mapstamp.Stamp{ g.loadPreset(d2wilderness.TreeBorderEast, presetA), g.loadPreset(d2wilderness.TreeBorderEast, presetB), g.loadPreset(d2wilderness.TreeBorderEast, presetC), } fenceSouthWestStamp := g.loadPreset(d2wilderness.TreeBorderSouthWest, presetA) fenceNorthEastStamp := g.loadPreset(d2wilderness.TreeBorderNorthEast, presetA) fenceSouthEastStamp := g.loadPreset(d2wilderness.TreeBorderSouthEast, presetA) fenceWestEdge := g.loadPreset(d2wilderness.TreeBoxNorthEast, presetA) areaRect := d2geom.Rectangle{ Left: startX, Top: startY + 9, Width: levelDetails.SizeXNormal, Height: levelDetails.SizeYNormal - 3, } g.generateWilderness1Contents(areaRect) // Draw the north and south fence for i := 0; i < 9; i++ { g.engine.PlaceStamp(fenceNorthStamp[rand.Intn(3)], startX+(i*9), startY) g.engine.PlaceStamp(fenceSouthStamp[rand.Intn(3)], startX+(i*9), startY+(levelDetails.SizeYNormal+6)) } // West fence for i := 1; i < 6; i++ { g.engine.PlaceStamp(fenceWestStamp[rand.Intn(3)], startX, startY+(levelDetails.SizeYNormal+6)-(i*9)) } // East Fence for i := 1; i < 10; i++ { g.engine.PlaceStamp(fenceEastStamp[rand.Intn(3)], startX+levelDetails.SizeXNormal, startY+(i*9)) } g.engine.PlaceStamp(fenceSouthWestStamp, startX, startY+levelDetails.SizeYNormal+6) g.engine.PlaceStamp(fenceWestEdge, startX, startY+(levelDetails.SizeYNormal-3)-45) g.engine.PlaceStamp(fenceNorthEastStamp, startX+levelDetails.SizeXNormal, startY) g.engine.PlaceStamp(fenceSouthEastStamp, startX+levelDetails.SizeXNormal, startY+levelDetails.SizeYNormal+6) } // nolint:gosec,gomnd // we dont need crypto-strong randomness, mapgen will get a refactor soon func (g *MapGenerator) generateWilderness1TownSouth(startX, startY int) { levelDetails := g.asset.Records.GetLevelDetails(wildernessDetailsRecordID) fenceNorthStamp := []*d2mapstamp.Stamp{ g.loadPreset(d2wilderness.TreeBorderNorth, presetA), g.loadPreset(d2wilderness.TreeBorderNorth, presetB), g.loadPreset(d2wilderness.TreeBorderNorth, presetC), } fenceWestStamp := []*d2mapstamp.Stamp{ g.loadPreset(d2wilderness.TreeBorderWest, presetA), g.loadPreset(d2wilderness.TreeBorderWest, presetB), g.loadPreset(d2wilderness.TreeBorderWest, presetC), } fenceSouthStamp := []*d2mapstamp.Stamp{ g.loadPreset(d2wilderness.TreeBorderSouth, presetA), g.loadPreset(d2wilderness.TreeBorderSouth, presetB), g.loadPreset(d2wilderness.TreeBorderSouth, presetC), } fenceNorthWestStamp := g.loadPreset(d2wilderness.TreeBorderNorthWest, presetA) fenceSouthWestStamp := g.loadPreset(d2wilderness.TreeBorderSouthWest, presetB) fenceWaterBorderSouthEast := g.loadPreset(d2wilderness.WaterBorderEast, presetC) areaRect := d2geom.Rectangle{ Left: startX + 2, Top: startY, Width: levelDetails.SizeXNormal - 2, Height: levelDetails.SizeYNormal - 3, } g.generateWilderness1Contents(areaRect) // Draw the north fence for i := 0; i < 4; i++ { g.engine.PlaceStamp(fenceNorthStamp[rand.Intn(3)], startX+(i*9)+5, startY-6) } // Draw the west fence for i := 0; i < 8; i++ { g.engine.PlaceStamp(fenceWestStamp[rand.Intn(3)], startX, startY+(i*9)+3) } // Draw the south fence for i := 1; i < 9; i++ { g.engine.PlaceStamp(fenceSouthStamp[rand.Intn(3)], startX+(i*9), startY+(8*9)+3) } g.engine.PlaceStamp(fenceNorthWestStamp, startX, startY-6) g.engine.PlaceStamp(fenceSouthWestStamp, startX, startY+(8*9)+3) g.engine.PlaceStamp(fenceWaterBorderSouthEast, startX+(9*9)-4, startY+(8*9)+1) } // nolint:gosec,gomnd // we dont need crypto-strong randomness, mapgen will get a refactor soon func (g *MapGenerator) generateWilderness1TownWest(startX, startY int) { levelDetails := g.asset.Records.GetLevelDetails(wildernessDetailsRecordID) fenceEastEdge := g.loadPreset(d2wilderness.TreeBoxSouthWest, presetA) fenceNorthWestStamp := g.loadPreset(d2wilderness.TreeBorderNorthWest, presetA) fenceNorthEastStamp := g.loadPreset(d2wilderness.TreeBorderNorthEast, presetA) fenceSouthWestStamp := g.loadPreset(d2wilderness.TreeBorderSouthWest, presetA) fenceSouthStamp := []*d2mapstamp.Stamp{ g.loadPreset(d2wilderness.TreeBorderSouth, presetA), g.loadPreset(d2wilderness.TreeBorderSouth, presetB), g.loadPreset(d2wilderness.TreeBorderSouth, presetC), } fenceNorthStamp := []*d2mapstamp.Stamp{ g.loadPreset(d2wilderness.TreeBorderNorth, presetA), g.loadPreset(d2wilderness.TreeBorderNorth, presetB), g.loadPreset(d2wilderness.TreeBorderNorth, presetC), } fenceEastStamp := []*d2mapstamp.Stamp{ g.loadPreset(d2wilderness.TreeBorderEast, presetA), g.loadPreset(d2wilderness.TreeBorderEast, presetB), g.loadPreset(d2wilderness.TreeBorderEast, presetC), } fenceWestStamp := []*d2mapstamp.Stamp{ g.loadPreset(d2wilderness.TreeBorderWest, presetA), g.loadPreset(d2wilderness.TreeBorderWest, presetB), g.loadPreset(d2wilderness.TreeBorderWest, presetC), } // Draw the north and south fences for i := 0; i < 9; i++ { if i > 0 && i < 8 { g.engine.PlaceStamp(fenceNorthStamp[rand.Intn(3)], startX+(i*9)-1, startY-15) } g.engine.PlaceStamp(fenceSouthStamp[rand.Intn(3)], startX+(i*9)-1, startY+levelDetails.SizeYNormal-12) } // Draw the east fence for i := 0; i < 6; i++ { g.engine.PlaceStamp(fenceEastStamp[rand.Intn(3)], startX+levelDetails.SizeXNormal-9, startY+(i*9)-6) } // Draw the west fence for i := 0; i < 9; i++ { g.engine.PlaceStamp(fenceWestStamp[rand.Intn(3)], startX, startY+(i*9)-6) } // Draw the west fence g.engine.PlaceStamp(fenceEastEdge, startX+levelDetails.SizeXNormal-9, startY+39) g.engine.PlaceStamp(fenceNorthWestStamp, startX, startY-15) g.engine.PlaceStamp(fenceSouthWestStamp, startX, startY+levelDetails.SizeYNormal-12) g.engine.PlaceStamp(fenceNorthEastStamp, startX+levelDetails.SizeXNormal-9, startY-15) areaRect := d2geom.Rectangle{ Left: startX + 9, Top: startY - 10, Width: levelDetails.SizeXNormal - 9, Height: levelDetails.SizeYNormal - 2, } g.generateWilderness1Contents(areaRect) } // nolint:gosec,gomnd // we dont need crypto-strong randomness, mapgen will get a refactor soon func (g *MapGenerator) generateWilderness1Contents(rect d2geom.Rectangle) { levelDetails := g.asset.Records.GetLevelDetails(wildernessDetailsRecordID) denOfEvil := g.loadPreset(d2wilderness.DenOfEvilEntrance, 0) denOfEvilLoc := d2geom.Point{ X: rect.Left + (rect.Width / 2) + rand.Intn(10), Y: rect.Top + (rect.Height / 2) + rand.Intn(10), } // Fill in the grass for y := 0; y < rect.Height; y++ { for x := 0; x < rect.Width; x++ { tile := g.engine.Tile(rect.Left+x, rect.Top+y) tile.RegionType = d2enum.RegionIdType(levelDetails.LevelType) floorTile := d2ds1.Tile{} floorTile.Prop1 = 1 tile.Components.Floors = []d2ds1.Tile{floorTile} // wildernessGrass tile.PrepareTile(x, y, g.engine) } } stuff := []*d2mapstamp.Stamp{ g.loadPreset(d2wilderness.StoneFill1, presetA), g.loadPreset(d2wilderness.StoneFill1, presetB), g.loadPreset(d2wilderness.StoneFill1, presetC), g.loadPreset(d2wilderness.StoneFill2, presetA), g.loadPreset(d2wilderness.StoneFill2, presetB), g.loadPreset(d2wilderness.StoneFill2, presetC), g.loadPreset(d2wilderness.Cottages1, presetA), g.loadPreset(d2wilderness.Cottages1, presetB), g.loadPreset(d2wilderness.Cottages1, presetC), g.loadPreset(d2wilderness.Cottages1, presetD), g.loadPreset(d2wilderness.Cottages1, presetE), g.loadPreset(d2wilderness.Cottages1, presetF), g.loadPreset(d2wilderness.FallenCamp1, presetA), g.loadPreset(d2wilderness.FallenCamp1, presetB), g.loadPreset(d2wilderness.FallenCamp1, presetC), g.loadPreset(d2wilderness.FallenCamp1, presetD), g.loadPreset(d2wilderness.Pond, presetA), g.loadPreset(d2wilderness.SwampFill1, presetA), g.loadPreset(d2wilderness.SwampFill2, presetA), } g.engine.PlaceStamp(denOfEvil, denOfEvilLoc.X, denOfEvilLoc.Y) numPlaced := 0 for numPlaced < 25 { stamp := stuff[rand.Intn(len(stuff))] stampRect := d2geom.Rectangle{ Left: rect.Left + rand.Intn(rect.Width) - stamp.Size().Width, Top: rect.Top + rand.Intn(rect.Height) - stamp.Size().Height, Width: stamp.Size().Width, Height: stamp.Size().Height, } if areaEmpty(g.engine, stampRect) { g.engine.PlaceStamp(stamp, stampRect.Left, stampRect.Top) numPlaced++ } } }