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

     1  package block
     2  
     3  import (
     4  	"github.com/df-mc/dragonfly/server/block/cube"
     5  	"github.com/df-mc/dragonfly/server/event"
     6  	"github.com/df-mc/dragonfly/server/world"
     7  	"github.com/df-mc/dragonfly/server/world/sound"
     8  	"math/rand"
     9  	"time"
    10  )
    11  
    12  // Lava is a light-emitting fluid block that causes fire damage.
    13  type Lava struct {
    14  	empty
    15  	replaceable
    16  
    17  	// Still makes the lava not spread whenever it is updated. Still lava cannot be acquired in the game
    18  	// without world editing.
    19  	Still bool
    20  	// Depth is the depth of the water. This is a number from 1-8, where 8 is a source block and 1 is the
    21  	// smallest possible lava block.
    22  	Depth int
    23  	// Falling specifies if the lava is falling. Falling lava will always appear as a source block, but its
    24  	// behaviour differs when it starts spreading.
    25  	Falling bool
    26  }
    27  
    28  // neighboursLavaFlammable returns true if one a block adjacent to the passed position is flammable.
    29  func neighboursLavaFlammable(pos cube.Pos, w *world.World) bool {
    30  	for i := cube.Face(0); i < 6; i++ {
    31  		if flammable, ok := w.Block(pos.Side(i)).(Flammable); ok && flammable.FlammabilityInfo().LavaFlammable {
    32  			return true
    33  		}
    34  	}
    35  	return false
    36  }
    37  
    38  // EntityInside ...
    39  func (l Lava) EntityInside(_ cube.Pos, _ *world.World, e world.Entity) {
    40  	if fallEntity, ok := e.(fallDistanceEntity); ok {
    41  		fallEntity.ResetFallDistance()
    42  	}
    43  	if flammable, ok := e.(flammableEntity); ok {
    44  		if l, ok := e.(livingEntity); ok && !l.AttackImmune() {
    45  			l.Hurt(4, LavaDamageSource{})
    46  		}
    47  		flammable.SetOnFire(15 * time.Second)
    48  	}
    49  }
    50  
    51  // RandomTick ...
    52  func (l Lava) RandomTick(pos cube.Pos, w *world.World, r *rand.Rand) {
    53  	i := r.Intn(3)
    54  	if i > 0 {
    55  		for j := 0; j < i; j++ {
    56  			pos = pos.Add(cube.Pos{r.Intn(3) - 1, 1, r.Intn(3) - 1})
    57  			if _, ok := w.Block(pos).(Air); ok {
    58  				if neighboursLavaFlammable(pos, w) {
    59  					w.SetBlock(pos, Fire{}, nil)
    60  				}
    61  			}
    62  		}
    63  	} else {
    64  		for j := 0; j < 3; j++ {
    65  			pos = pos.Add(cube.Pos{r.Intn(3) - 1, 0, r.Intn(3) - 1})
    66  			if _, ok := w.Block(pos.Side(cube.FaceUp)).(Air); ok {
    67  				if flammable, ok := w.Block(pos).(Flammable); ok && flammable.FlammabilityInfo().LavaFlammable && flammable.FlammabilityInfo().Encouragement > 0 {
    68  					w.SetBlock(pos, Fire{}, nil)
    69  				}
    70  			}
    71  		}
    72  	}
    73  }
    74  
    75  // HasLiquidDrops ...
    76  func (Lava) HasLiquidDrops() bool {
    77  	return false
    78  }
    79  
    80  // LightDiffusionLevel always returns 2.
    81  func (Lava) LightDiffusionLevel() uint8 {
    82  	return 2
    83  }
    84  
    85  // LightEmissionLevel returns 15.
    86  func (Lava) LightEmissionLevel() uint8 {
    87  	return 15
    88  }
    89  
    90  // NeighbourUpdateTick ...
    91  func (l Lava) NeighbourUpdateTick(pos, _ cube.Pos, w *world.World) {
    92  	if !l.Harden(pos, w, nil) {
    93  		w.ScheduleBlockUpdate(pos, w.Dimension().LavaSpreadDuration())
    94  	}
    95  }
    96  
    97  // ScheduledTick ...
    98  func (l Lava) ScheduledTick(pos cube.Pos, w *world.World, _ *rand.Rand) {
    99  	if !l.Harden(pos, w, nil) {
   100  		tickLiquid(l, pos, w)
   101  	}
   102  }
   103  
   104  // LiquidDepth returns the depth of the lava.
   105  func (l Lava) LiquidDepth() int {
   106  	return l.Depth
   107  }
   108  
   109  // SpreadDecay always returns 2.
   110  func (Lava) SpreadDecay() int {
   111  	return 2
   112  }
   113  
   114  // WithDepth returns a new Lava block with the depth passed and falling if set to true.
   115  func (l Lava) WithDepth(depth int, falling bool) world.Liquid {
   116  	l.Depth = depth
   117  	l.Falling = falling
   118  	l.Still = false
   119  	return l
   120  }
   121  
   122  // LiquidFalling checks if the lava is falling.
   123  func (l Lava) LiquidFalling() bool {
   124  	return l.Falling
   125  }
   126  
   127  // BlastResistance always returns 500.
   128  func (Lava) BlastResistance() float64 {
   129  	return 500
   130  }
   131  
   132  // LiquidType returns 10 as a unique identifier for the lava liquid.
   133  func (Lava) LiquidType() string {
   134  	return "lava"
   135  }
   136  
   137  // Harden handles the hardening logic of lava.
   138  func (l Lava) Harden(pos cube.Pos, w *world.World, flownIntoBy *cube.Pos) bool {
   139  	var ok bool
   140  	var water, b world.Block
   141  
   142  	if flownIntoBy == nil {
   143  		var water, b world.Block
   144  		_, soulSoilFound := w.Block(pos.Side(cube.FaceDown)).(SoulSoil)
   145  		pos.Neighbours(func(neighbour cube.Pos) {
   146  			if b != nil || neighbour[1] == pos[1]-1 {
   147  				return
   148  			}
   149  			if _, ok := w.Block(neighbour).(BlueIce); ok {
   150  				if soulSoilFound {
   151  					b = Basalt{}
   152  				}
   153  				return
   154  			}
   155  			if waterBlock, ok := w.Block(neighbour).(Water); ok {
   156  				water = waterBlock
   157  				if l.Depth == 8 && !l.Falling {
   158  					b = Obsidian{}
   159  					return
   160  				}
   161  				b = Cobblestone{}
   162  			}
   163  		}, w.Range())
   164  		if b != nil {
   165  			ctx := event.C()
   166  			if w.Handler().HandleLiquidHarden(ctx, pos, l, water, b); ctx.Cancelled() {
   167  				return false
   168  			}
   169  			w.PlaySound(pos.Vec3Centre(), sound.Fizz{})
   170  			w.SetBlock(pos, b, nil)
   171  			return true
   172  		}
   173  		return false
   174  	}
   175  	water, ok = w.Block(*flownIntoBy).(Water)
   176  	if !ok {
   177  		return false
   178  	}
   179  
   180  	if l.Depth == 8 && !l.Falling {
   181  		b = Obsidian{}
   182  	} else {
   183  		b = Cobblestone{}
   184  	}
   185  	ctx := event.C()
   186  	if w.Handler().HandleLiquidHarden(ctx, pos, l, water, b); ctx.Cancelled() {
   187  		return false
   188  	}
   189  	w.SetBlock(pos, b, nil)
   190  	w.PlaySound(pos.Vec3Centre(), sound.Fizz{})
   191  	return true
   192  }
   193  
   194  // EncodeBlock ...
   195  func (l Lava) EncodeBlock() (name string, properties map[string]any) {
   196  	if l.Depth < 1 || l.Depth > 8 {
   197  		panic("invalid lava depth, must be between 1 and 8")
   198  	}
   199  	v := 8 - l.Depth
   200  	if l.Falling {
   201  		v += 8
   202  	}
   203  	if l.Still {
   204  		return "minecraft:lava", map[string]any{"liquid_depth": int32(v)}
   205  	}
   206  	return "minecraft:flowing_lava", map[string]any{"liquid_depth": int32(v)}
   207  }
   208  
   209  // allLava returns a list of all lava states.
   210  func allLava() (b []world.Block) {
   211  	f := func(still, falling bool) {
   212  		b = append(b, Lava{Still: still, Falling: falling, Depth: 8})
   213  		b = append(b, Lava{Still: still, Falling: falling, Depth: 7})
   214  		b = append(b, Lava{Still: still, Falling: falling, Depth: 6})
   215  		b = append(b, Lava{Still: still, Falling: falling, Depth: 5})
   216  		b = append(b, Lava{Still: still, Falling: falling, Depth: 4})
   217  		b = append(b, Lava{Still: still, Falling: falling, Depth: 3})
   218  		b = append(b, Lava{Still: still, Falling: falling, Depth: 2})
   219  		b = append(b, Lava{Still: still, Falling: falling, Depth: 1})
   220  	}
   221  	f(true, true)
   222  	f(true, false)
   223  	f(false, false)
   224  	f(false, true)
   225  	return
   226  }
   227  
   228  // LavaDamageSource is used for damage caused by being in lava.
   229  type LavaDamageSource struct{}
   230  
   231  func (LavaDamageSource) ReducedByResistance() bool { return true }
   232  func (LavaDamageSource) ReducedByArmour() bool     { return true }
   233  func (LavaDamageSource) Fire() bool                { return true }