github.com/df-mc/dragonfly@v0.9.13/server/block/sponge.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  )
    10  
    11  // Sponge is a block that can be used to remove water around itself when placed, turning into a wet sponge in the
    12  // process.
    13  type Sponge struct {
    14  	solid
    15  
    16  	// Wet specifies whether the dry or the wet variant of the block is used.
    17  	Wet bool
    18  }
    19  
    20  // BreakInfo ...
    21  func (s Sponge) BreakInfo() BreakInfo {
    22  	return newBreakInfo(0.6, alwaysHarvestable, nothingEffective, oneOf(s))
    23  }
    24  
    25  // SmeltInfo ...
    26  func (s Sponge) SmeltInfo() item.SmeltInfo {
    27  	if s.Wet {
    28  		return newSmeltInfo(item.NewStack(Sponge{}, 1), 0.15)
    29  	}
    30  	return item.SmeltInfo{}
    31  }
    32  
    33  // EncodeItem ...
    34  func (s Sponge) EncodeItem() (name string, meta int16) {
    35  	if s.Wet {
    36  		meta = 1
    37  	}
    38  	return "minecraft:sponge", meta
    39  }
    40  
    41  // EncodeBlock ...
    42  func (s Sponge) EncodeBlock() (string, map[string]any) {
    43  	if s.Wet {
    44  		return "minecraft:sponge", map[string]any{"sponge_type": "wet"}
    45  	}
    46  	return "minecraft:sponge", map[string]any{"sponge_type": "dry"}
    47  }
    48  
    49  // UseOnBlock places the sponge, absorbs nearby water if it's still dry and flags it as wet if any water has been
    50  // absorbed.
    51  func (s Sponge) UseOnBlock(pos cube.Pos, face cube.Face, _ mgl64.Vec3, w *world.World, user item.User, ctx *item.UseContext) (used bool) {
    52  	var particles = false
    53  	pos, _, used = firstReplaceable(w, pos, face, s)
    54  	if !used {
    55  		return
    56  	}
    57  
    58  	// Check if the Sponge is placed in the Nether and if so, turn it into a normal Sponge instantly.
    59  	if w.Dimension().WaterEvaporates() && s.Wet {
    60  		s.Wet = false
    61  		particles = true
    62  	}
    63  
    64  	place(w, pos, s, user, ctx)
    65  	if particles && placed(ctx) {
    66  		w.AddParticle(pos.Side(cube.FaceUp).Vec3(), particle.Evaporate{})
    67  	}
    68  	return placed(ctx)
    69  }
    70  
    71  // NeighbourUpdateTick checks for nearby water flow. If water could be found and the sponge is dry, it will absorb the
    72  // water and be flagged as wet.
    73  func (s Sponge) NeighbourUpdateTick(pos, _ cube.Pos, w *world.World) {
    74  	// The sponge is dry, so it can absorb nearby water.
    75  	if !s.Wet {
    76  		if s.absorbWater(pos, w) > 0 {
    77  			// Water has been absorbed, so we flag the sponge as wet.
    78  			s.setWet(pos, w)
    79  		}
    80  	}
    81  }
    82  
    83  // setWet flags a sponge as wet. It replaces the block at pos by a wet sponge block and displays a block break
    84  // particle at the sponge's position with an offset of 0.5 on each axis.
    85  func (s Sponge) setWet(pos cube.Pos, w *world.World) {
    86  	s.Wet = true
    87  	w.SetBlock(pos, s, nil)
    88  	w.AddParticle(pos.Vec3Centre(), particle.BlockBreak{Block: Water{Depth: 1}})
    89  }
    90  
    91  // absorbWater replaces water blocks near the sponge by air out to a taxicab geometry of 7 in all directions.
    92  // The maximum for absorbed blocks is 65.
    93  // The returned int specifies the amount of replaced water blocks.
    94  func (s Sponge) absorbWater(pos cube.Pos, w *world.World) int {
    95  	// distanceToSponge binds a world.Pos to its distance from the sponge's position.
    96  	type distanceToSponge struct {
    97  		block    cube.Pos
    98  		distance int32
    99  	}
   100  
   101  	queue := make([]distanceToSponge, 0)
   102  	queue = append(queue, distanceToSponge{pos, 0})
   103  
   104  	// A sponge can only absorb up to 65 water blocks.
   105  	replaced := 0
   106  	for replaced < 65 {
   107  		if len(queue) == 0 {
   108  			break
   109  		}
   110  
   111  		// Pop the next distanceToSponge entry from the queue.
   112  		next := queue[0]
   113  		queue = queue[1:]
   114  
   115  		next.block.Neighbours(func(neighbour cube.Pos) {
   116  			liquid, found := w.Liquid(neighbour)
   117  			if found {
   118  				if _, isWater := liquid.(Water); isWater {
   119  					w.SetLiquid(neighbour, nil)
   120  					replaced++
   121  					if next.distance < 7 {
   122  						queue = append(queue, distanceToSponge{neighbour, next.distance + 1})
   123  					}
   124  				}
   125  			}
   126  		}, w.Range())
   127  	}
   128  
   129  	return replaced
   130  }