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

     1  package block
     2  
     3  import (
     4  	"github.com/df-mc/dragonfly/server/block/cube"
     5  	"github.com/df-mc/dragonfly/server/internal/nbtconv"
     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  	"math/rand"
    11  	"time"
    12  )
    13  
    14  // BlastFurnace is a block that smelts ores, raw metals, iron and gold armor and tools, similar to a furnace, but at
    15  // twice the speed. It also serves as an armorer's job site block.
    16  // The empty value of BlastFurnace is not valid. It must be created using block.NewBlastFurnace(cube.Face).
    17  type BlastFurnace struct {
    18  	solid
    19  	bassDrum
    20  	*smelter
    21  
    22  	// Facing is the direction the blast furnace is facing.
    23  	Facing cube.Face
    24  	// Lit is true if the blast furnace is lit.
    25  	Lit bool
    26  }
    27  
    28  // NewBlastFurnace creates a new initialised blast furnace. The smelter is properly initialised.
    29  func NewBlastFurnace(face cube.Face) BlastFurnace {
    30  	return BlastFurnace{
    31  		Facing:  face,
    32  		smelter: newSmelter(),
    33  	}
    34  }
    35  
    36  // Tick is called to check if the blast furnace should update and start or stop smelting.
    37  func (b BlastFurnace) Tick(_ int64, pos cube.Pos, w *world.World) {
    38  	if b.Lit && rand.Float64() <= 0.016 { // Every three or so seconds.
    39  		w.PlaySound(pos.Vec3Centre(), sound.BlastFurnaceCrackle{})
    40  	}
    41  	if lit := b.smelter.tickSmelting(time.Second*5, time.Millisecond*200, b.Lit, func(i item.SmeltInfo) bool {
    42  		return i.Ores
    43  	}); b.Lit != lit {
    44  		b.Lit = lit
    45  		w.SetBlock(pos, b, nil)
    46  	}
    47  }
    48  
    49  // EncodeItem ...
    50  func (b BlastFurnace) EncodeItem() (name string, meta int16) {
    51  	return "minecraft:blast_furnace", 0
    52  }
    53  
    54  // EncodeBlock ...
    55  func (b BlastFurnace) EncodeBlock() (name string, properties map[string]interface{}) {
    56  	if b.Lit {
    57  		return "minecraft:lit_blast_furnace", map[string]interface{}{"minecraft:cardinal_direction": b.Facing.String()}
    58  	}
    59  	return "minecraft:blast_furnace", map[string]interface{}{"minecraft:cardinal_direction": b.Facing.String()}
    60  }
    61  
    62  // UseOnBlock ...
    63  func (b BlastFurnace) UseOnBlock(pos cube.Pos, face cube.Face, _ mgl64.Vec3, w *world.World, user item.User, ctx *item.UseContext) bool {
    64  	pos, _, used := firstReplaceable(w, pos, face, b)
    65  	if !used {
    66  		return false
    67  	}
    68  
    69  	place(w, pos, NewBlastFurnace(user.Rotation().Direction().Face().Opposite()), user, ctx)
    70  	return placed(ctx)
    71  }
    72  
    73  // BreakInfo ...
    74  func (b BlastFurnace) BreakInfo() BreakInfo {
    75  	xp := b.Experience()
    76  	return newBreakInfo(3.5, alwaysHarvestable, pickaxeEffective, oneOf(b)).withXPDropRange(xp, xp)
    77  }
    78  
    79  // Activate ...
    80  func (b BlastFurnace) Activate(pos cube.Pos, _ cube.Face, _ *world.World, u item.User, _ *item.UseContext) bool {
    81  	if opener, ok := u.(ContainerOpener); ok {
    82  		opener.OpenBlockContainer(pos)
    83  		return true
    84  	}
    85  	return false
    86  }
    87  
    88  // EncodeNBT ...
    89  func (b BlastFurnace) EncodeNBT() map[string]interface{} {
    90  	if b.smelter == nil {
    91  		//noinspection GoAssignmentToReceiver
    92  		b = NewBlastFurnace(b.Facing)
    93  	}
    94  	remaining, maximum, cook := b.Durations()
    95  	return map[string]interface{}{
    96  		"BurnTime":     int16(remaining.Milliseconds() / 50),
    97  		"CookTime":     int16(cook.Milliseconds() / 50),
    98  		"BurnDuration": int16(maximum.Milliseconds() / 50),
    99  		"StoredXPInt":  int16(b.Experience()),
   100  		"Items":        nbtconv.InvToNBT(b.Inventory()),
   101  		"id":           "BlastFurnace",
   102  	}
   103  }
   104  
   105  // DecodeNBT ...
   106  func (b BlastFurnace) DecodeNBT(data map[string]interface{}) interface{} {
   107  	remaining := nbtconv.TickDuration[int16](data, "BurnTime")
   108  	maximum := nbtconv.TickDuration[int16](data, "BurnDuration")
   109  	cook := nbtconv.TickDuration[int16](data, "CookTime")
   110  
   111  	xp := int(nbtconv.Int16(data, "StoredXPInt"))
   112  	lit := b.Lit
   113  
   114  	//noinspection GoAssignmentToReceiver
   115  	b = NewBlastFurnace(b.Facing)
   116  	b.Lit = lit
   117  	b.setExperience(xp)
   118  	b.setDurations(remaining, maximum, cook)
   119  	nbtconv.InvFromNBT(b.Inventory(), nbtconv.Slice(data, "Items"))
   120  	return b
   121  }
   122  
   123  // allBlastFurnaces ...
   124  func allBlastFurnaces() (furnaces []world.Block) {
   125  	for _, face := range cube.HorizontalFaces() {
   126  		furnaces = append(furnaces, BlastFurnace{Facing: face})
   127  		furnaces = append(furnaces, BlastFurnace{Facing: face, Lit: true})
   128  	}
   129  	return
   130  }