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 }