package block import ( "github.com/df-mc/dragonfly/server/block/cube" "github.com/df-mc/dragonfly/server/block/model" "github.com/df-mc/dragonfly/server/item" "github.com/df-mc/dragonfly/server/world" "github.com/df-mc/dragonfly/server/world/sound" "github.com/go-gl/mathgl/mgl64" ) // Slab is a half block that allows entities to walk up blocks without jumping. type Slab struct { // Block is the block to use for the type of slab. Block world.Block // Top specifies if the slab is in the top part of the block. Top bool // Double specifies if the slab is a double slab. These double slabs can be made by placing another slab // on an existing slab. Double bool } // UseOnBlock handles the placement of slabs with relation to them being upside down or not and handles slabs // being turned into double slabs. func (s Slab) UseOnBlock(pos cube.Pos, face cube.Face, clickPos mgl64.Vec3, tx *world.Tx, user item.User, ctx *item.UseContext) (used bool) { id, meta := s.EncodeItem() clickedBlock := tx.Block(pos) if clickedSlab, ok := clickedBlock.(Slab); ok && !s.Double { clickedId, clickedMeta := clickedSlab.EncodeItem() if !clickedSlab.Double && id == clickedId && meta == clickedMeta && ((face == cube.FaceUp && !clickedSlab.Top) || (face == cube.FaceDown && clickedSlab.Top)) { // A half slab of the same type was clicked at the top, so we can make it full. clickedSlab.Double = true place(tx, pos, clickedSlab, user, ctx) return placed(ctx) } } if sideSlab, ok := tx.Block(pos.Side(face)).(Slab); ok && !replaceableWith(tx, pos, s) && !s.Double { sideId, sideMeta := sideSlab.EncodeItem() // The block on the side of the one clicked was a slab and the block clicked was not replaceableWith, so // the slab on the side must've been half and may now be filled if the wood types are the same. if !sideSlab.Double && id == sideId && meta == sideMeta { sideSlab.Double = true place(tx, pos.Side(face), sideSlab, user, ctx) return placed(ctx) } } pos, face, used = firstReplaceable(tx, pos, face, s) if !used { return } if face == cube.FaceDown || (clickPos[1] > 0.5 && face != cube.FaceUp) { s.Top = true } place(tx, pos, s, user, ctx) return placed(ctx) } // Instrument ... func (s Slab) Instrument() sound.Instrument { if _, ok := s.Block.(Planks); ok { return sound.Bass() } if _, ok := s.Block.(BambooMosaic); ok { return sound.Bass() } return sound.BassDrum() } // FlammabilityInfo ... func (s Slab) FlammabilityInfo() FlammabilityInfo { if flammable, ok := s.Block.(Flammable); ok { return flammable.FlammabilityInfo() } return newFlammabilityInfo(0, 0, false) } // FuelInfo ... func (s Slab) FuelInfo() item.FuelInfo { if fuel, ok := s.Block.(item.Fuel); ok { return fuel.FuelInfo() } return item.FuelInfo{} } // CanDisplace ... func (s Slab) CanDisplace(b world.Liquid) bool { water, ok := b.(Water) return !s.Double && ok && water.Depth == 8 } // SideClosed ... func (s Slab) SideClosed(pos, side cube.Pos, _ *world.Tx) bool { // Only returns true if the side is below the slab and if the slab is not upside down. return !s.Top && side[1] == pos[1]-1 } // LightDiffusionLevel returns 0 if the slab is a half slab, or 15 if it is double. func (s Slab) LightDiffusionLevel() uint8 { if s.Double { return 15 } return 0 } // CanRedstoneWireStepDown ... func (s Slab) CanRedstoneWireStepDown(cube.Pos, cube.Pos, *world.Tx) bool { return s.Double } // BreakInfo ... func (s Slab) BreakInfo() BreakInfo { hardness, blastResistance, harvestable, effective := 2.0, 30.0, pickaxeHarvestable, pickaxeEffective switch block := s.Block.(type) { case Stone, Sandstone, Quartz, Purpur, Blackstone, PolishedBlackstoneBrick: // These slab types do not match their block's hardness or blast resistance case StoneBricks: if block.Type == MossyStoneBricks() { hardness = 1.5 } case Breakable: breakInfo := block.BreakInfo() hardness, blastResistance, harvestable, effective = breakInfo.Hardness, breakInfo.BlastResistance, breakInfo.Harvestable, breakInfo.Effective } return newBreakInfo(hardness, harvestable, effective, func(tool item.Tool, enchantments []item.Enchantment) []item.Stack { if s.Double { return []item.Stack{item.NewStack(s, 2)} } return []item.Stack{item.NewStack(s, 1)} }).withBlastResistance(blastResistance) } // Model ... func (s Slab) Model() world.BlockModel { return model.Slab{Double: s.Double, Top: s.Top} } // EncodeItem ... func (s Slab) EncodeItem() (string, int16) { name, suffix := encodeSlabBlock(s.Block, false) return "minecraft:" + name + suffix, 0 } // EncodeBlock ... func (s Slab) EncodeBlock() (string, map[string]any) { side := "bottom" if s.Top { side = "top" } name, suffix := encodeSlabBlock(s.Block, s.Double) return "minecraft:" + name + suffix, map[string]any{"minecraft:vertical_half": side} } // allSlabs ... func allSlabs() (b []world.Block) { for _, s := range SlabBlocks() { b = append(b, Slab{Block: s, Double: true}) b = append(b, Slab{Block: s, Top: true, Double: true}) b = append(b, Slab{Block: s, Top: true}) b = append(b, Slab{Block: s}) } return }