Wednesday, 7 January 2015

Mining Blocks with Tools

This section gives an overview of how vanilla uses tools to mine blocks and harvest items when the block breaks.

To skip straight to some sample code which can help you test out your custom Blocks or Items:
https://github.com/TheGreyGhost/MinecraftByExample  (see Example 13)

Key concepts

  • Blocks have an associated hardness which affects the mining speed.  Typical hardness is 0.5 for dirt up to 2000 for obsidian.  Some blocks are invulnerable (unbreakable) such as command block or BlockBarrier.  The block hardness is usually set when constructing the block, using .setHardness() or .setUnbreakable().
  • Items can be tagged with one or more ToolClasses such as “axe”, “pickaxe”, “shovel”.  Vanilla items have at most one ToolClass, but custom items subclassed from ItemTool can have multiple ToolClasses.  You can set item ToolClasses using Item.setHarvestLevel(ToolClass).
  • Each Block is particularly susceptible to one of the ToolClasses (eg wood is susceptible to “axe”, dirt susceptible to “shovel”).  When mined using an ItemTool which has a ToolClass that matches the Block, the digging speed is multiplied by the “EfficiencyOnProperMaterial” of the tool.  For example, chopping wood with an iron axe has a 6.0x factor speedup compared with using a pickaxe.  The ToolClass of the Block is set using Block.setHarvestLevel(ToolClass).  The EfficiencyOnProperMaterial is determined by specifying the Item.ToolMaterial in the ItemTool constructor.
  • Each Block also has differing resistance to tools depending on what the tool is made of, i.e. wood, stone, gold, iron, or diamond.  This resistance of the Block is called its harvest level, which ranges from 0 for wood to 3 for diamond. If the harvest level of the tool is less than the harvest level of the block, it mines much more slowly and also doesn’t harvest any items when the block finally breaks.  The harvest level of the Item is set using Item.setHarvestLevel(ToolClass, level), and likewise the harvest level of the Block is set using Block.setHarvestLevel(ToolClass, level).  Different IBlockState can be assigned different harvest levels if desired.
  • In addition to ToolClass, ItemTools also have a set of blocks that they are “effective” on (i.e. can mine very quickly).  These “EFFECTIVE_ON“ blocks are specified in the ItemTool constructor as a Set.
  • When broken, a block is "harvested" if the appropriate tool is used.  Harvesting spawns a random number of items specific to the block type.  If a tool with a "silk harvest" enchantment is used, it will change the type of items dropped - for example a stone block will yield a stone block items instead of cobblestone item.
  • The logic for mining blocks is rather complicated and can be “intercepted” in many different ways.  See the links below for further details.
  • If possible, give your Item or Block the appropriate ToolClasses, Materials, EFFECTIVE_ON Block Set etc to produce the desired mining speed and harvesting behaviour.  Or even easier, if you want your item to act the same as a vanilla item (say ItemPickaxe), make your item a subclass of the ItemPickaxe.
  • Otherwise - if you are adding a custom Block, override the appropriate Block methods.  If you are adding a custom Item, override the appropriate Item methods.  Avoid overriding the “high level” Block or Item methods if possible; use the lower-level methods instead.  As a guide - if the vanilla base method is not overridden by any vanilla classes, you probably shouldn’t either unless you have good reason.
  • If you want to modify the behaviour of vanilla items on vanilla blocks, use Events.

The various relevant Events, Block methods, and Item methods are listed below.  The ones that are most likely to be useful are highlighted in bold.  The others might be useful in specific circumstances but not often.

Items

Item.setHarvestLevel(ToolClass, level) - first choice - give your tool one or more ToolClasses equivalent to existing tool eg "pickaxe", "axe".
ItemTool constructor -
  •  EFFECTIVE_ON set of blocks - to add extra "special case" blocks that your item is effective on which don't match the Item's ToolClass(es).
  • ToolMaterial, which affects the maximum dig speed and the tool durability.
Item.getStrVsBlock() - add further "special cases" that ToolClass and ItemTool constructor don't cover.
Item.getDigSpeed() - metadata / damage sensitive version of getStrVsBlock()
Item.onBlockStartBreak() - can be used to abort block breaking before it is destroyed
Item.onBlockDestroyed() - used to damage the item once the block is destroyed

In Adventure mode, the "CanDestroy" NBT tag in the tool's itemstack is used to determine which blocks it can destroy.

Blocks

First choice for most cases:
  • Block.setHardness() - set the base hardness of the block
  • Block.setHarvestLevel(ToolClass, level) - specify the type of tool that is most effective against this block, and what the tool needs to be made of (wood, stone, iron, diamond) in order to drop items when harvested.  There are two versions, one  for normal blocks, and one which depends on the block state (metadata) - for example a tree that is made of different types of wood with different toughness. 
  • Block.onBlockHarvested() - this method mostly does nothing but some add extra effects- eg TNT generates an explosion.
  • Block.getDrops() - get a list of items that might drop when the block is harvested normally.  In simple cases, you can just override quantityDropped() and getItemDropped() instead. 

Of some use for special cases:
  • Block.onBlockDestroyedByPlayer() - used to drop extra items (eg skulls) or destroy other parts of a multiblock (eg bed) 
  • Block.harvestBlock() - can be used to override default harvesting behaviour (for example - shearing a tree to increase the sapling drop rate and trigger a special achievement)
  • Block.createStackedBlock() - set the type of drop for silk enchantment harvesting.
  • Block.dropBlockAsItemWithChance() - customise the random dropping - for example ignoring the effect of fortune enchantment when harvesting wheat.

These remaining block methods are used by vanilla but never overridden.
  • Block.getBlockHardness()
  • Block.isToolEffective()
  • Block.getHarvestTool()
  • Block.getPlayerRelativeBlockHardness()
  • Block.canHarvestBlock()
  • Block.removedByPlayer()
  • Block.canSilkHarvest()
  • Block.dropBlockAsItem()

Events

  • PlayerInteractEvent(LEFT_CLICK_BLOCK) - can be used to cancel digging before it starts.
  • PlayerEvent.BreakSpeed - change the digging speed (block damage per tick)
  • PlayerEvent.HarvestCheck - can be used to set harvesting = true for block/item combinations which aren't otherwised covered by the ToolClass.
  • BlockEvent.BreakEvent - called just before block destruction - if cancelled, abort without destroying the block.
  • BlockEvent.HarvestDropsEvent - modify the list of item that might be dropped when the block is harvested.

Vanilla code that may be of interest 

ItemInWorldManager (server):
  onBlockClicked, updateBlockRemoving, blockRemoving, tryHarvestBlock
PlayerControllerMP (client):
  clickMouse and a couple of obfuscated methods, currently func_180511_b and func_180512_c
ForgeHooks:
  blockStrength, canToolHarvestBlock, isToolEffective, canHarvestBlock

Further links

~Mining Blocks With Tools
    Logic flow of vanilla block mining code
    Calculating rate of damage when mining

For some sample code which can help you test out your custom Blocks or Items:
https://github.com/TheGreyGhost/MinecraftByExample  (see Example 13)




No comments:

Post a Comment