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  }