Files
ngzz-mc/internal/worldgen/village.go

131 lines
4.2 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 村庄生成:结构 pass(村民系统.md §2、世界生态.md §3)。
//
// 规则:村庄按 384 格网格撒点(噪声抖动选位,仅平原/森林群系),
// 每个村庄水井 + 3 间房屋;建筑按绝对坐标定义,跨区块落位(世界生成.md §4)。
package worldgen
import (
"mc/internal/block"
"mc/internal/chunk"
)
// 村庄参数(村民系统.md §2)。
const (
villageSpacing = 384 // 村庄网格间距(格)
villageJitter = 96 // 中心抖动半径
villageRadius = 32 // 村庄建筑最大半径(区块跨越判定)
)
// villageCenter 返回网格点 (gx, gz) 派生出的村庄中心绝对坐标。
func (g *Generator) villageCenter(gx, gz int32) (int32, int32) {
jx := int32(g.ore.Eval2(float64(gx)*7.7+91, float64(gz)*3.1+17) * villageJitter)
jz := int32(g.ore.Eval2(float64(gx)*5.3+57, float64(gz)*8.9+29) * villageJitter)
return gx*villageSpacing + villageSpacing/2 + jx, gz*villageSpacing + villageSpacing/2 + jz
}
// placeVillage 生成跨越本区块的村庄建筑(世界生成.md §4 跨区块安全)。
func (g *Generator) placeVillage(c *chunk.Chunk, cx, cz int32) {
minX, maxX := cx*16, cx*16+15
minZ, maxZ := cz*16, cz*16+15
// 附近的网格点(±1 个网格内即可覆盖)
for dgx := int32(-1); dgx <= 1; dgx++ {
for dgz := int32(-1); dgz <= 1; dgz++ {
gx := floorDiv32(cx*16, villageSpacing) + dgx
gz := floorDiv32(cz*16, villageSpacing) + dgz
vx, vz := g.villageCenter(gx, gz)
// 村庄建筑范围与本区块相交?
if vx < minX-villageRadius || vx > maxX+villageRadius || vz < minZ-villageRadius || vz > maxZ+villageRadius {
continue
}
// 群系白名单(沙漠/雪原不建村)
b := g.biomeAt(vx, vz)
if b != biomePlains && b != biomeForest {
continue
}
// 水井 + 房屋(绝对坐标,逐块过滤到当前区块)
h := int32(g.heightAt(vx, vz))
g.placeWellAbs(c, cx, cz, vx, h, vz)
offsets := [][2]int32{{-12, -8}, {8, -10}, {-4, 12}}
for i, o := range offsets {
hx, hz := vx+o[0], vz+o[1]
hh := int32(g.heightAt(hx, hz))
g.placeHouseAbs(c, cx, cz, hx, hh, hz, uint64(gx)*73856093^uint64(gz)*19349663^uint64(i))
}
}
}
}
// inChunk 判定绝对坐标是否属于区块 (cx, cz)。
func inChunk(x, z, cx, cz int32) bool {
return floorDiv32(x, 16) == cx && floorDiv32(z, 16) == cz
}
// setAbs 绝对坐标写入(跨区块过滤,世界生成.md §4)。
func (g *Generator) setAbs(c *chunk.Chunk, cx, cz, x, y, z int32, s block.State) {
if y < 0 || y >= chunk.Height {
return
}
if !inChunk(x, z, cx, cz) {
return
}
c.Fill(int(x-cx*16), int(y), int(z-cz*16), s)
}
// placeWellAbs 水井:圆石环 + 中心水(3×3,村民系统.md §2 布局)。
func (g *Generator) placeWellAbs(c *chunk.Chunk, cx, cz, wx, h, wz int32) {
for dx := int32(-1); dx <= 1; dx++ {
for dz := int32(-1); dz <= 1; dz++ {
if dx == 0 && dz == 0 {
g.setAbs(c, cx, cz, wx+dx, h+1, wz+dz, g.water) // 井水
} else {
g.setAbs(c, cx, cz, wx+dx, h+1, wz+dz, g.cobble) // 井沿
}
}
}
}
// placeHouseAbs 村民房屋:木板地基 + 墙高 3(门洞)+ 平顶外沿 + 四角火把。
// 布局与 entity.Blueprint 一致(村民系统.md §6.2)。
func (g *Generator) placeHouseAbs(c *chunk.Chunk, cx, cz, hx, h, hz int32, seed uint64) {
w, d := int32(5+seed%2), int32(5) // 5–6 × 5
for x := int32(0); x < w; x++ {
for z := int32(0); z < d; z++ {
g.setAbs(c, cx, cz, hx+x, h+1, hz+z, g.planks)
}
}
// 墙(2 层,南侧门洞)
for y := int32(2); y <= 3; y++ {
for x := int32(0); x < w; x++ {
for z := int32(0); z < d; z++ {
edge := x == 0 || x == w-1 || z == 0 || z == d-1
if !edge {
continue
}
if y == 2 && z == d-1 && x == w/2 {
continue // 门洞
}
g.setAbs(c, cx, cz, hx+x, h+y, hz+z, g.planks)
}
}
}
// 屋顶外沿
for x := int32(-1); x <= w; x++ {
for z := int32(-1); z <= d; z++ {
g.setAbs(c, cx, cz, hx+x, h+4, hz+z, g.planks)
}
}
// 四角火把
for _, cc := range [4][2]int32{{0, 0}, {w - 1, 0}, {0, d - 1}, {w - 1, d - 1}} {
g.setAbs(c, cx, cz, hx+cc[0], h+3, hz+cc[1], g.torch)
}
}
// floorDiv32 负安全除法(villageCenter 网格定位)。
func floorDiv32(a, b int32) int32 {
q := a / b
if a%b < 0 {
q--
}
return q
}