|
| 1 | +// |
| 2 | +/* |
| 3 | + * This is an example of high end custom content. |
| 4 | + * |
| 5 | + * This code creates the Debug Stick from 1.13+ in 1.12.2, |
| 6 | + * which allows easily changing the blockstate in-world. |
| 7 | + * A functional difference from the 1.13+ version is that this |
| 8 | + * can also function in survival mode, and has less bugs. |
| 9 | + * |
| 10 | + * A number of comments have been added to help understand it. |
| 11 | + */ |
| 12 | + |
| 13 | +// import the classes that are used in this script |
| 14 | +import com.cleanroommc.groovyscript.compat.content.GroovyItem |
| 15 | +import net.minecraft.block.properties.IProperty |
| 16 | +import net.minecraft.util.EnumActionResult |
| 17 | +import net.minecraft.util.text.ITextComponent |
| 18 | +import net.minecraft.util.text.TextComponentTranslation |
| 19 | +import net.minecraftforge.event.entity.player.PlayerInteractEvent |
| 20 | + |
| 21 | +// a number of static methods have been created as shorthand |
| 22 | + |
| 23 | +/** |
| 24 | + * @return the next blockstate in the series of the focused property, respective of direction |
| 25 | + */ |
| 26 | +static <T extends Comparable<T>> IBlockState cycleState(IBlockState state, IProperty<T> property, boolean invert) { |
| 27 | + state.withProperty(property, getRelative(property.getAllowedValues(), state.getValue(property), invert)) |
| 28 | +} |
| 29 | + |
| 30 | +/** |
| 31 | + * the next or prior type relative to the target |
| 32 | + */ |
| 33 | +static <T> T getRelative(Collection<T> allowedValues, T currentValue, boolean invert) { |
| 34 | + int index = allowedValues.findIndexOf({ it == currentValue }) |
| 35 | + if (index === -1) return allowedValues[0] |
| 36 | + int target = index + (invert ? 1 : -1) |
| 37 | + allowedValues[allowedValues.size() <= target ? 0 : target] |
| 38 | +} |
| 39 | + |
| 40 | +/** |
| 41 | + * get the name of the property for the blockstate |
| 42 | + */ |
| 43 | +static <T extends Comparable<T>> String getPropertyName(IBlockState state, IProperty<T> property) { |
| 44 | + property.getName(state.getValue(property)) |
| 45 | +} |
| 46 | + |
| 47 | +/** |
| 48 | + * send a status message to the player - it appears above the hotbar |
| 49 | + */ |
| 50 | +static void message(EntityPlayer player, ITextComponent message) { |
| 51 | + player.sendStatusMessage(message, true) |
| 52 | +} |
| 53 | + |
| 54 | +/** |
| 55 | + * rotate through the states of the property or through type of property being targeted for the given blockstate interacted with |
| 56 | + */ |
| 57 | +static void handleInteraction(EntityPlayer player, World world, BlockPos pos, boolean shouldCycleState, ItemStack debugStick) { |
| 58 | + def state = world.getBlockState(pos) |
| 59 | + def block = state.getBlock() |
| 60 | + def collection = state.getPropertyKeys() |
| 61 | + def key = block.getRegistryName().toString() |
| 62 | + if (collection.isEmpty()) { |
| 63 | + message(player, new TextComponentTranslation(debugStick.getTranslationKey() + ".empty", key)) |
| 64 | + return |
| 65 | + } |
| 66 | + def data = debugStick.getTagCompound() |
| 67 | + if (data == null) { |
| 68 | + data = nbt() |
| 69 | + debugStick.setTagCompound(data) |
| 70 | + } |
| 71 | + |
| 72 | + def targetBlock = data.getString(key) |
| 73 | + |
| 74 | + def property = state.getProperties().keySet().findResult({ it.getName() == targetBlock ? it : null }) |
| 75 | + |
| 76 | + if (shouldCycleState) { |
| 77 | + if (property == null) { |
| 78 | + property = collection.iterator().next() |
| 79 | + } |
| 80 | + def blockstate = cycleState(state, property, player.isSneaking()) |
| 81 | + world.setBlockState(pos, blockstate, 2 | 8 | 16) |
| 82 | + message(player, new TextComponentTranslation(debugStick.getTranslationKey() + ".update", property.getName(), getPropertyName(blockstate, property))) |
| 83 | + } else { |
| 84 | + property = getRelative(collection, property, player.isSneaking()) |
| 85 | + def newString = property.getName() |
| 86 | + data.setString(key, newString) |
| 87 | + message(player, new TextComponentTranslation(debugStick.getTranslationKey() + ".select", newString, getPropertyName(state, property))) |
| 88 | + } |
| 89 | +} |
| 90 | + |
| 91 | +/** |
| 92 | + * this extends GroovyItem to make it easier add custom effects and register it. |
| 93 | + * any other class could be used instead |
| 94 | + */ |
| 95 | +class ItemDebugStick extends GroovyItem { |
| 96 | + |
| 97 | + /** |
| 98 | + * sets the name of this item - a required part of GroovyItem |
| 99 | + * will check the "groovyscriptdev/models/item/debug_stick.json" file for its texture |
| 100 | + * which has been edited to point to the texture of "minecraft:stick" |
| 101 | + */ |
| 102 | + ItemDebugStick() { |
| 103 | + super('debug_stick') |
| 104 | + } |
| 105 | + |
| 106 | + /** |
| 107 | + * ensure that the debug stick cannot mine blocks in survival |
| 108 | + */ |
| 109 | + float getDestroySpeed(ItemStack stack, IBlockState state) { |
| 110 | + 0.0f |
| 111 | + } |
| 112 | + |
| 113 | + /** |
| 114 | + * you can override methods, this overrides interacting with the item |
| 115 | + */ |
| 116 | + EnumActionResult onItemUseFirst(EntityPlayer player, World world, BlockPos pos, EnumFacing side, float hitX, float hitY, float hitZ, EnumHand hand) { |
| 117 | + if (!world.isRemote) DebugTool.handleInteraction(player, world, pos, true, player.getHeldItem(hand)) |
| 118 | + EnumActionResult.SUCCESS |
| 119 | + } |
| 120 | + |
| 121 | + /** |
| 122 | + * the debug stick should not destroy blocks in creative |
| 123 | + */ |
| 124 | + boolean canDestroyBlockInCreative(World world, BlockPos pos, ItemStack stack, EntityPlayer player) { |
| 125 | + false |
| 126 | + } |
| 127 | +} |
| 128 | + |
| 129 | +// this creates and registers the custom item |
| 130 | +new ItemDebugStick() |
| 131 | + .setCreativeTab(creativeTab('tools')) // sets the tab the debug stick is added to |
| 132 | + // these three methods are unique to GroovyItem |
| 133 | + .setRarity(EnumRarity.EPIC) // sets the color of the name |
| 134 | + .setEnchantedEffect() // makes the item have the shimmer effect as if it was enchanted |
| 135 | + .register() // the same as `content.registerItem(item.getRegistryName().getPath(), item)` |
| 136 | + |
| 137 | + |
| 138 | +// since there isnt a method to check for "left click block" (only "destroy block") in item, we use an event |
| 139 | +// this could also occur in postInit! further, if it was in postInit it could be reloaded. |
| 140 | +event_manager.listen { PlayerInteractEvent.LeftClickBlock event -> |
| 141 | + def player = event.getEntityPlayer() |
| 142 | + if (player.world.isRemote) return |
| 143 | + def stack = player.getHeldItem(event.getHand()) |
| 144 | + if (stack.getItem() instanceof ItemDebugStick) { // could also do `stack in item('groovyscriptdev:debug_stick')` |
| 145 | + // only operate if in creative or if its not immediately after a prior swing |
| 146 | + if (player.isCreative() || player.ticksSinceLastSwing != 1) handleInteraction(player, player.world, event.getPos(), false, stack) |
| 147 | + event.setCanceled(true) // cancel the normal operations |
| 148 | + } |
| 149 | +} |
0 commit comments