// 动物繁殖与村民交易:动物体系.md §3、村民系统.md §7 的核心玩法逻辑。 package entity import ( "encoding/json" "fmt" "os" ) // 动物繁殖参数(动物体系.md §3)。 const ( breedCooldown = 60 * 20 // 繁殖冷却 60 秒(tick) babyGrowTime = 20 * 60 * 20 // 幼崽成长 20 分钟(tick) perChunkAnimal = 10 // 每区块同种动物上限 ) // Animal 动物附加数据(动物体系.md §2)。 type Animal struct { Species int // 0 牛 / 1 猪 / 2 羊 / 3 鸡(声音/外观/掉落/繁殖共用) BreedCooldown int // 繁殖冷却剩余 tick GrowthTicks int // 幼崽成长剩余(0 = 成年) LoveTicks int // 喂食后进入繁殖模式剩余 tick Sheared bool ParentA ID // 近亲繁殖记录(后期) ParentB ID } // AnimalSpecies 读取物种字段;无 Animal 数据时回退 ID%4(动物体系.md §2)。 func AnimalSpecies(e *Entity) int { if e == nil || e.Kind != KindAnimal { return 0 } if d, ok := e.Data.(*Animal); ok { return d.Species } return int(e.ID % 4) } // Breed 繁殖两只同种成年动物 → 幼崽(动物体系.md §3:冷却与上限控制)。 // 返回新幼崽;冷却未到/上限达到返回 nil。 func (m *Manager) Breed(a, b *Entity, w WorldView) *Entity { if a.Kind != KindAnimal || b.Kind != KindAnimal || a.Dead || b.Dead { return nil } sa, sb := AnimalSpecies(a), AnimalSpecies(b) if sa != sb { return nil } ad, ok1 := a.Data.(*Animal) bd, ok2 := b.Data.(*Animal) if !ok1 { ad = &Animal{Species: sa} a.Data = ad } if !ok2 { bd = &Animal{Species: sb} b.Data = bd } if ad.BreedCooldown > 0 || bd.BreedCooldown > 0 || ad.GrowthTicks > 0 || bd.GrowthTicks > 0 { return nil } // 每区块数量控制(防无限繁殖卡服,动物体系.md §7 踩坑) if m.countNear(a, perChunkAnimal) >= perChunkAnimal { return nil } ad.BreedCooldown = breedCooldown bd.BreedCooldown = breedCooldown baby := m.Spawn(KindAnimal, a.Pos[0], a.Pos[1], a.Pos[2]) baby.Data = &Animal{Species: sa, GrowthTicks: babyGrowTime, ParentA: a.ID, ParentB: b.ID} baby.Health = 10 // 幼崽血量 return baby } // TickAnimal 动物成长/冷却计时(调用方每 tick 对动物实体调用)。 func TickAnimal(e *Entity) { if e.Kind != KindAnimal { return } d, ok := e.Data.(*Animal) if !ok { return } if d.BreedCooldown > 0 { d.BreedCooldown-- } if d.LoveTicks > 0 { d.LoveTicks-- } if d.GrowthTicks > 0 { d.GrowthTicks-- if d.GrowthTicks == 0 { e.Health = 20 // 成年 } } } const loveDuration = 30 * 20 // 喂食后 30 秒可配对 // Feed 喂食进入 love;幼崽/冷却中返回 false。 func Feed(e *Entity) bool { if e == nil || e.Kind != KindAnimal || e.Dead { return false } d, ok := e.Data.(*Animal) if !ok { d = &Animal{Species: AnimalSpecies(e)} e.Data = d } if d.GrowthTicks > 0 || d.BreedCooldown > 0 { return false } d.LoveTicks = loveDuration return true } // LovePartner 附近同种 love 中的另一只成年动物。 func (m *Manager) LovePartner(e *Entity) *Entity { if e == nil { return nil } sp := AnimalSpecies(e) for _, o := range m.List() { if o == e || o.Kind != KindAnimal || o.Dead { continue } if AnimalSpecies(o) != sp { continue } d, ok := o.Data.(*Animal) if !ok || d.LoveTicks <= 0 || d.GrowthTicks > 0 { continue } dx, dz := o.Pos[0]-e.Pos[0], o.Pos[2]-e.Pos[2] if dx*dx+dz*dz < 8*8 { return o } } return nil } // ShearSheep 剪羊毛:未剪过的羊掉 1–3 白羊毛。 func ShearSheep(e *Entity, nWool int) bool { if e == nil || e.Kind != KindAnimal || AnimalSpecies(e) != 2 { return false } d, ok := e.Data.(*Animal) if !ok { d = &Animal{Species: 2} e.Data = d } if d.Sheared { return false } d.Sheared = true if nWool < 1 { nWool = 1 } if nWool > 3 { nWool = 3 } return true } // MilkCow 空桶挤牛奶(牛)。 func MilkCow(e *Entity) bool { return e != nil && e.Kind == KindAnimal && !e.Dead && AnimalSpecies(e) == 0 } // countNear 统计同种动物数量(以 a 所在区块计,简化按距离 16)。 func (m *Manager) countNear(a *Entity, _ int) int { n := 0 for _, e := range m.List() { if e.Kind == KindAnimal { dx, dz := e.Pos[0]-a.Pos[0], e.Pos[2]-a.Pos[2] if dx*dx+dz*dz < 16*16 { n++ } } } return n } // ---- 村民交易(村民系统.md §7:数据驱动交易表)---- // TradeOffer 一条交易(收购 → 出售)。 type TradeOffer struct { Buy string `json:"buy"` // 收购物品 BuyN int `json:"buy_n"` // 收购数量 Sell string `json:"sell"` // 出售物品 SellN int `json:"sell_n"` // 出售数量 Uses int `json:"uses"` // 已交易次数(涨价基础) } // TradeTable 交易表(按职业)。 type TradeTable struct { byProfession map[uint8][]TradeOffer } // LoadTrades 解析交易表 JSON。 func LoadTrades(path string) (*TradeTable, error) { b, err := os.ReadFile(path) if err != nil { return nil, fmt.Errorf("entity.LoadTrades: %w", err) } var doc struct { Professions map[string][]TradeOffer `json:"professions"` } if err := json.Unmarshal(b, &doc); err != nil { return nil, fmt.Errorf("entity.LoadTrades: %w", err) } t := &TradeTable{byProfession: make(map[uint8][]TradeOffer)} names := map[string]uint8{"farmer": 0, "builder": 1, "blacksmith": 2, "trader": 3} for name, offers := range doc.Professions { if id, ok := names[name]; ok { t.byProfession[id] = offers } } return t, nil } // OffersFor 返回某职业的交易列表。 func (t *TradeTable) OffersFor(profession uint8) []TradeOffer { return t.byProfession[profession] }