github.com/df-mc/dragonfly@v0.9.13/server/block/break_info.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/item/enchantment"
     7  	"github.com/df-mc/dragonfly/server/world"
     8  	"math"
     9  	"math/rand"
    10  	"time"
    11  )
    12  
    13  // Breakable represents a block that may be broken by a player in survival mode. Blocks not include are blocks
    14  // such as bedrock.
    15  type Breakable interface {
    16  	// BreakInfo returns information of the block related to the breaking of it.
    17  	BreakInfo() BreakInfo
    18  }
    19  
    20  // BreakDuration returns the base duration that breaking the block passed takes when being broken using the
    21  // item passed.
    22  func BreakDuration(b world.Block, i item.Stack) time.Duration {
    23  	breakable, ok := b.(Breakable)
    24  	if !ok {
    25  		return math.MaxInt64
    26  	}
    27  	t, ok := i.Item().(item.Tool)
    28  	if !ok {
    29  		t = item.ToolNone{}
    30  	}
    31  	info := breakable.BreakInfo()
    32  
    33  	breakTime := info.Hardness * 5
    34  	if info.Harvestable(t) {
    35  		breakTime = info.Hardness * 1.5
    36  	}
    37  	if info.Effective(t) {
    38  		eff := t.BaseMiningEfficiency(b)
    39  		if e, ok := i.Enchantment(enchantment.Efficiency{}); ok {
    40  			breakTime += (enchantment.Efficiency{}).Addend(e.Level())
    41  		}
    42  		breakTime /= eff
    43  	}
    44  	// TODO: Account for haste etc here.
    45  	timeInTicksAccurate := math.Round(breakTime/0.05) * 0.05
    46  
    47  	return (time.Duration(math.Round(timeInTicksAccurate*20)) * time.Second) / 20
    48  }
    49  
    50  // BreaksInstantly checks if the block passed can be broken instantly using the item stack passed to break
    51  // it.
    52  func BreaksInstantly(b world.Block, i item.Stack) bool {
    53  	breakable, ok := b.(Breakable)
    54  	if !ok {
    55  		return false
    56  	}
    57  	hardness := breakable.BreakInfo().Hardness
    58  	if hardness == 0 {
    59  		return true
    60  	}
    61  	t, ok := i.Item().(item.Tool)
    62  	if !ok || !breakable.BreakInfo().Effective(t) {
    63  		return false
    64  	}
    65  
    66  	// TODO: Account for haste etc here.
    67  	efficiencyVal := 0.0
    68  	if e, ok := i.Enchantment(enchantment.Efficiency{}); ok {
    69  		efficiencyVal += (enchantment.Efficiency{}).Addend(e.Level())
    70  	}
    71  	hasteVal := 0.0
    72  	return (t.BaseMiningEfficiency(b)+efficiencyVal)*hasteVal >= hardness*30
    73  }
    74  
    75  // BreakInfo is a struct returned by every block. It holds information on block breaking related data, such as
    76  // the tool type and tier required to break it.
    77  type BreakInfo struct {
    78  	// Hardness is the hardness of the block, which influences the speed with which the block may be mined.
    79  	Hardness float64
    80  	// Harvestable is a function called to check if the block is harvestable using the tool passed. If the
    81  	// item used to break the block is not a tool, a tool.ToolNone is passed.
    82  	Harvestable func(t item.Tool) bool
    83  	// Effective is a function called to check if the block can be mined more effectively with the tool passed
    84  	// than with an empty hand.
    85  	Effective func(t item.Tool) bool
    86  	// Drops is a function called to get the drops of the block if it is broken using the item passed.
    87  	Drops func(t item.Tool, enchantments []item.Enchantment) []item.Stack
    88  	// BreakHandler is called after the block has broken.
    89  	BreakHandler func(pos cube.Pos, w *world.World, u item.User)
    90  	// XPDrops is the range of XP a block can drop when broken.
    91  	XPDrops XPDropRange
    92  	// BlastResistance is the blast resistance of the block, which influences the block's ability to withstand an
    93  	// explosive blast.
    94  	BlastResistance float64
    95  }
    96  
    97  // newBreakInfo creates a BreakInfo struct with the properties passed. The XPDrops field is 0 by default. The blast
    98  // resistance is set to the block's hardness*5 by default.
    99  func newBreakInfo(hardness float64, harvestable func(item.Tool) bool, effective func(item.Tool) bool, drops func(item.Tool, []item.Enchantment) []item.Stack) BreakInfo {
   100  	return BreakInfo{
   101  		Hardness:        hardness,
   102  		BlastResistance: hardness * 5,
   103  		Harvestable:     harvestable,
   104  		Effective:       effective,
   105  		Drops:           drops,
   106  	}
   107  }
   108  
   109  // withXPDropRange sets the XPDropRange field of the BreakInfo struct to the passed value.
   110  func (b BreakInfo) withXPDropRange(min, max int) BreakInfo {
   111  	b.XPDrops = XPDropRange{min, max}
   112  	return b
   113  }
   114  
   115  // withBlastResistance sets the BlastResistance field of the BreakInfo struct to the passed value.
   116  func (b BreakInfo) withBlastResistance(res float64) BreakInfo {
   117  	b.BlastResistance = res
   118  	return b
   119  }
   120  
   121  // withBreakHandler sets the BreakHandler field of the BreakInfo struct to the passed value.
   122  func (b BreakInfo) withBreakHandler(handler func(pos cube.Pos, w *world.World, u item.User)) BreakInfo {
   123  	b.BreakHandler = handler
   124  	return b
   125  }
   126  
   127  // XPDropRange holds the min & max XP drop amounts of blocks.
   128  type XPDropRange [2]int
   129  
   130  // RandomValue returns a random XP value that falls within the drop range.
   131  func (r XPDropRange) RandomValue() int {
   132  	diff := r[1] - r[0]
   133  	// Add one because it's a [r[0], r[1]] interval.
   134  	return rand.Intn(diff+1) + r[0]
   135  }
   136  
   137  // pickaxeEffective is a convenience function for blocks that are effectively mined with a pickaxe.
   138  var pickaxeEffective = func(t item.Tool) bool {
   139  	return t.ToolType() == item.TypePickaxe
   140  }
   141  
   142  // axeEffective is a convenience function for blocks that are effectively mined with an axe.
   143  var axeEffective = func(t item.Tool) bool {
   144  	return t.ToolType() == item.TypeAxe
   145  }
   146  
   147  // shearsEffective is a convenience function for blocks that are effectively mined with shears.
   148  var shearsEffective = func(t item.Tool) bool {
   149  	return t.ToolType() == item.TypeShears
   150  }
   151  
   152  // shovelEffective is a convenience function for blocks that are effectively mined with a shovel.
   153  var shovelEffective = func(t item.Tool) bool {
   154  	return t.ToolType() == item.TypeShovel
   155  }
   156  
   157  // hoeEffective is a convenience function for blocks that are effectively mined with a hoe.
   158  var hoeEffective = func(t item.Tool) bool {
   159  	return t.ToolType() == item.TypeHoe
   160  }
   161  
   162  // nothingEffective is a convenience function for blocks that cannot be mined efficiently with any tool.
   163  var nothingEffective = func(item.Tool) bool {
   164  	return false
   165  }
   166  
   167  // alwaysHarvestable is a convenience function for blocks that are harvestable using any item.
   168  var alwaysHarvestable = func(t item.Tool) bool {
   169  	return true
   170  }
   171  
   172  // neverHarvestable is a convenience function for blocks that are not harvestable by any item.
   173  var neverHarvestable = func(t item.Tool) bool {
   174  	return false
   175  }
   176  
   177  // pickaxeHarvestable is a convenience function for blocks that are harvestable using any kind of pickaxe.
   178  var pickaxeHarvestable = pickaxeEffective
   179  
   180  // simpleDrops returns a drops function that returns the items passed.
   181  func simpleDrops(s ...item.Stack) func(item.Tool, []item.Enchantment) []item.Stack {
   182  	return func(item.Tool, []item.Enchantment) []item.Stack {
   183  		return s
   184  	}
   185  }
   186  
   187  // oneOf returns a drops function that returns one of each of the item types passed.
   188  func oneOf(i ...world.Item) func(item.Tool, []item.Enchantment) []item.Stack {
   189  	return func(item.Tool, []item.Enchantment) []item.Stack {
   190  		var s []item.Stack
   191  		for _, it := range i {
   192  			s = append(s, item.NewStack(it, 1))
   193  		}
   194  		return s
   195  	}
   196  }
   197  
   198  // hasSilkTouch checks if an item has the silk touch enchantment.
   199  func hasSilkTouch(enchantments []item.Enchantment) bool {
   200  	for _, enchant := range enchantments {
   201  		if _, ok := enchant.Type().(enchantment.SilkTouch); ok {
   202  			return true
   203  		}
   204  	}
   205  	return false
   206  }
   207  
   208  // silkTouchOneOf returns a drop function that returns 1x of the silk touch drop when silk touch exists, or 1x of the
   209  // normal drop when it does not.
   210  func silkTouchOneOf(normal, silkTouch world.Item) func(item.Tool, []item.Enchantment) []item.Stack {
   211  	return func(t item.Tool, enchantments []item.Enchantment) []item.Stack {
   212  		if hasSilkTouch(enchantments) {
   213  			return []item.Stack{item.NewStack(silkTouch, 1)}
   214  		}
   215  		return []item.Stack{item.NewStack(normal, 1)}
   216  	}
   217  }
   218  
   219  // silkTouchDrop returns a drop function that returns the silk touch drop when silk touch exists, or the
   220  // normal drop when it does not.
   221  func silkTouchDrop(normal, silkTouch item.Stack) func(item.Tool, []item.Enchantment) []item.Stack {
   222  	return func(t item.Tool, enchantments []item.Enchantment) []item.Stack {
   223  		if hasSilkTouch(enchantments) {
   224  			return []item.Stack{silkTouch}
   225  		}
   226  		return []item.Stack{normal}
   227  	}
   228  }
   229  
   230  // silkTouchOnlyDrop returns a drop function that returns the drop when silk touch exists.
   231  func silkTouchOnlyDrop(it world.Item) func(t item.Tool, enchantments []item.Enchantment) []item.Stack {
   232  	return func(t item.Tool, enchantments []item.Enchantment) []item.Stack {
   233  		if hasSilkTouch(enchantments) {
   234  			return []item.Stack{item.NewStack(it, 1)}
   235  		}
   236  		return nil
   237  	}
   238  }