github.com/df-mc/dragonfly@v0.9.13/server/block/slab.go (about) 1 package block 2 3 import ( 4 "github.com/df-mc/dragonfly/server/block/cube" 5 "github.com/df-mc/dragonfly/server/block/model" 6 "github.com/df-mc/dragonfly/server/item" 7 "github.com/df-mc/dragonfly/server/world" 8 "github.com/df-mc/dragonfly/server/world/sound" 9 "github.com/go-gl/mathgl/mgl64" 10 "time" 11 ) 12 13 // Slab is a half block that allows entities to walk up blocks without jumping. 14 type Slab struct { 15 // Block is the block to use for the type of slab. 16 Block world.Block 17 // Top specifies if the slab is in the top part of the block. 18 Top bool 19 // Double specifies if the slab is a double slab. These double slabs can be made by placing another slab 20 // on an existing slab. 21 Double bool 22 } 23 24 // UseOnBlock handles the placement of slabs with relation to them being upside down or not and handles slabs 25 // being turned into double slabs. 26 func (s Slab) UseOnBlock(pos cube.Pos, face cube.Face, clickPos mgl64.Vec3, w *world.World, user item.User, ctx *item.UseContext) (used bool) { 27 id, meta := s.EncodeItem() 28 clickedBlock := w.Block(pos) 29 if clickedSlab, ok := clickedBlock.(Slab); ok && !s.Double { 30 clickedId, clickedMeta := clickedSlab.EncodeItem() 31 if !clickedSlab.Double && id == clickedId && meta == clickedMeta && ((face == cube.FaceUp && !clickedSlab.Top) || (face == cube.FaceDown && clickedSlab.Top)) { 32 // A half slab of the same type was clicked at the top, so we can make it full. 33 clickedSlab.Double = true 34 35 place(w, pos, clickedSlab, user, ctx) 36 return placed(ctx) 37 } 38 } 39 if sideSlab, ok := w.Block(pos.Side(face)).(Slab); ok && !replaceableWith(w, pos, s) && !s.Double { 40 sideId, sideMeta := sideSlab.EncodeItem() 41 // The block on the side of the one clicked was a slab and the block clicked was not replaceableWith, so 42 // the slab on the side must've been half and may now be filled if the wood types are the same. 43 if !sideSlab.Double && id == sideId && meta == sideMeta { 44 sideSlab.Double = true 45 46 place(w, pos.Side(face), sideSlab, user, ctx) 47 return placed(ctx) 48 } 49 } 50 pos, face, used = firstReplaceable(w, pos, face, s) 51 if !used { 52 return 53 } 54 if face == cube.FaceDown || (clickPos[1] > 0.5 && face != cube.FaceUp) { 55 s.Top = true 56 } 57 58 place(w, pos, s, user, ctx) 59 return placed(ctx) 60 } 61 62 // Instrument ... 63 func (s Slab) Instrument() sound.Instrument { 64 if _, ok := s.Block.(Planks); ok { 65 return sound.Bass() 66 } 67 return sound.BassDrum() 68 } 69 70 // FlammabilityInfo ... 71 func (s Slab) FlammabilityInfo() FlammabilityInfo { 72 if w, ok := s.Block.(Planks); ok && w.Wood.Flammable() { 73 return newFlammabilityInfo(5, 20, true) 74 } 75 return newFlammabilityInfo(0, 0, false) 76 } 77 78 // FuelInfo ... 79 func (s Slab) FuelInfo() item.FuelInfo { 80 if w, ok := s.Block.(Planks); ok && w.Wood.Flammable() { 81 return newFuelInfo(time.Second * 15) 82 } 83 return item.FuelInfo{} 84 } 85 86 // CanDisplace ... 87 func (s Slab) CanDisplace(b world.Liquid) bool { 88 water, ok := b.(Water) 89 return !s.Double && ok && water.Depth == 8 90 } 91 92 // SideClosed ... 93 func (s Slab) SideClosed(pos, side cube.Pos, _ *world.World) bool { 94 // Only returns true if the side is below the slab and if the slab is not upside down. 95 return !s.Top && side[1] == pos[1]-1 96 } 97 98 // LightDiffusionLevel returns 0 if the slab is a half slab, or 15 if it is double. 99 func (s Slab) LightDiffusionLevel() uint8 { 100 if s.Double { 101 return 15 102 } 103 return 0 104 } 105 106 // BreakInfo ... 107 func (s Slab) BreakInfo() BreakInfo { 108 hardness, blastResistance, harvestable, effective := 2.0, 30.0, pickaxeHarvestable, pickaxeEffective 109 110 switch block := s.Block.(type) { 111 // TODO: Copper 112 // TODO: Deepslate 113 case EndBricks: 114 hardness = 3.0 115 blastResistance = 45.0 116 case StoneBricks: 117 if block.Type == MossyStoneBricks() { 118 hardness = 1.5 119 } 120 case Andesite: 121 if block.Polished { 122 hardness = 1.5 123 } 124 case Diorite: 125 if block.Polished { 126 hardness = 1.5 127 } 128 case Granite: 129 if block.Polished { 130 hardness = 1.5 131 } 132 case Prismarine: 133 hardness = 1.5 134 case Planks: 135 harvestable = alwaysHarvestable 136 effective = axeEffective 137 blastResistance = 15.0 138 } 139 return newBreakInfo(hardness, harvestable, effective, func(tool item.Tool, enchantments []item.Enchantment) []item.Stack { 140 if s.Double { 141 return []item.Stack{item.NewStack(s, 2)} 142 } 143 return []item.Stack{item.NewStack(s, 1)} 144 }).withBlastResistance(blastResistance) 145 } 146 147 // Model ... 148 func (s Slab) Model() world.BlockModel { 149 return model.Slab{Double: s.Double, Top: s.Top} 150 } 151 152 // EncodeItem ... 153 func (s Slab) EncodeItem() (string, int16) { 154 id, slabType, meta := encodeSlabBlock(s.Block) 155 if slabType != "" { 156 return "minecraft:" + encodeLegacySlabId(slabType), meta 157 } 158 return "minecraft:" + id + "_slab", meta 159 } 160 161 // EncodeBlock ... 162 func (s Slab) EncodeBlock() (string, map[string]any) { 163 id, slabType, _ := encodeSlabBlock(s.Block) 164 side := "bottom" 165 if s.Top { 166 side = "top" 167 } 168 properties := map[string]any{"minecraft:vertical_half": side} 169 if slabType != "" { 170 properties[slabType] = id 171 id = encodeLegacySlabId(slabType) 172 if s.Double { 173 id = "double_" + id 174 } 175 } else if s.Double { 176 id = id + "_double_slab" 177 } else { 178 id = id + "_slab" 179 } 180 return "minecraft:" + id, properties 181 } 182 183 // allSlabs ... 184 func allSlabs() (b []world.Block) { 185 for _, s := range SlabBlocks() { 186 b = append(b, Slab{Block: s, Double: true}) 187 b = append(b, Slab{Block: s, Top: true, Double: true}) 188 b = append(b, Slab{Block: s, Top: true}) 189 b = append(b, Slab{Block: s}) 190 } 191 return 192 }