// 掉落物实体:合并与拾取(实体系统.md §5)。 package entity import ( "math" "time" ) // 掉落物参数(实体系统.md §5)。 const ( dropMergeDist = 1.0 // 同物品合并距离 dropStackMax = 9999 // 单堆叠合并上限(地上大堆;拾取时按背包栈上限拆分) dropPickupDist = 1.5 // 拾取半径 dropPerChunk = 32 // 每区块掉落物上限 ) // dropPickupDelay 生成后可拾取延迟(测试可置 0)。 var dropPickupDelay = time.Second // Drop 掉落物实体数据。 type Drop struct { Item string Count int Born time.Time // 生成时刻(1 秒内不可拾取,掉落可见期) } // SpawnDrop 生成掉落物:同物品近距合并(上限 dropStackMax);超上限丢弃。 func (m *Manager) SpawnDrop(itemName string, count int, x, y, z float64) { if count <= 0 { return } m.mu.Lock() defer m.mu.Unlock() // 合并 if old, ok := m.drops[itemName]; ok && !old.Dead { dx, dz := old.Pos[0]-x, old.Pos[2]-z if math.Sqrt(dx*dx+dz*dz) < dropMergeDist { if d, ok := old.Data.(*Drop); ok && d.Count+count <= dropStackMax { d.Count += count return } } } // 上限检查 drops := 0 for _, e := range m.entities { if e.Kind == KindItemDrop { drops++ } } if drops >= dropPerChunk*8 { // 全局粗上限(按区块过滤后置) return } m.next++ e := &Entity{ID: m.next, Kind: KindItemDrop, Pos: [3]float64{x, y, z}, Health: 1, Data: &Drop{Item: itemName, Count: count, Born: time.Now()}} e.Vel = [3]float64{(rand01(e.ID) - 0.5) * 2, 4, (rand01(e.ID>>16) - 0.5) * 2} // 弹出初速度 m.entities[e.ID] = e m.drops[itemName] = e } // Data 实体附加数据。 // rand01 实体 ID 派生确定性随机(掉落初速度用,存档回放一致)。 func rand01(seed ID) float64 { const mod = 1000003 return float64(seed%mod) / mod } // PickupNearby 拾取玩家附近掉落物,返回拾取的物品列表(实体系统.md §5 拾取)。 // 生成后 1 秒内不可拾取(掉落可见期,与原版一致)。 func (m *Manager) PickupNearby(px, py, pz float64) []Drop { var out []Drop for _, e := range m.List() { if e.Kind != KindItemDrop || e.Dead { continue } d, ok := e.Data.(*Drop) if !ok { continue } if time.Since(d.Born) < dropPickupDelay { continue // 新鲜掉落:可拾取延迟(掉落可见) } dx, dy, dz := e.Pos[0]-px, e.Pos[1]-py, e.Pos[2]-pz if dx*dx+dy*dy+dz*dz < dropPickupDist*dropPickupDist { e.Dead = true m.Remove(e.ID) out = append(out, *d) } } return out }