github.com/df-mc/dragonfly@v0.9.13/server/block/sea_pickle.go (about)

     1  package block
     2  
     3  import (
     4  	"github.com/df-mc/dragonfly/server/block/cube"
     5  	"github.com/df-mc/dragonfly/server/item"
     6  	"github.com/df-mc/dragonfly/server/world"
     7  	"github.com/df-mc/dragonfly/server/world/particle"
     8  	"github.com/go-gl/mathgl/mgl64"
     9  	"math"
    10  	"math/rand"
    11  )
    12  
    13  // SeaPickle is a small stationary underwater block that emits light, and is typically found in colonies of up to
    14  // four sea pickles.
    15  type SeaPickle struct {
    16  	empty
    17  	transparent
    18  	sourceWaterDisplacer
    19  
    20  	// AdditionalCount is the amount of additional sea pickles clustered together.
    21  	AdditionalCount int
    22  	// Dead is whether the sea pickles are not alive. Sea pickles are only considered alive when inside of water. While
    23  	// alive, sea pickles emit light & can be grown with bone meal.
    24  	Dead bool
    25  }
    26  
    27  // canSurvive ...
    28  func (SeaPickle) canSurvive(pos cube.Pos, w *world.World) bool {
    29  	below := w.Block(pos.Side(cube.FaceDown))
    30  	if !below.Model().FaceSolid(pos.Side(cube.FaceDown), cube.FaceUp, w) {
    31  		return false
    32  	}
    33  	if emitter, ok := below.(LightDiffuser); ok && emitter.LightDiffusionLevel() != 15 {
    34  		return false
    35  	}
    36  	return true
    37  }
    38  
    39  // BoneMeal ...
    40  func (s SeaPickle) BoneMeal(pos cube.Pos, w *world.World) bool {
    41  	if s.Dead {
    42  		return false
    43  	}
    44  	if coral, ok := w.Block(pos.Side(cube.FaceDown)).(CoralBlock); !ok || coral.Dead {
    45  		return false
    46  	}
    47  
    48  	if s.AdditionalCount != 3 {
    49  		s.AdditionalCount = 3
    50  		w.SetBlock(pos, s, nil)
    51  	}
    52  
    53  	for x := -2; x <= 2; x++ {
    54  		distance := -int(math.Abs(float64(x))) + 2
    55  		for z := -distance; z <= distance; z++ {
    56  			for y := -1; y < 1; y++ {
    57  				if (x == 0 && y == 0 && z == 0) || rand.Intn(6) != 0 {
    58  					continue
    59  				}
    60  				newPos := pos.Add(cube.Pos{x, y, z})
    61  
    62  				if _, ok := w.Block(newPos).(Water); !ok {
    63  					continue
    64  				}
    65  				if coral, ok := w.Block(newPos.Side(cube.FaceDown)).(CoralBlock); !ok || coral.Dead {
    66  					continue
    67  				}
    68  				w.SetBlock(newPos, SeaPickle{AdditionalCount: rand.Intn(3) + 1}, nil)
    69  			}
    70  		}
    71  	}
    72  
    73  	return true
    74  }
    75  
    76  // UseOnBlock ...
    77  func (s SeaPickle) UseOnBlock(pos cube.Pos, face cube.Face, _ mgl64.Vec3, w *world.World, user item.User, ctx *item.UseContext) bool {
    78  	if existing, ok := w.Block(pos).(SeaPickle); ok {
    79  		if existing.AdditionalCount >= 3 {
    80  			return false
    81  		}
    82  
    83  		existing.AdditionalCount++
    84  		place(w, pos, existing, user, ctx)
    85  		return placed(ctx)
    86  	}
    87  
    88  	pos, _, used := firstReplaceable(w, pos, face, s)
    89  	if !used {
    90  		return false
    91  	}
    92  	if !s.canSurvive(pos, w) {
    93  		return false
    94  	}
    95  
    96  	s.Dead = true
    97  	if liquid, ok := w.Liquid(pos); ok {
    98  		_, ok = liquid.(Water)
    99  		s.Dead = !ok
   100  	}
   101  
   102  	place(w, pos, s, user, ctx)
   103  	return placed(ctx)
   104  }
   105  
   106  // NeighbourUpdateTick ...
   107  func (s SeaPickle) NeighbourUpdateTick(pos, _ cube.Pos, w *world.World) {
   108  	if !s.canSurvive(pos, w) {
   109  		w.SetBlock(pos, nil, nil)
   110  		w.AddParticle(pos.Vec3Centre(), particle.BlockBreak{Block: s})
   111  		dropItem(w, item.NewStack(s, s.AdditionalCount+1), pos.Vec3Centre())
   112  		return
   113  	}
   114  
   115  	alive := false
   116  	if liquid, ok := w.Liquid(pos); ok {
   117  		_, alive = liquid.(Water)
   118  	}
   119  	if s.Dead == alive {
   120  		s.Dead = !alive
   121  		w.SetBlock(pos, s, nil)
   122  	}
   123  }
   124  
   125  // HasLiquidDrops ...
   126  func (SeaPickle) HasLiquidDrops() bool {
   127  	return true
   128  }
   129  
   130  // SideClosed ...
   131  func (SeaPickle) SideClosed(cube.Pos, cube.Pos, *world.World) bool {
   132  	return false
   133  }
   134  
   135  // LightEmissionLevel ...
   136  func (s SeaPickle) LightEmissionLevel() uint8 {
   137  	if s.Dead {
   138  		return 0
   139  	}
   140  	return uint8(6 + s.AdditionalCount*3)
   141  }
   142  
   143  // BreakInfo ...
   144  func (s SeaPickle) BreakInfo() BreakInfo {
   145  	return newBreakInfo(0, alwaysHarvestable, nothingEffective, simpleDrops(item.NewStack(s, s.AdditionalCount+1)))
   146  }
   147  
   148  // FlammabilityInfo ...
   149  func (SeaPickle) FlammabilityInfo() FlammabilityInfo {
   150  	return newFlammabilityInfo(15, 100, true)
   151  }
   152  
   153  // SmeltInfo ...
   154  func (SeaPickle) SmeltInfo() item.SmeltInfo {
   155  	return newSmeltInfo(item.NewStack(item.Dye{Colour: item.ColourLime()}, 1), 0.1)
   156  }
   157  
   158  // CompostChance ...
   159  func (SeaPickle) CompostChance() float64 {
   160  	return 0.65
   161  }
   162  
   163  // EncodeItem ...
   164  func (SeaPickle) EncodeItem() (name string, meta int16) {
   165  	return "minecraft:sea_pickle", 0
   166  }
   167  
   168  // EncodeBlock ...
   169  func (s SeaPickle) EncodeBlock() (string, map[string]any) {
   170  	return "minecraft:sea_pickle", map[string]any{"cluster_count": int32(s.AdditionalCount), "dead_bit": s.Dead}
   171  }
   172  
   173  // allSeaPickles ...
   174  func allSeaPickles() (b []world.Block) {
   175  	for i := 0; i <= 3; i++ {
   176  		b = append(b, SeaPickle{AdditionalCount: i})
   177  		b = append(b, SeaPickle{AdditionalCount: i, Dead: true})
   178  	}
   179  	return
   180  }