1
1
mirror of https://github.com/OpenDiablo2/OpenDiablo2 synced 2024-11-03 01:37:18 -04:00
OpenDiablo2/d2core/d2map/d2mapgen/act1_overworld.go
gravestench fc87b2be7a
Removing d2datadict singletons (#738)
* Remove weapons, armor, misc, itemCommon, itemTyps datadict singletons

- removed loader calls from d2app
- removed the HeroObjects singleton from `d2core/d2inventory`
- added an InventoryItemFactory in d2inventory
- package-level functions that use data records are now methods of the InventoryItemFactory
- renamed ItemGenerator in d2item to ItemFactory
- package-level functions that use records are now methods of ItemFactory
- d2map.MapEntityFactory now has an item factory instance for creating items
- fixed a bug in unique item record loader where it loaded an empty record
- added a PlayerStateFactory for creating a player state (uses the asset manager)
- updated the test inventory/equipment code in d2player to handle errors from the ItemFactory
- character select and character creation screens have a player state and inventory item factory
- updated item tests to use the item factory

* minor edit

* Removed d2datadict.Experience singleton

added a HeroStatsFactory, much like the other factories. The factory  gets an
asset manager reference in order to use data records.

* removed d2datadict.AutoMagic singleton

* removed d2datadict.AutoMap singleton

* removed d2datadict.BodyLocations singleton

* removed d2datadict.Books singleton

* Removed singletons for level records

- removed loader calls in d2app
- changed type references from d2datadict to d2records
- added a `MapGenerator` in d2mapgen which uses thew asset manager and map engine
- package-level map generation functions are now MapGenerator methods
- `d2datadict.GetLevelDetails(id int)` is now a method of the RecordManager

* remove SkillCalc and MissileCalc singletons

* Removed CharStats and ItemStatCost singletons

- added an ItemStatFactory which uses the asset manager to create stats
- package-level functions for stats in d2item are now StatFactory methods
- changed type references from d2datadict to d2records
- `d2player.GetAllPlayerStates` is now a method of the `PlayerStateFactory`

* Removed DkillDesc and Skills singletons from d2datadict

- removed loader calls from d2app
- diablo2stats.Stat instances are given a reference to the factory for doing record lookups

* update the stats test to use mock a asset manager and stat factory

* fixed diablo2stats tests and diablo2item tests

* removed CompCodes singleton from d2datadict

* remove cubemain singleton from d2datadict

* removed DifficultyLevels singleton from d2datadict

* removed ElemTypes singleton from d2datadict

* removed events.go loader from d2datadict (was unused)

* removed Gems singleton from d2datadict

* removed Hireling and Inventory singletons from d2datadict

* removed MagicPrefix and MagicSuffix singletons from d2datadict

* removed ItemRatios singleton from d2datadict

* removed Missiles singleton from d2datadict

* removed MonModes singleton

* Removed all monster and npc singletons from d2datadict

- MapStamp instances now get a reference to their factory for doing record lookups

* removed SoundEntry and SoundEnviron singletons from d2datadict
2020-09-20 17:52:01 -04:00

299 lines
10 KiB
Go

package d2mapgen
import (
"log"
"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"
)
// 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(2)
g.engine.ResetMap(d2enum.RegionAct1Town, 150, 150)
mapWidth := g.engine.Size().Width
mapHeight := g.engine.Size().Height
townStamp := g.engine.LoadStamp(d2enum.RegionAct1Town, 1, -1)
townStamp.RegionPath()
townSize := townStamp.Size()
log.Printf("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)
for y := townSize.Height; y < mapHeight-9; y += 9 {
g.engine.PlaceStamp(rightWaterBorderStamp, mapWidth-17, y)
g.engine.PlaceStamp(rightWaterBorderStamp2, mapWidth-9, y)
}
g.generateWilderness1TownSouth(mapWidth-wilderness1Details.SizeXNormal-14, townSize.Height)
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)
}
}
func (g *MapGenerator) generateWilderness1TownEast(startX, startY int) {
levelDetails := g.asset.Records.GetLevelDetails(2)
fenceNorthStamp := []*d2mapstamp.Stamp{
g.loadPreset(d2wilderness.TreeBorderNorth, 0),
g.loadPreset(d2wilderness.TreeBorderNorth, 1),
g.loadPreset(d2wilderness.TreeBorderNorth, 2),
}
fenceSouthStamp := []*d2mapstamp.Stamp{
g.loadPreset(d2wilderness.TreeBorderSouth, 0),
g.loadPreset(d2wilderness.TreeBorderSouth, 1),
g.loadPreset(d2wilderness.TreeBorderSouth, 2),
}
fenceWestStamp := []*d2mapstamp.Stamp{
g.loadPreset(d2wilderness.TreeBorderWest, 0),
g.loadPreset(d2wilderness.TreeBorderWest, 1),
g.loadPreset(d2wilderness.TreeBorderWest, 2),
}
fenceEastStamp := []*d2mapstamp.Stamp{
g.loadPreset(d2wilderness.TreeBorderEast, 0),
g.loadPreset(d2wilderness.TreeBorderEast, 1),
g.loadPreset(d2wilderness.TreeBorderEast, 2),
}
fenceSouthWestStamp := g.loadPreset(d2wilderness.TreeBorderSouthWest, 0)
fenceNorthEastStamp := g.loadPreset(d2wilderness.TreeBorderNorthEast, 0)
fenceSouthEastStamp := g.loadPreset(d2wilderness.TreeBorderSouthEast, 0)
fenceWestEdge := g.loadPreset(d2wilderness.TreeBoxNorthEast, 0)
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)
}
func (g *MapGenerator) generateWilderness1TownSouth(startX, startY int) {
levelDetails := g.asset.Records.GetLevelDetails(2)
fenceNorthStamp := []*d2mapstamp.Stamp{
g.loadPreset(d2wilderness.TreeBorderNorth, 0),
g.loadPreset(d2wilderness.TreeBorderNorth, 1),
g.loadPreset(d2wilderness.TreeBorderNorth, 2),
}
fenceWestStamp := []*d2mapstamp.Stamp{
g.loadPreset(d2wilderness.TreeBorderWest, 0),
g.loadPreset(d2wilderness.TreeBorderWest, 1),
g.loadPreset(d2wilderness.TreeBorderWest, 2),
}
fenceSouthStamp := []*d2mapstamp.Stamp{
g.loadPreset(d2wilderness.TreeBorderSouth, 0),
g.loadPreset(d2wilderness.TreeBorderSouth, 1),
g.loadPreset(d2wilderness.TreeBorderSouth, 2),
}
fenceNorthWestStamp := g.loadPreset(d2wilderness.TreeBorderNorthWest, 0)
fenceSouthWestStamp := g.loadPreset(d2wilderness.TreeBorderSouthWest, 0)
fenceWaterBorderSouthEast := g.loadPreset(d2wilderness.WaterBorderEast, 1)
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)
}
func (g *MapGenerator) generateWilderness1TownWest(startX, startY int) {
levelDetails := g.asset.Records.GetLevelDetails(2)
fenceEastEdge := g.loadPreset(d2wilderness.TreeBoxSouthWest, 0)
fenceNorthWestStamp := g.loadPreset(d2wilderness.TreeBorderNorthWest, 0)
fenceNorthEastStamp := g.loadPreset(d2wilderness.TreeBorderNorthEast, 0)
fenceSouthWestStamp := g.loadPreset(d2wilderness.TreeBorderSouthWest, 0)
fenceSouthStamp := []*d2mapstamp.Stamp{
g.loadPreset(d2wilderness.TreeBorderSouth, 0),
g.loadPreset(d2wilderness.TreeBorderSouth, 1),
g.loadPreset(d2wilderness.TreeBorderSouth, 2),
}
fenceNorthStamp := []*d2mapstamp.Stamp{
g.loadPreset(d2wilderness.TreeBorderNorth, 0),
g.loadPreset(d2wilderness.TreeBorderNorth, 1),
g.loadPreset(d2wilderness.TreeBorderNorth, 2),
}
fenceEastStamp := []*d2mapstamp.Stamp{
g.loadPreset(d2wilderness.TreeBorderEast, 0),
g.loadPreset(d2wilderness.TreeBorderEast, 1),
g.loadPreset(d2wilderness.TreeBorderEast, 2),
}
fenceWestStamp := []*d2mapstamp.Stamp{
g.loadPreset(d2wilderness.TreeBorderWest, 0),
g.loadPreset(d2wilderness.TreeBorderWest, 1),
g.loadPreset(d2wilderness.TreeBorderWest, 2),
}
// 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)
}
func (g *MapGenerator) generateWilderness1Contents(rect d2geom.Rectangle) {
levelDetails := g.asset.Records.GetLevelDetails(2)
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)
tile.Components.Floors = []d2ds1.FloorShadowRecord{{Prop1: 1, Style: 0, Sequence: 0}} // wildernessGrass
tile.PrepareTile(x, y, g.engine)
}
}
stuff := []*d2mapstamp.Stamp{
g.loadPreset(d2wilderness.StoneFill1, 0),
g.loadPreset(d2wilderness.StoneFill1, 1),
g.loadPreset(d2wilderness.StoneFill1, 2),
g.loadPreset(d2wilderness.StoneFill2, 0),
g.loadPreset(d2wilderness.StoneFill2, 1),
g.loadPreset(d2wilderness.StoneFill2, 2),
g.loadPreset(d2wilderness.Cottages1, 0),
g.loadPreset(d2wilderness.Cottages1, 1),
g.loadPreset(d2wilderness.Cottages1, 2),
g.loadPreset(d2wilderness.Cottages1, 3),
g.loadPreset(d2wilderness.Cottages1, 4),
g.loadPreset(d2wilderness.Cottages1, 5),
g.loadPreset(d2wilderness.FallenCamp1, 0),
g.loadPreset(d2wilderness.FallenCamp1, 1),
g.loadPreset(d2wilderness.FallenCamp1, 2),
g.loadPreset(d2wilderness.FallenCamp1, 3),
g.loadPreset(d2wilderness.Pond, 0),
g.loadPreset(d2wilderness.SwampFill1, 0),
g.loadPreset(d2wilderness.SwampFill2, 0),
}
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++
}
}
}