├── .gitignore ├── src └── com │ └── thebombzen │ └── mods │ ├── thebombzenapi │ └── autoswitch │ ├── configuration │ ├── ConfigGuiFactory.java │ ├── EntityWeaponPair.java │ ├── ToolSelectionMode.java │ ├── ConfigScreen.java │ ├── BlockToolPair.java │ ├── SingleEntityIdentifier.java │ ├── EntityIdentifier.java │ ├── SingleValueIdentifier.java │ ├── BlockItemIdentifier.java │ ├── SingleBlockItemIdentifier.java │ ├── ValueSet.java │ └── Configuration.java │ ├── Constants.java │ ├── NotSoRandom.java │ ├── installer │ └── ASInstallerFrame.java │ ├── Tests.java │ └── AutoSwitch.java ├── .gitmodules ├── resources ├── mcmod.info └── AutoSwitch_Overrides.txt ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore anything built 2 | build 3 | 4 | # And also jarfiles 5 | *.jar 6 | 7 | -------------------------------------------------------------------------------- /src/com/thebombzen/mods/thebombzenapi: -------------------------------------------------------------------------------- 1 | ../../../../ThebombzenAPI/src/com/thebombzen/mods/thebombzenapi -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "ThebombzenAPI"] 2 | path = ThebombzenAPI 3 | url = https://github.com/thebombzen/ThebombzenAPI/ 4 | -------------------------------------------------------------------------------- /src/com/thebombzen/mods/autoswitch/configuration/ConfigGuiFactory.java: -------------------------------------------------------------------------------- 1 | package com.thebombzen.mods.autoswitch.configuration; 2 | 3 | import com.thebombzen.mods.thebombzenapi.client.ThebombzenAPIConfigGuiFactory; 4 | 5 | public class ConfigGuiFactory extends ThebombzenAPIConfigGuiFactory { 6 | public ConfigGuiFactory(){ 7 | super(ConfigScreen.class); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /resources/mcmod.info: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "modid": "autoswitch", 4 | "name": "AutoSwitch", 5 | "description": "AutoSwitch automatically switches your tools and weapons when you dig or attack. It has a very advanced algorithm, taking into account not only dig speed, but harvestability, enchantments, damageablity, etc. You can easily configure its options with the config screen (look to the lower left).", 6 | "version": "5.6.2", 7 | "credits": "Thebombzen", 8 | "url": "https://thebombzen.com/AutoSwitch/thread/" 9 | } 10 | ] 11 | -------------------------------------------------------------------------------- /src/com/thebombzen/mods/autoswitch/Constants.java: -------------------------------------------------------------------------------- 1 | package com.thebombzen.mods.autoswitch; 2 | 3 | public interface Constants { 4 | 5 | /** 6 | * Represents the current version of AutoSwitch. 7 | */ 8 | public static final String VERSION = "5.6.2"; 9 | 10 | /** 11 | * Represents the human-readable versions of Minecraft this was built for. 12 | */ 13 | public static final String SUPPORTED_MC_VERSIONS = "1.9.4 to 1.12.2"; 14 | 15 | /** 16 | * Represents the versions of Minecraft this should be installed in. 17 | */ 18 | public static final String[] INSTALL_MC_VERSIONS = {"1.12.2", "1.12.1", "1.11.2", "1.10.2", "1.9.4"}; 19 | } 20 | -------------------------------------------------------------------------------- /src/com/thebombzen/mods/autoswitch/configuration/EntityWeaponPair.java: -------------------------------------------------------------------------------- 1 | package com.thebombzen.mods.autoswitch.configuration; 2 | 3 | public class EntityWeaponPair { 4 | 5 | private EntityIdentifier entity; 6 | private BlockItemIdentifier weapon; 7 | 8 | public EntityWeaponPair(EntityIdentifier entity, BlockItemIdentifier weapon) { 9 | this.entity = entity; 10 | this.weapon = weapon; 11 | } 12 | 13 | public EntityIdentifier getEntity() { 14 | return entity; 15 | } 16 | 17 | public BlockItemIdentifier getWeapon() { 18 | return weapon; 19 | } 20 | 21 | public void setBlock(EntityIdentifier entity) { 22 | this.entity = entity; 23 | } 24 | 25 | public void setTool(BlockItemIdentifier weapon) { 26 | this.weapon = weapon; 27 | } 28 | 29 | @Override 30 | public String toString() { 31 | return entity + ", " + weapon; 32 | } 33 | 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/com/thebombzen/mods/autoswitch/configuration/ToolSelectionMode.java: -------------------------------------------------------------------------------- 1 | package com.thebombzen.mods.autoswitch.configuration; 2 | 3 | public enum ToolSelectionMode { 4 | FAST_STANDARD("Fast Standard"), 5 | SLOW_STANDARD("Slow Standard"), 6 | FAST_NONSTANDARD("Fast Nonstandard"); 7 | 8 | public static ToolSelectionMode parse(String info){ 9 | String line = info.replaceAll("\\s", "").replace("_", "").toLowerCase(); 10 | if (line.equals("faststandard")){ 11 | return FAST_STANDARD; 12 | } else if (line.equals("slowstandard")){ 13 | return SLOW_STANDARD; 14 | } else if (line.equals("fastnonstandard")){ 15 | return FAST_NONSTANDARD; 16 | } else { 17 | return null; 18 | } 19 | } 20 | 21 | private String name; 22 | 23 | private ToolSelectionMode(String name){ 24 | this.name = name; 25 | } 26 | 27 | public boolean isFast(){ 28 | return this != SLOW_STANDARD; 29 | } 30 | 31 | public boolean isStandard(){ 32 | return this != FAST_NONSTANDARD; 33 | } 34 | 35 | @Override 36 | public String toString(){ 37 | return name; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/com/thebombzen/mods/autoswitch/NotSoRandom.java: -------------------------------------------------------------------------------- 1 | package com.thebombzen.mods.autoswitch; 2 | 3 | import java.util.Random; 4 | 5 | /** 6 | * NotSoRandom provides an implementation of Random that can be used to 7 | * obtain determinate results when calculating random occurrences. 8 | * E.g. insert it into the World object, call a random function, then replace. 9 | * @author thebombzen 10 | */ 11 | public class NotSoRandom extends Random { 12 | 13 | private static final long serialVersionUID = 7668644027932430864L; 14 | 15 | private boolean useZero; 16 | 17 | public NotSoRandom(boolean useZero) { 18 | this.useZero = useZero; 19 | } 20 | 21 | @Override 22 | public double nextDouble() { 23 | if (useZero){ 24 | return 0.0D; 25 | } else { 26 | return 1.0D; 27 | } 28 | } 29 | 30 | @Override 31 | public float nextFloat() { 32 | if (useZero) { 33 | return 0.0F; 34 | } else { 35 | return 1.0F; 36 | } 37 | } 38 | 39 | @Override 40 | public double nextGaussian(){ 41 | return nextDouble(); 42 | } 43 | 44 | @Override 45 | public int nextInt(int n) { 46 | if (useZero) { 47 | return 0; 48 | } else { 49 | return n - 1; 50 | } 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Version 5.2.3 and onward is licensed under the MIT License. 2 | For older license versions, see the git history. 3 | 4 | The MIT License (MIT) 5 | Copyright (c) 2011-2017 Leo Izen (thebombzen) 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 8 | 9 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 12 | 13 | -------------------------------------------------------------------------------- /src/com/thebombzen/mods/autoswitch/configuration/ConfigScreen.java: -------------------------------------------------------------------------------- 1 | package com.thebombzen.mods.autoswitch.configuration; 2 | 3 | import java.awt.Desktop; 4 | import java.io.IOException; 5 | 6 | import com.thebombzen.mods.autoswitch.AutoSwitch; 7 | import com.thebombzen.mods.thebombzenapi.client.ThebombzenAPIConfigScreen; 8 | 9 | import net.minecraft.client.gui.GuiButton; 10 | import net.minecraft.client.gui.GuiScreen; 11 | import net.minecraftforge.fml.relauncher.Side; 12 | import net.minecraftforge.fml.relauncher.SideOnly; 13 | 14 | @SideOnly(Side.CLIENT) 15 | public class ConfigScreen extends ThebombzenAPIConfigScreen { 16 | 17 | public ConfigScreen(GuiScreen parentScreen) { 18 | super(AutoSwitch.instance, parentScreen, AutoSwitch.instance.getConfiguration()); 19 | } 20 | 21 | @Override 22 | protected void actionPerformed(GuiButton button) { 23 | super.actionPerformed(button); 24 | // field_146127_k == id 25 | if (button.id == 4911) { 26 | try { 27 | try { 28 | Desktop.getDesktop().edit(((Configuration)config).getExtraConfigFile()); 29 | } catch (UnsupportedOperationException e){ 30 | Desktop.getDesktop().open(((Configuration)config).getExtraConfigFile()); 31 | } 32 | } catch (IOException e) { 33 | mod.throwException("Unable to open file!", e, false); 34 | } 35 | } 36 | } 37 | 38 | @Override 39 | public void initGui() { 40 | super.initGui(); 41 | this.buttonList.add(new GuiButton(4911, this.width / 2 - 100, 42 | this.height / 6 + 140, 200, 20, 43 | "Open AutoSwitch Overrides File...")); 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/com/thebombzen/mods/autoswitch/configuration/BlockToolPair.java: -------------------------------------------------------------------------------- 1 | package com.thebombzen.mods.autoswitch.configuration; 2 | 3 | 4 | /** 5 | * This is a convenience class for creating pairings between blocks and tools. 6 | * The method names are self-explanatory. 7 | * @author thebombzen 8 | */ 9 | public class BlockToolPair { 10 | 11 | private BlockItemIdentifier block; 12 | private BlockItemIdentifier tool; 13 | 14 | public BlockToolPair(BlockItemIdentifier block, BlockItemIdentifier tool) { 15 | this.block = block; 16 | this.tool = tool; 17 | } 18 | 19 | @Override 20 | public boolean equals(Object obj) { 21 | if (this == obj) 22 | return true; 23 | if (obj == null) 24 | return false; 25 | if (getClass() != obj.getClass()) 26 | return false; 27 | BlockToolPair other = (BlockToolPair) obj; 28 | if (block == null) { 29 | if (other.block != null) 30 | return false; 31 | } else if (!block.equals(other.block)) 32 | return false; 33 | if (tool == null) { 34 | if (other.tool != null) 35 | return false; 36 | } else if (!tool.equals(other.tool)) 37 | return false; 38 | return true; 39 | } 40 | 41 | public BlockItemIdentifier getBlock() { 42 | return block; 43 | } 44 | 45 | public BlockItemIdentifier getTool() { 46 | return tool; 47 | } 48 | 49 | @Override 50 | public int hashCode() { 51 | final int prime = 31; 52 | int result = 1; 53 | result = prime * result + ((block == null) ? 0 : block.hashCode()); 54 | result = prime * result + ((tool == null) ? 0 : tool.hashCode()); 55 | return result; 56 | } 57 | 58 | public void setBlock(BlockItemIdentifier block) { 59 | this.block = block; 60 | } 61 | 62 | public void setTool(BlockItemIdentifier tool) { 63 | this.tool = tool; 64 | } 65 | 66 | @Override 67 | public String toString() { 68 | return block + ", " + tool; 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AutoSwitch # 2 | 3 | AutoSwitch is a very advanced mod that automatically switches your tools and weapons. 4 | 5 | See [http://is.gd/ThebombzensMods#AutoSwitch](http://is.gd/ThebombzensMods#AutoSwitch) for details. 6 | 7 | Note: If you want to contribute, feel free! Just send me a pull request and I'll review it before adding it. 8 | 9 | ## Reporting Bugs ## 10 | You can report bugs on the bug tracker at Github. 11 | 12 | If your bug report is about incorrect behavior or a crash, remember to provide a link to a debug log. Do this by enabling debug logging in the config screen. Then, trigger the bug (preferably in creative mode, set "Use in Creative" to ON). Then send me the file .minecraft → mods → AutoSwitch → DEBUG.txt by putting it on a paste site like [https://gist.github.com/](https://gist.github.com/), [http://pastebin.com/](http://pastebin.com/), or [https://0x0.st/](https://0x0.st/). Remember to disable debug when you're done. 13 | 14 | ## Compiling ## 15 | 16 | First, you need to clone [ThebombzenAPI](https://github.com/thebombzen/ThebombzenAPI) to the same directory that you cloned AutoSwitch. i.e. you should see ThebombzenAPI/ and AutoSwitch/ in the same directory. 17 | 18 | Then navigate to AutoSwitch and run: 19 | 20 | $ ./build.sh 21 | 22 | This will create the directory "build" which and all build supplies inside of it, and should create a finished AutoSwitch jar file upon completion. 23 | 24 | On Windows? Sorry, you're on your own. I don't know how to write CMD Batch files. 25 | 26 | ## Eclipse ## 27 | 28 | Once you've run the buildscript at least once, you can go to Eclipse and select File -> Import -> Existing Projects to Workspace, and select AutoSwitch as the root project directory. If you have the Git plugin it should recognize AutoSwitch as a git respository. 29 | 30 | ## Releases ## 31 | 32 | The releases in the upper-right contain intermediary releases that don't bump the version number. This is to publish hotfixes without reminding everyone to update. 33 | 34 | 35 | -------------------------------------------------------------------------------- /src/com/thebombzen/mods/autoswitch/configuration/SingleEntityIdentifier.java: -------------------------------------------------------------------------------- 1 | package com.thebombzen.mods.autoswitch.configuration; 2 | 3 | import com.thebombzen.mods.thebombzenapi.ThebombzenAPI; 4 | import com.thebombzen.mods.thebombzenapi.configuration.BooleanTester; 5 | import com.thebombzen.mods.thebombzenapi.configuration.ConfigFormatException; 6 | 7 | import net.minecraft.entity.EntityList; 8 | import net.minecraft.entity.EntityLivingBase; 9 | import net.minecraft.entity.player.EntityPlayer; 10 | import net.minecraft.util.text.translation.LanguageMap; 11 | 12 | public class SingleEntityIdentifier implements BooleanTester { 13 | private int id; 14 | private String name; 15 | 16 | public SingleEntityIdentifier(){ 17 | this.id = -1; 18 | this.name = null; 19 | } 20 | 21 | public SingleEntityIdentifier(int id){ 22 | this.id = id; 23 | this.name = null; 24 | } 25 | 26 | public SingleEntityIdentifier(String name){ 27 | this.id = -1; 28 | this.name = name.toLowerCase(); 29 | } 30 | 31 | public static SingleEntityIdentifier parseSingleEntityIdentifier(String info) throws ConfigFormatException { 32 | if (info.equals("@")){ 33 | return new SingleEntityIdentifier(); 34 | } 35 | int id; 36 | try { 37 | id = ThebombzenAPI.parseInteger(info); 38 | return new SingleEntityIdentifier(id); 39 | } catch (NumberFormatException nfe){ 40 | 41 | } 42 | if (info.matches("^(\\w|-|\\.)+$")){ 43 | return new SingleEntityIdentifier(info); 44 | } 45 | throw new ConfigFormatException("Illegal SingleEntityIdentifier: " + info); 46 | } 47 | 48 | @Override 49 | public boolean contains(EntityLivingBase c) { 50 | if (c instanceof EntityPlayer){ 51 | return id == 0 || (id == -1 && name.equals("player")); 52 | } 53 | if (id == -1){ 54 | if (name == null){ 55 | return true; 56 | } else { 57 | String listName = EntityList.getEntityString(c); 58 | if (listName.toLowerCase().equals(name)){ 59 | return true; 60 | } else if (ThebombzenAPI.invokePrivateMethod(null, LanguageMap.class, new String[]{"getInstance", "func_74808_a", "a"}, new Class[]{}).translateKey("entity."+listName+".name").toLowerCase().equals(name)){ 61 | return true; 62 | } else { 63 | return false; 64 | } 65 | } 66 | } else { 67 | return EntityList.getID(c.getClass()) == id; 68 | } 69 | } 70 | 71 | @Override 72 | public String toString() { 73 | return id == -1 ? (name == null ? "@" : name) : Integer.toString(id); 74 | } 75 | 76 | @Override 77 | public int hashCode() { 78 | final int prime = 31; 79 | int result = 1; 80 | result = prime * result + id; 81 | result = prime * result + ((name == null) ? 0 : name.hashCode()); 82 | return result; 83 | } 84 | 85 | @Override 86 | public boolean equals(Object obj) { 87 | if (this == obj) 88 | return true; 89 | if (obj == null) 90 | return false; 91 | if (getClass() != obj.getClass()) 92 | return false; 93 | SingleEntityIdentifier other = (SingleEntityIdentifier) obj; 94 | if (id != other.id) 95 | return false; 96 | if (name == null) { 97 | if (other.name != null) 98 | return false; 99 | } else if (!name.equals(other.name)) 100 | return false; 101 | return true; 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/com/thebombzen/mods/autoswitch/configuration/EntityIdentifier.java: -------------------------------------------------------------------------------- 1 | package com.thebombzen.mods.autoswitch.configuration; 2 | 3 | import com.thebombzen.mods.thebombzenapi.ThebombzenAPI; 4 | import com.thebombzen.mods.thebombzenapi.configuration.CompoundExpression; 5 | import com.thebombzen.mods.thebombzenapi.configuration.ConfigFormatException; 6 | 7 | import net.minecraft.entity.EntityLivingBase; 8 | 9 | public class EntityIdentifier extends CompoundExpression { 10 | 11 | public static EntityIdentifier parseEntityIdentifier(String info) throws ConfigFormatException { 12 | 13 | if (info.length() == 0){ 14 | throw new ConfigFormatException("Empty entity identifier!"); 15 | } 16 | 17 | if (!info.contains("&") && !info.contains("|") && !info.contains("^") && !info.contains("!")){ 18 | if (info.startsWith("(") && info.endsWith(")")){ 19 | return parseEntityIdentifier(info.substring(1, info.length() - 1)); 20 | } else { 21 | return new EntityIdentifier(SingleEntityIdentifier.parseSingleEntityIdentifier(info)); 22 | } 23 | } 24 | 25 | int index = info.indexOf('^'); 26 | while (index >= 0){ 27 | if (index == 0){ 28 | throw new ConfigFormatException("^ requires something on both sides: " + info); 29 | } 30 | if (ThebombzenAPI.isSeparatorAtTopLevel(info, index)){ 31 | String before = info.substring(0, index); 32 | String after = info.substring(index + 1); 33 | return new EntityIdentifier(XOR, EntityIdentifier.parseEntityIdentifier(before), EntityIdentifier.parseEntityIdentifier(after)); 34 | } else { 35 | index = info.indexOf('^', index + 1); 36 | } 37 | } 38 | 39 | index = info.indexOf('|'); 40 | while (index >= 0){ 41 | if (index == 0){ 42 | throw new ConfigFormatException("| requires something on both sides: " + info); 43 | } 44 | if (ThebombzenAPI.isSeparatorAtTopLevel(info, index)){ 45 | String before = info.substring(0, index); 46 | String after = info.substring(index + 1); 47 | return new EntityIdentifier(OR, EntityIdentifier.parseEntityIdentifier(before), EntityIdentifier.parseEntityIdentifier(after)); 48 | } else { 49 | index = info.indexOf('|', index + 1); 50 | } 51 | } 52 | 53 | index = info.indexOf('&'); 54 | while (index >= 0){ 55 | if (index == 0){ 56 | throw new ConfigFormatException("& requires something on both sides: " + info); 57 | } 58 | if (ThebombzenAPI.isSeparatorAtTopLevel(info, index)){ 59 | String before = info.substring(0, index); 60 | String after = info.substring(index + 1); 61 | return new EntityIdentifier(AND, EntityIdentifier.parseEntityIdentifier(before), EntityIdentifier.parseEntityIdentifier(after)); 62 | } else { 63 | index = info.indexOf('&', index + 1); 64 | } 65 | } 66 | 67 | if (info.startsWith("!")){ 68 | return new EntityIdentifier(NOT, EntityIdentifier.parseEntityIdentifier(info.substring(1)), null); 69 | } 70 | 71 | if (info.startsWith("(") && info.endsWith(")")){ 72 | return EntityIdentifier.parseEntityIdentifier(info.substring(1, info.length() - 1)); 73 | } 74 | 75 | throw new ConfigFormatException("Malformed entity identifier: " + info); 76 | 77 | } 78 | 79 | public EntityIdentifier(int type, EntityIdentifier first, EntityIdentifier second){ 80 | super(type, first, second); 81 | } 82 | 83 | public EntityIdentifier(SingleEntityIdentifier singleID){ 84 | super(singleID); 85 | } 86 | 87 | } 88 | -------------------------------------------------------------------------------- /src/com/thebombzen/mods/autoswitch/configuration/SingleValueIdentifier.java: -------------------------------------------------------------------------------- 1 | package com.thebombzen.mods.autoswitch.configuration; 2 | 3 | import com.thebombzen.mods.autoswitch.AutoSwitch; 4 | 5 | import net.minecraft.block.Block; 6 | import net.minecraft.block.state.IBlockState; 7 | import net.minecraft.item.Item; 8 | import net.minecraft.item.ItemStack; 9 | import net.minecraft.util.ResourceLocation; 10 | 11 | public class SingleValueIdentifier { 12 | private ItemStack itemStack = null; 13 | private IBlockState block = null; 14 | private boolean isItem; 15 | public SingleValueIdentifier(IBlockState block){ 16 | this.block = block; 17 | isItem = false; 18 | } 19 | public SingleValueIdentifier(ItemStack stack){ 20 | itemStack = stack == null ? null : stack.copy(); 21 | isItem = true; 22 | } 23 | public SingleValueIdentifier(SingleValueIdentifier id){ 24 | isItem = id.isItem; 25 | itemStack = id.itemStack == null ? null : id.itemStack.copy(); 26 | block = id.block; 27 | } 28 | 29 | public ItemStack getItemStack(){ 30 | if (!isItem){ 31 | AutoSwitch.instance.throwException("Getting ItemStack of block SVI", new UnsupportedOperationException(), false); 32 | return null; 33 | } else { 34 | return itemStack; 35 | } 36 | } 37 | 38 | 39 | public IBlockState getBlockState(){ 40 | if (isItem){ 41 | AutoSwitch.instance.throwException("Getting IBlockState of item SVI", new UnsupportedOperationException(), false); 42 | return null; 43 | } else { 44 | return block; 45 | } 46 | } 47 | 48 | public String getModId(){ 49 | if (isItem && itemStack == null){ 50 | return null; 51 | } 52 | return getUniqueIdentifier().getResourceDomain(); 53 | } 54 | 55 | public String getName(){ 56 | if (isItem && itemStack == null){ 57 | return null; 58 | } 59 | return getUniqueIdentifier().getResourcePath(); 60 | } 61 | 62 | public ResourceLocation getUniqueIdentifier(){ 63 | if (isItem){ 64 | if (itemStack == null){ 65 | return null; 66 | } 67 | return Item.REGISTRY.getNameForObject(itemStack.getItem()); 68 | } else { 69 | return Block.REGISTRY.getNameForObject(block.getBlock()); 70 | } 71 | } 72 | 73 | public boolean isItem(){ 74 | return isItem; 75 | } 76 | @Override 77 | public int hashCode() { 78 | final int prime = 31; 79 | int result = 1; 80 | result = prime * result + ((block == null) ? 0 : block.hashCode()); 81 | result = prime * result + (isItem ? 1231 : 1237); 82 | result = prime * result 83 | + ((itemStack == null) ? 0 : itemStack.hashCode()); 84 | return result; 85 | } 86 | @Override 87 | public boolean equals(Object obj) { 88 | if (this == obj) 89 | return true; 90 | if (obj == null) 91 | return false; 92 | if (getClass() != obj.getClass()) 93 | return false; 94 | SingleValueIdentifier other = (SingleValueIdentifier) obj; 95 | if (block == null) { 96 | if (other.block != null) 97 | return false; 98 | } else if (!block.equals(other.block)) 99 | return false; 100 | if (isItem != other.isItem) 101 | return false; 102 | if (itemStack == null) { 103 | if (other.itemStack != null) 104 | return false; 105 | } else if (!itemStack.equals(other.itemStack)) 106 | return false; 107 | return true; 108 | } 109 | @Override 110 | public String toString() { 111 | return "SingleValueIdentifier [itemStack=" + itemStack + ", block=" 112 | + block + ", isItem=" + isItem + "]"; 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/com/thebombzen/mods/autoswitch/configuration/BlockItemIdentifier.java: -------------------------------------------------------------------------------- 1 | package com.thebombzen.mods.autoswitch.configuration; 2 | 3 | import com.thebombzen.mods.thebombzenapi.ThebombzenAPI; 4 | import com.thebombzen.mods.thebombzenapi.configuration.CompoundExpression; 5 | import com.thebombzen.mods.thebombzenapi.configuration.ConfigFormatException; 6 | 7 | import net.minecraft.block.state.IBlockState; 8 | import net.minecraft.item.ItemStack; 9 | 10 | public class BlockItemIdentifier extends CompoundExpression { 11 | 12 | public static BlockItemIdentifier parseBlockItemIdentifier(String info) throws ConfigFormatException { 13 | 14 | if (info.length() == 0){ 15 | throw new ConfigFormatException("Empty block/item identifier"); 16 | } 17 | 18 | if (!info.contains("&") && !info.contains("|") && !info.contains("^") && !info.contains("!")){ 19 | if (info.startsWith("(") && info.endsWith(")")){ 20 | return parseBlockItemIdentifier(info.substring(1, info.length() - 1)); 21 | } else { 22 | return new BlockItemIdentifier(SingleBlockItemIdentifier.parseSingleBlockItemIdentifier(info)); 23 | } 24 | } 25 | 26 | int index = info.indexOf('^'); 27 | while (index >= 0){ 28 | if (index == 0){ 29 | throw new ConfigFormatException("^ requires something on both sides: " + info); 30 | } 31 | if (ThebombzenAPI.isSeparatorAtTopLevel(info, index)){ 32 | String before = info.substring(0, index); 33 | String after = info.substring(index + 1); 34 | return new BlockItemIdentifier(XOR, BlockItemIdentifier.parseBlockItemIdentifier(before), BlockItemIdentifier.parseBlockItemIdentifier(after)); 35 | } else { 36 | index = info.indexOf('^', index + 1); 37 | } 38 | } 39 | 40 | index = info.indexOf('|'); 41 | while (index >= 0){ 42 | if (index == 0){ 43 | throw new ConfigFormatException("| requires something on both sides: " + info); 44 | } 45 | if (ThebombzenAPI.isSeparatorAtTopLevel(info, index)){ 46 | String before = info.substring(0, index); 47 | String after = info.substring(index + 1); 48 | return new BlockItemIdentifier(OR, BlockItemIdentifier.parseBlockItemIdentifier(before), BlockItemIdentifier.parseBlockItemIdentifier(after)); 49 | } else { 50 | index = info.indexOf('|', index + 1); 51 | } 52 | } 53 | 54 | index = info.indexOf('&'); 55 | while (index >= 0){ 56 | if (index == 0){ 57 | throw new ConfigFormatException("& requires something on both sides: " + info); 58 | } 59 | if (ThebombzenAPI.isSeparatorAtTopLevel(info, index)){ 60 | String before = info.substring(0, index); 61 | String after = info.substring(index + 1); 62 | return new BlockItemIdentifier(AND, BlockItemIdentifier.parseBlockItemIdentifier(before), BlockItemIdentifier.parseBlockItemIdentifier(after)); 63 | } else { 64 | index = info.indexOf('&', index + 1); 65 | } 66 | } 67 | 68 | if (info.startsWith("!")){ 69 | return new BlockItemIdentifier(NOT, BlockItemIdentifier.parseBlockItemIdentifier(info.substring(1)), null); 70 | } 71 | 72 | if (info.startsWith("(") && info.endsWith(")")){ 73 | return BlockItemIdentifier.parseBlockItemIdentifier(info.substring(1, info.length() - 1)); 74 | } 75 | 76 | throw new ConfigFormatException("Malformed block/item identifier: " + info); 77 | 78 | } 79 | 80 | public BlockItemIdentifier(int type, BlockItemIdentifier first, BlockItemIdentifier second){ 81 | super(type, first, second); 82 | } 83 | 84 | public BlockItemIdentifier(SingleBlockItemIdentifier singleID){ 85 | super(singleID); 86 | } 87 | 88 | public boolean contains(IBlockState state){ 89 | return contains(new SingleValueIdentifier(state)); 90 | } 91 | 92 | public boolean contains(ItemStack itemStack){ 93 | if (itemStack == null){ 94 | return false; 95 | } 96 | return contains(new SingleValueIdentifier(itemStack)); 97 | } 98 | 99 | } 100 | -------------------------------------------------------------------------------- /src/com/thebombzen/mods/autoswitch/configuration/SingleBlockItemIdentifier.java: -------------------------------------------------------------------------------- 1 | package com.thebombzen.mods.autoswitch.configuration; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import java.util.Scanner; 6 | 7 | import com.thebombzen.mods.thebombzenapi.ThebombzenAPI; 8 | import com.thebombzen.mods.thebombzenapi.configuration.BooleanTester; 9 | import com.thebombzen.mods.thebombzenapi.configuration.ConfigFormatException; 10 | 11 | import net.minecraft.block.Block; 12 | import net.minecraft.block.state.IBlockState; 13 | import net.minecraft.init.Blocks; 14 | import net.minecraft.item.Item; 15 | import net.minecraft.item.ItemStack; 16 | import net.minecraft.util.ResourceLocation; 17 | 18 | /** 19 | * Represents a block or item with the given String namespace, name, and damage values. 20 | * @author thebombzen 21 | */ 22 | public class SingleBlockItemIdentifier implements BooleanTester { 23 | 24 | public static final int ALL = 0; 25 | public static final int NAME = 1; 26 | public static final int CLASS = 2; 27 | public static final int MATERIAL = 3; 28 | 29 | /** 30 | * Parse an identifier from a string. 31 | */ 32 | public static SingleBlockItemIdentifier parseSingleBlockItemIdentifier(String info) throws ConfigFormatException { 33 | if (info.length() == 0){ 34 | throw new ConfigFormatException("Empty block/item identifier"); 35 | } 36 | char type = info.charAt(0); 37 | int superNum = 0; 38 | if (type == '@' || type == '$'){ 39 | info = info.substring(1); 40 | } else if (type == '['){ 41 | int lastIndex = info.indexOf(']'); 42 | if (lastIndex < 0){ 43 | throw new ConfigFormatException("No closing ]: " + info); 44 | } 45 | String superNumS = info.substring(1, lastIndex); 46 | try { 47 | superNum = ThebombzenAPI.parseInteger(superNumS); 48 | } catch (NumberFormatException e){ 49 | throw new ConfigFormatException("Invalid broadening number: " + superNumS, e); 50 | } 51 | info = info.substring(lastIndex + 1); 52 | } 53 | 54 | boolean all = info.startsWith("+") || info.startsWith("-"); 55 | 56 | if (all){ 57 | info = "false_prefix" + info; 58 | } 59 | 60 | Scanner scanner = new Scanner(info); 61 | scanner.useDelimiter("(?<=[^\\+-])(?=[\\+-])"); 62 | 63 | if (!scanner.hasNext()){ 64 | scanner.close(); 65 | return new SingleBlockItemIdentifier(SingleBlockItemIdentifier.ALL); 66 | } 67 | 68 | String fullname = scanner.next(); 69 | String modid; 70 | String name; 71 | 72 | //System.out.println(fullname); 73 | 74 | if (all){ 75 | modid = ""; 76 | name = ""; 77 | } else { 78 | int index = fullname.indexOf(':'); 79 | if (index < 0){ 80 | modid = "minecraft"; 81 | name = fullname; 82 | } else { 83 | modid = fullname.substring(0, index); 84 | name = fullname.substring(index + 1); 85 | } 86 | } 87 | 88 | List valueSets = new ArrayList(); 89 | 90 | while (scanner.hasNext()){ 91 | String s = scanner.next(); 92 | ValueSet valueSet = ValueSet.parseValueSet(s); 93 | valueSets.add(valueSet); 94 | } 95 | 96 | scanner.close(); 97 | 98 | ValueSet[] sets = valueSets.toArray(new ValueSet[valueSets.size()]); 99 | 100 | if (all){ 101 | return new SingleBlockItemIdentifier(SingleBlockItemIdentifier.ALL, sets); 102 | } 103 | 104 | switch(type){ 105 | case '@': 106 | case '[': 107 | return new SingleBlockItemIdentifier(SingleBlockItemIdentifier.CLASS, modid, name, superNum, sets); 108 | case '$': 109 | return new SingleBlockItemIdentifier(SingleBlockItemIdentifier.MATERIAL, modid, name, 0, sets); 110 | default: 111 | return new SingleBlockItemIdentifier(SingleBlockItemIdentifier.NAME, modid, name, 0, sets); 112 | } 113 | } 114 | private ResourceLocation resourceLocation; 115 | private ValueSet[] valueSets; 116 | 117 | private int type; 118 | private int superNum; 119 | 120 | /** 121 | * Construct an identifier with the given String namespace, name, and damage value. 122 | * @param namespace The String namespace such as "minecraft" 123 | * @param name The String name such as "log" 124 | * @param damageValue The Damage value 125 | */ 126 | public SingleBlockItemIdentifier(int type, String namespace, String name, int damageValue){ 127 | this(type, namespace, name, 0, new ValueSet(damageValue, Integer.MAX_VALUE, false)); 128 | } 129 | 130 | /** 131 | * Construct an identifier with the given String namespace, name, and damage values. 132 | * @param namespace The String namespace such as "minecraft" 133 | * @param name The String name such as "log" 134 | * @param damageValues The Damage values 135 | */ 136 | public SingleBlockItemIdentifier(int type, String namespace, String name, int superNum, ValueSet... damageValues){ 137 | 138 | if (type < 0 || type > 3){ 139 | throw new IllegalArgumentException(); 140 | } 141 | this.superNum = superNum; 142 | this.type = type; 143 | this.resourceLocation = new ResourceLocation(namespace + ":" + name); 144 | if (damageValues.length == 0 || damageValues.length > 0 && damageValues[0].doesSubtract()){ 145 | ValueSet[] temp = new ValueSet[damageValues.length + 1]; 146 | System.arraycopy(damageValues, 0, temp, 1, damageValues.length); 147 | temp[0] = new ValueSet(); 148 | damageValues = temp; 149 | } 150 | this.valueSets = damageValues; 151 | } 152 | 153 | /** 154 | * Construct a BlockItemIdentifier that matches the damages values on any item/block. 155 | * @param damageValues 156 | */ 157 | public SingleBlockItemIdentifier(int type, ValueSet... damageValues){ 158 | this(type, "", "", 0, damageValues); 159 | } 160 | 161 | /** 162 | * Gets whether this identifier refers to the given name and damage value. 163 | * @param name The String name of this block/item 164 | * @param damageValue The damage value of this block/item 165 | */ 166 | @Override 167 | public boolean contains(SingleValueIdentifier identifier){ 168 | String modid = identifier.getModId(); 169 | String name = identifier.getName(); 170 | switch (type){ 171 | case ALL: 172 | break; 173 | case NAME: 174 | if (!getName().equals(name) || !getModId().equals(modid)){ 175 | return false; 176 | } 177 | break; 178 | case CLASS: 179 | if (isBlock()){ 180 | IBlockState state = identifier.getBlockState(); 181 | if (state == null){ 182 | break; 183 | } 184 | Class clazz = getBlock().getClass(); 185 | for (int i = 0; i < superNum; i++){ 186 | if (clazz.getSuperclass() != null){ 187 | clazz = clazz.getSuperclass(); 188 | } else { 189 | break; 190 | } 191 | } 192 | if (!clazz.isAssignableFrom(state.getBlock().getClass())){ 193 | return false; 194 | } 195 | break; 196 | } 197 | if (isItem()){ 198 | ItemStack stack = identifier.getItemStack(); 199 | if (stack == null){ 200 | break; 201 | } 202 | Class clazz = getItem().getClass(); 203 | for (int i = 0; i < superNum; i++){ 204 | if (clazz.getSuperclass() != null){ 205 | clazz = clazz.getSuperclass(); 206 | } else { 207 | break; 208 | } 209 | } 210 | if (!clazz.isAssignableFrom(stack.getItem().getClass())){ 211 | return false; 212 | } 213 | } else { 214 | return false; 215 | } 216 | break; 217 | case MATERIAL: 218 | if (getBlock() == null){ 219 | break; 220 | } 221 | IBlockState blockState = identifier.getBlockState(); 222 | if (blockState == null || getBlock() == null){ 223 | return false; 224 | } 225 | if (!getBlock().getDefaultState().getMaterial().equals(blockState.getMaterial())){ 226 | return false; 227 | } 228 | break; 229 | } 230 | for (int i = valueSets.length - 1; i >= 0; i--){ 231 | ValueSet set = valueSets[i]; 232 | SingleValueIdentifier id2 = identifier; 233 | if (identifier.isItem()){ 234 | if (set.getMask() < 0){ 235 | if (identifier.getItemStack() == null || identifier.getItemStack().getMaxDamage() < identifier.getItemStack().getItemDamage()){ 236 | continue; 237 | } 238 | ItemStack newStack = identifier.getItemStack().copy(); 239 | newStack.setItemDamage(newStack.getMaxDamage() - newStack.getItemDamage()); 240 | id2 = new SingleValueIdentifier(newStack); 241 | } 242 | } 243 | if (set.contains(id2)){ 244 | return !set.doesSubtract(); 245 | } 246 | } 247 | return false; 248 | } 249 | 250 | /** 251 | * Gets the block associated with this identifier. 252 | * null if this is not a block. 253 | */ 254 | public Block getBlock() { 255 | Block block = Block.getBlockFromName(resourceLocation.toString()); 256 | if (Block.isEqualTo(block, Blocks.AIR)){ 257 | return null; 258 | } else { 259 | return block; 260 | } 261 | } 262 | 263 | /** 264 | * Return the array of ValueSets 265 | */ 266 | public ValueSet[] getDamageValues() { 267 | return valueSets; 268 | } 269 | 270 | /** 271 | * Gets the item associated with this identifier. If it's a block it 272 | * will return the corresponding ItemBlock. 273 | */ 274 | public Item getItem() { 275 | return Item.getByNameOrId(resourceLocation.toString()); 276 | } 277 | 278 | /** 279 | * Gets the String namespace of this item/block 280 | * @return 281 | */ 282 | public String getModId(){ 283 | return resourceLocation.getResourceDomain(); 284 | } 285 | 286 | /** 287 | * Gets the String name of this item/block 288 | */ 289 | public String getName(){ 290 | return resourceLocation.getResourcePath(); 291 | } 292 | 293 | /** 294 | * Determines whether this IDMetadataPair is a valid (non-null) block. Air 295 | * is not a valid block. 296 | * 297 | * @return Whether this is a valid Block object. 298 | */ 299 | public boolean isBlock() { 300 | return getBlock() != null; 301 | } 302 | 303 | /** 304 | * Determines whether this IDMetadataPair is a valid (non-null) item. All 305 | * valid registered blocks are also valid items, see 306 | * {net.minecraft.item.ItemBlock} 307 | * 308 | * @return Whether this is a valid Item object. 309 | */ 310 | public boolean isItem() { 311 | return getItem() != null; 312 | } 313 | 314 | } 315 | -------------------------------------------------------------------------------- /src/com/thebombzen/mods/autoswitch/configuration/ValueSet.java: -------------------------------------------------------------------------------- 1 | package com.thebombzen.mods.autoswitch.configuration; 2 | 3 | import java.util.Iterator; 4 | import java.util.Scanner; 5 | 6 | import com.google.common.collect.ImmutableMap; 7 | import com.thebombzen.mods.autoswitch.AutoSwitch; 8 | import com.thebombzen.mods.thebombzenapi.ThebombzenAPI; 9 | import com.thebombzen.mods.thebombzenapi.configuration.BooleanTester; 10 | import com.thebombzen.mods.thebombzenapi.configuration.ConfigFormatException; 11 | 12 | import net.minecraft.block.properties.IProperty; 13 | import net.minecraft.block.state.IBlockState; 14 | import net.minecraft.enchantment.Enchantment; 15 | import net.minecraft.item.ItemStack; 16 | import net.minecraft.nbt.NBTTagList; 17 | 18 | 19 | /** 20 | * This class represents a set of values. It could be a universal set, 21 | * a one-item set, or a set based off a mask. 22 | * @author thebombzen 23 | */ 24 | public class ValueSet implements BooleanTester { 25 | 26 | public static ValueSet parseValueSet(String s) throws ConfigFormatException { 27 | String firsts = null; 28 | int firstn = 0; 29 | String seconds = null; 30 | int secondn = 0; 31 | int thirdn = 0; 32 | boolean subtract; 33 | 34 | switch (s.charAt(0)){ 35 | case '+': 36 | subtract = false; 37 | break; 38 | case '-': 39 | subtract = true; 40 | break; 41 | default: 42 | throw new ConfigFormatException("Value Set doesn't start with + or -: " + s); 43 | } 44 | 45 | Scanner scanner = new Scanner(s.substring(1)); 46 | scanner.useDelimiter(":"); 47 | 48 | boolean enchant = false; 49 | boolean block = false; 50 | 51 | if (scanner.hasNext()){ 52 | String num = scanner.next(); 53 | if (Character.toUpperCase(num.charAt(0)) == 'E'){ 54 | num = num.substring(1); 55 | enchant = true; 56 | } 57 | try { 58 | firstn = ThebombzenAPI.parseInteger(num); 59 | } catch (NumberFormatException nfe){ 60 | block = true; 61 | if (enchant){ 62 | num = 'E' + num; 63 | enchant = false; 64 | } 65 | firsts = num; 66 | } 67 | if (enchant && Enchantment.getEnchantmentByID(firstn) == null){ 68 | scanner.close(); 69 | throw new ConfigFormatException("Invalid Enchantment ID: " + firstn); 70 | } 71 | } else { 72 | scanner.close(); 73 | return new ValueSet(); 74 | } 75 | 76 | if (scanner.hasNext()){ 77 | String num = scanner.next(); 78 | if (block){ 79 | seconds = num; 80 | } else { 81 | try { 82 | secondn = ThebombzenAPI.parseInteger(num); 83 | } catch (NumberFormatException nfe){ 84 | scanner.close(); 85 | throw new ConfigFormatException("Invalid number: " + num); 86 | } 87 | } 88 | } else { 89 | scanner.close(); 90 | if (block){ 91 | throw new ConfigFormatException("No value for property: " + firsts); 92 | } 93 | if (enchant){ 94 | return new ValueSet(Enchantment.getEnchantmentByID(firstn), 1, Integer.MAX_VALUE, subtract); 95 | } else { 96 | return new ValueSet(firstn, Integer.MAX_VALUE, subtract); 97 | } 98 | } 99 | 100 | if (enchant){ 101 | if (scanner.hasNext()){ 102 | String num = scanner.next(); 103 | scanner.close(); 104 | try { 105 | thirdn = ThebombzenAPI.parseInteger(num); 106 | } catch (NumberFormatException e){ 107 | throw new ConfigFormatException("Invalid number: " + num); 108 | } 109 | return new ValueSet(Enchantment.getEnchantmentByID(firstn), secondn, thirdn, subtract); 110 | } else { 111 | scanner.close(); 112 | return new ValueSet(Enchantment.getEnchantmentByID(firstn), secondn, Integer.MAX_VALUE, subtract); 113 | } 114 | } else { 115 | scanner.close(); 116 | if (block){ 117 | return new ValueSet(firsts, seconds, subtract); 118 | } else { 119 | return new ValueSet(firstn, secondn, subtract); 120 | } 121 | } 122 | } 123 | 124 | /** 125 | * If true, this is meant to subtract rather than add. 126 | */ 127 | private final boolean subtract; 128 | private final int type; 129 | 130 | private int data = -1; 131 | private int mask = -1; 132 | 133 | private String state = null; 134 | private String value = null; 135 | 136 | 137 | private int min = -1; 138 | private int max = -1; 139 | private Enchantment enchantment = null; 140 | 141 | public static final int UNIVERSAL = 0; 142 | public static final int ITEM_DAMAGE = 1; 143 | public static final int ITEM_ENCHANTMENT = 2; 144 | public static final int BLOCK_STATE = 3; 145 | 146 | /** 147 | * Construct a universal set 148 | */ 149 | public ValueSet(){ 150 | this.type = ValueSet.UNIVERSAL; 151 | this.subtract = false; 152 | } 153 | 154 | /** 155 | * Construct a set using enchantments 156 | * @param enchantment The enchantment for which to check 157 | * @param min The minimum accepted power of the enchantment 158 | * @param max The maximum accepted power of the enchantment 159 | * @param subtract whether to subtract (true) or add (false) the value. 160 | */ 161 | public ValueSet(Enchantment enchantment, int min, int max, boolean subtract){ 162 | this.type = ValueSet.ITEM_ENCHANTMENT; 163 | this.subtract = subtract; 164 | this.min = min; 165 | this.max = max; 166 | this.enchantment = enchantment; 167 | } 168 | 169 | /** 170 | * Construct a set using block states 171 | * @param state The name of the block state, e.g. axis (for axis:x) 172 | * @param value The value of the block state, e.g. x (for axis:x) 173 | * @param subtract whether to subtract (true) or add (false) the value. 174 | */ 175 | public ValueSet(String state, String value, boolean subtract){ 176 | this.type = ValueSet.BLOCK_STATE; 177 | this.subtract = subtract; 178 | this.state = state; 179 | this.value = value; 180 | } 181 | 182 | /** 183 | * Construct a set that matches all values whose mask matches the data 184 | * @param data The data 185 | * @param mask The mask to apply to potential values 186 | * @param subtract whether to subtract (true) or add (false) the value. 187 | */ 188 | public ValueSet(int data, int mask, boolean subtract){ 189 | this.type = ValueSet.ITEM_DAMAGE; 190 | this.data = data; 191 | this.mask = mask; 192 | this.subtract = subtract; 193 | } 194 | 195 | private boolean contains(IBlockState state){ 196 | if (state == null){ 197 | return false; 198 | } 199 | ImmutableMap, Comparable> properties = state.getProperties(); 200 | Iterator> iterator = properties.keySet().iterator(); 201 | while (iterator.hasNext()){ 202 | IProperty prop = iterator.next(); 203 | if (prop.getName().equalsIgnoreCase(this.state) && properties.get(prop).toString().equalsIgnoreCase(this.value)){ 204 | return true; 205 | } 206 | 207 | } 208 | return false; 209 | } 210 | 211 | /** 212 | * Returns true if this set contains the value 213 | * @param value the value to check 214 | */ 215 | private boolean contains(ItemStack stack){ 216 | if (stack == null){ 217 | return false; 218 | } 219 | if (type == ValueSet.ITEM_ENCHANTMENT){ 220 | NBTTagList list = stack.getEnchantmentTagList(); 221 | if (list == null){ 222 | return false; 223 | } 224 | for (int i = 0; i < list.tagCount(); i++){ 225 | short id = list.getCompoundTagAt(i).getShort("id"); 226 | short lvl = list.getCompoundTagAt(i).getShort("lvl"); 227 | if (id == Enchantment.getEnchantmentID(enchantment)){ 228 | if (lvl >= min && lvl <= max){ 229 | return true; 230 | } else { 231 | return false; 232 | } 233 | } else { 234 | continue; 235 | } 236 | } 237 | return false; 238 | } else { 239 | return (stack.getItemDamage() & mask) == data; 240 | } 241 | } 242 | 243 | @Override 244 | public boolean contains(SingleValueIdentifier id){ 245 | if (type == ValueSet.UNIVERSAL){ 246 | return true; 247 | } 248 | if (id.isItem()){ 249 | if (type == ValueSet.BLOCK_STATE){ 250 | AutoSwitch.instance.throwException("Trying to check the block state of an item!", new UnsupportedOperationException(), false); 251 | return false; 252 | } 253 | return contains(id.getItemStack()); 254 | } else { 255 | if (type != ValueSet.BLOCK_STATE){ 256 | AutoSwitch.instance.throwException("Trying to check the item state of a block!", new UnsupportedOperationException(), false); 257 | return false; 258 | } 259 | return contains(id.getBlockState()); 260 | } 261 | } 262 | 263 | /** 264 | * Returns whether this set subtracts rather than adds values to the total. 265 | */ 266 | public boolean doesSubtract(){ 267 | return subtract; 268 | } 269 | 270 | public int getData() { 271 | return data; 272 | } 273 | 274 | public int getMask() { 275 | return mask; 276 | } 277 | 278 | /** 279 | * Returns a String representation, as seen in the config. 280 | */ 281 | @Override 282 | public String toString() { 283 | StringBuilder b = new StringBuilder(subtract ? '-' : '+'); 284 | switch (type){ 285 | case UNIVERSAL: 286 | b.append('U'); 287 | break; 288 | case ITEM_ENCHANTMENT: 289 | b.append('E').append("0x").append(Integer.toHexString(Enchantment.getEnchantmentID(enchantment))).append(":0x").append(Integer.toHexString(min)).append(":0x").append(Integer.toHexString(max)); 290 | break; 291 | case ITEM_DAMAGE: 292 | b.append("0x").append(Integer.toHexString(data)).append(":0x").append(Integer.toHexString(mask)); 293 | break; 294 | case BLOCK_STATE: 295 | b.append(state).append(':').append(value); 296 | break; 297 | } 298 | return b.toString(); 299 | } 300 | 301 | @Override 302 | public int hashCode() { 303 | final int prime = 31; 304 | int result = 1; 305 | result = prime * result + data; 306 | result = prime * result 307 | + ((enchantment == null) ? 0 : enchantment.hashCode()); 308 | result = prime * result + mask; 309 | result = prime * result + max; 310 | result = prime * result + min; 311 | result = prime * result + ((state == null) ? 0 : state.hashCode()); 312 | result = prime * result + (subtract ? 1231 : 1237); 313 | result = prime * result + type; 314 | result = prime * result + ((value == null) ? 0 : value.hashCode()); 315 | return result; 316 | } 317 | 318 | @Override 319 | public boolean equals(Object obj) { 320 | if (this == obj) 321 | return true; 322 | if (obj == null) 323 | return false; 324 | if (getClass() != obj.getClass()) 325 | return false; 326 | ValueSet other = (ValueSet) obj; 327 | if (data != other.data) 328 | return false; 329 | if (enchantment == null) { 330 | if (other.enchantment != null) 331 | return false; 332 | } else if (!enchantment.equals(other.enchantment)) 333 | return false; 334 | if (mask != other.mask) 335 | return false; 336 | if (max != other.max) 337 | return false; 338 | if (min != other.min) 339 | return false; 340 | if (state == null) { 341 | if (other.state != null) 342 | return false; 343 | } else if (!state.equals(other.state)) 344 | return false; 345 | if (subtract != other.subtract) 346 | return false; 347 | if (type != other.type) 348 | return false; 349 | if (value == null) { 350 | if (other.value != null) 351 | return false; 352 | } else if (!value.equals(other.value)) 353 | return false; 354 | return true; 355 | } 356 | 357 | } 358 | -------------------------------------------------------------------------------- /src/com/thebombzen/mods/autoswitch/installer/ASInstallerFrame.java: -------------------------------------------------------------------------------- 1 | package com.thebombzen.mods.autoswitch.installer; 2 | 3 | import java.awt.BorderLayout; 4 | import java.awt.Desktop; 5 | import java.awt.Dimension; 6 | import java.awt.Toolkit; 7 | import java.awt.event.ActionEvent; 8 | import java.awt.event.ActionListener; 9 | import java.awt.event.MouseAdapter; 10 | import java.awt.event.MouseEvent; 11 | import java.io.BufferedReader; 12 | import java.io.File; 13 | import java.io.FileInputStream; 14 | import java.io.FileOutputStream; 15 | import java.io.IOException; 16 | import java.io.InputStreamReader; 17 | import java.net.MalformedURLException; 18 | import java.net.URI; 19 | import java.net.URISyntaxException; 20 | import java.net.URL; 21 | import java.nio.channels.Channels; 22 | import java.nio.channels.FileChannel; 23 | import java.nio.channels.ReadableByteChannel; 24 | import java.util.ArrayList; 25 | import java.util.Arrays; 26 | import java.util.List; 27 | import java.util.Scanner; 28 | import java.util.jar.JarFile; 29 | 30 | import javax.swing.Box; 31 | import javax.swing.JButton; 32 | import javax.swing.JDialog; 33 | import javax.swing.JFileChooser; 34 | import javax.swing.JFrame; 35 | import javax.swing.JLabel; 36 | import javax.swing.JOptionPane; 37 | import javax.swing.JTextField; 38 | import javax.swing.WindowConstants; 39 | 40 | import com.thebombzen.mods.autoswitch.Constants; 41 | 42 | public class ASInstallerFrame extends JFrame { 43 | 44 | private static final long serialVersionUID = 1L; 45 | private static ASInstallerFrame instance; 46 | 47 | public static void copyFile(File sourceFile, File destFile) 48 | throws IOException { 49 | 50 | if (!destFile.exists()) { 51 | destFile.createNewFile(); 52 | } 53 | 54 | FileChannel source = null; 55 | FileChannel destination = null; 56 | FileInputStream inStream = null; 57 | FileOutputStream outStream = null; 58 | 59 | try { 60 | inStream = new FileInputStream(sourceFile); 61 | outStream = new FileOutputStream(destFile); 62 | source = inStream.getChannel(); 63 | destination = outStream.getChannel(); 64 | 65 | long count = 0; 66 | long size = source.size(); 67 | while ((count += destination.transferFrom(source, count, size 68 | - count)) < size) 69 | ; 70 | } finally { 71 | if (inStream != null) { 72 | inStream.close(); 73 | } 74 | if (outStream != null) { 75 | outStream.close(); 76 | } 77 | if (source != null) { 78 | source.close(); 79 | } 80 | if (destination != null) { 81 | destination.close(); 82 | } 83 | } 84 | } 85 | 86 | public static String getMinecraftClientDirectory() throws IOException { 87 | String name = System.getProperty("os.name"); 88 | if (name.toLowerCase().contains("windows")) { 89 | return new File(System.getenv("appdata") + "\\.minecraft") 90 | .getCanonicalPath(); 91 | } else if (name.toLowerCase().contains("mac") 92 | || name.toLowerCase().contains("osx") 93 | || name.toLowerCase().contains("os x")) { 94 | return new File(System.getProperty("user.home") 95 | + "/Library/Application Support/minecraft") 96 | .getCanonicalPath(); 97 | } else { 98 | return new File(System.getProperty("user.home") + "/.minecraft") 99 | .getCanonicalPath(); 100 | } 101 | } 102 | 103 | public static void main(String[] args) throws Exception { 104 | if (args.length > 0) { 105 | if (args[0].equals("--no-gui") || args[0].equals("-n")) { 106 | installNoGui(getMinecraftClientDirectory()); 107 | return; 108 | } else { 109 | System.err 110 | .println("Run with --no-gui or -n to install without a gui."); 111 | } 112 | } 113 | new ASInstallerFrame().setVisible(true); 114 | } 115 | 116 | private JTextField textField; 117 | private JDialog dialog; 118 | 119 | public ASInstallerFrame() throws IOException { 120 | instance = this; 121 | Box superBox = Box.createHorizontalBox(); 122 | superBox.add(Box.createHorizontalStrut(10)); 123 | 124 | Box content = Box.createVerticalBox(); 125 | 126 | content.add(Box.createVerticalStrut(10)); 127 | 128 | JLabel label = new JLabel("Select minecraft folder:"); 129 | Box labelBox = Box.createHorizontalBox(); 130 | labelBox.add(label); 131 | labelBox.add(Box.createHorizontalGlue()); 132 | content.add(labelBox); 133 | 134 | Box textBox = Box.createHorizontalBox(); 135 | textField = new JTextField(); 136 | textField.setText(getMinecraftClientDirectory()); 137 | textBox.add(textField); 138 | textBox.add(Box.createHorizontalStrut(10)); 139 | JButton browseButton = new JButton("Browse"); 140 | textBox.add(browseButton); 141 | content.add(textBox); 142 | 143 | content.add(Box.createVerticalStrut(10)); 144 | 145 | JLabel forgeLabel = new JLabel( 146 | "Remember to also install Minecraft Forge."); 147 | Box forgeLabelBox = Box.createHorizontalBox(); 148 | forgeLabelBox.add(forgeLabel); 149 | forgeLabelBox.add(Box.createHorizontalGlue()); 150 | content.add(forgeLabelBox); 151 | 152 | Box forgeLinkBox = Box.createHorizontalBox(); 153 | JLabel forgeLinkLabel = new JLabel( 154 | "Download Minecraft Forge Here"); 155 | forgeLinkBox.add(forgeLinkLabel); 156 | forgeLinkBox.add(Box.createHorizontalGlue()); 157 | content.add(forgeLinkBox); 158 | 159 | content.add(Box.createVerticalStrut(10)); 160 | 161 | /* 162 | * JLabel tbzapiLabel = new 163 | * JLabel("Remember to also install ThebombzenAPI."); Box tbzapiLabelBox 164 | * = Box.createHorizontalBox(); tbzapiLabelBox.add(tbzapiLabel); 165 | * tbzapiLabelBox.add(Box.createHorizontalGlue()); 166 | * content.add(tbzapiLabelBox); 167 | * 168 | * Box linkBox = Box.createHorizontalBox(); JLabel linkLabel = new 169 | * JLabel( 170 | * "Download ThebombzenAPI Here" 171 | * ); linkBox.add(linkLabel); linkBox.add(Box.createHorizontalGlue()); 172 | * content.add(linkBox); 173 | * 174 | * content.add(Box.createVerticalStrut(10)); 175 | */ 176 | 177 | JButton install = new JButton("Install AutoSwitch"); 178 | Box installBox = Box.createHorizontalBox(); 179 | installBox.add(Box.createHorizontalGlue()); 180 | installBox.add(install); 181 | installBox.add(Box.createHorizontalGlue()); 182 | content.add(installBox); 183 | 184 | content.add(Box.createVerticalStrut(10)); 185 | 186 | superBox.add(content); 187 | superBox.add(Box.createHorizontalStrut(10)); 188 | 189 | install.addActionListener(new ActionListener() { 190 | @Override 191 | public void actionPerformed(ActionEvent ae) { 192 | new Thread(new Runnable() { 193 | @Override 194 | public void run() { 195 | install(); 196 | } 197 | }).start(); 198 | } 199 | }); 200 | 201 | forgeLinkLabel.addMouseListener(new MouseAdapter() { 202 | @Override 203 | public void mouseClicked(MouseEvent me) { 204 | try { 205 | Desktop.getDesktop().browse( 206 | new URI("http://files.minecraftforge.net/")); 207 | } catch (IOException e) { 208 | 209 | } catch (URISyntaxException e) { 210 | 211 | } 212 | } 213 | }); 214 | 215 | /* 216 | * linkLabel.addMouseListener(new MouseAdapter(){ 217 | * 218 | * @Override public void mouseClicked(MouseEvent me){ try { 219 | * Desktop.getDesktop().browse(new 220 | * URI("http://is.gd/ThebombzensMods#ThebombzenAPI")); } catch 221 | * (IOException e) { 222 | * 223 | * } catch (URISyntaxException e) { 224 | * 225 | * } } }); 226 | */ 227 | 228 | browseButton.addActionListener(new ActionListener() { 229 | @Override 230 | public void actionPerformed(ActionEvent ae) { 231 | JFileChooser jfc = new JFileChooser(); 232 | jfc.setMultiSelectionEnabled(false); 233 | jfc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); 234 | int result = jfc.showOpenDialog(instance); 235 | if (result == JFileChooser.APPROVE_OPTION) { 236 | textField.setText(jfc.getSelectedFile().getAbsolutePath()); 237 | } 238 | } 239 | }); 240 | 241 | this.add(superBox); 242 | 243 | this.setTitle("Install AutoSwitch"); 244 | this.pack(); 245 | this.setDefaultCloseOperation(EXIT_ON_CLOSE); 246 | 247 | Dimension size = Toolkit.getDefaultToolkit().getScreenSize(); 248 | 249 | this.setLocation((size.width - getWidth()) / 2, 250 | (size.height - getHeight()) / 2); 251 | 252 | dialog = new JDialog(this); 253 | dialog.setLayout(new BorderLayout()); 254 | dialog.add(Box.createVerticalStrut(15), BorderLayout.NORTH); 255 | dialog.add(Box.createVerticalStrut(15), BorderLayout.SOUTH); 256 | dialog.add(Box.createHorizontalStrut(25), BorderLayout.WEST); 257 | dialog.add(Box.createHorizontalStrut(25), BorderLayout.EAST); 258 | dialog.add(new JLabel("Installing..."), BorderLayout.CENTER); 259 | dialog.setTitle("Installing"); 260 | dialog.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); 261 | dialog.pack(); 262 | dialog.setLocation(this.getX() + (this.getWidth() - dialog.getWidth()) 263 | / 2, this.getY() + (this.getHeight() - dialog.getHeight()) / 2); 264 | } 265 | 266 | public static String getThebombzenAPILatestVersion(String mcVersion) throws IOException { 267 | String latestVersion = null; 268 | BufferedReader br = null; 269 | try { 270 | URL versionURL = new URL( 271 | "https://thebombzen.com/ThebombzenAPI/release/Latest-" 272 | + mcVersion + ".txt"); 273 | br = new BufferedReader(new InputStreamReader( 274 | versionURL.openStream())); 275 | latestVersion = br.readLine(); 276 | latestVersion += String.format("%n%s%n", br.readLine()); 277 | } catch (MalformedURLException e) { 278 | throw new Error(); 279 | } finally { 280 | if (br != null){ 281 | br.close(); 282 | } 283 | } 284 | return latestVersion; 285 | } 286 | 287 | private void install() { 288 | try { 289 | install(textField.getText()); 290 | } catch (Exception e) { 291 | e.printStackTrace(); 292 | dialog.dispose(); 293 | JOptionPane.showMessageDialog(this, 294 | "Error installing. Install manually.", "Error Installing", 295 | JOptionPane.ERROR_MESSAGE); 296 | } 297 | System.exit(0); 298 | } 299 | 300 | private static void installNoGui(String directory) throws Exception { 301 | for (String mcVersion : Constants.INSTALL_MC_VERSIONS) { 302 | installNoGui(directory, mcVersion); 303 | } 304 | } 305 | 306 | private static void installNoGui(String directory, String mcVersion) throws Exception { 307 | File dir = new File(directory); 308 | if (!dir.isDirectory()) { 309 | System.err.println("Invalid directory: " + directory); 310 | return; 311 | } 312 | File modsFolder = new File(directory, "mods"); 313 | modsFolder.mkdir(); 314 | File versionFolder = new File(modsFolder, mcVersion); 315 | versionFolder.mkdir(); 316 | File file = new File(ASInstallerFrame.class.getProtectionDomain() 317 | .getCodeSource().getLocation().toURI()); 318 | JarFile jarFile = new JarFile(file); 319 | if (jarFile 320 | .getEntry("com/thebombzen/mods/autoswitch/installer/ASInstallerFrame.class") == null) { 321 | jarFile.close(); 322 | throw new Exception("Unable to open jar file!"); 323 | } 324 | jarFile.close(); 325 | installThebombzenAPI(directory); 326 | List testFiles = new ArrayList(); 327 | testFiles.addAll(Arrays.asList(modsFolder.listFiles())); 328 | testFiles.addAll(Arrays.asList(versionFolder.listFiles())); 329 | for (File testMod : testFiles) { 330 | if (testMod 331 | .getName() 332 | .matches( 333 | "^AutoSwitch(Mod)?-v\\d+\\.\\d+(\\.\\d+)?-mc(beta)?\\d+\\.\\d+(\\.\\d+)?\\.(jar|zip)$")) { 334 | testMod.delete(); 335 | } 336 | } 337 | copyFile(file, new File(versionFolder, file.getName())); 338 | } 339 | 340 | private void install(String directory) throws Exception { 341 | File dir = new File(directory); 342 | if (!dir.isDirectory()) { 343 | JOptionPane 344 | .showMessageDialog( 345 | this, 346 | "Something's wrong with the given folder. Check spelling and try again.", 347 | "Hmmm...", JOptionPane.ERROR_MESSAGE); 348 | return; 349 | } 350 | 351 | dialog.setVisible(true); 352 | installNoGui(directory); 353 | dialog.dispose(); 354 | JOptionPane.showMessageDialog(this, 355 | "Successfully installed AutoSwitch!", "Success!", 356 | JOptionPane.INFORMATION_MESSAGE); 357 | } 358 | 359 | public static void installThebombzenAPI(String directory) throws Exception { 360 | for (String mcVersion : Constants.INSTALL_MC_VERSIONS) { 361 | installThebombzenAPI(directory, mcVersion); 362 | } 363 | } 364 | 365 | public static void installThebombzenAPI(String directory, String mcVersion) throws Exception { 366 | String latest = getThebombzenAPILatestVersion(mcVersion); 367 | Scanner scanner = new Scanner(latest); 368 | scanner.useDelimiter(String.format("%n")); 369 | String fileName = scanner.next(); 370 | String url = scanner.next(); 371 | scanner.close(); 372 | 373 | File modsFolder = new File(directory, "mods"); 374 | modsFolder.mkdirs(); 375 | File versionFolder = new File(modsFolder, mcVersion); 376 | versionFolder.mkdirs(); 377 | 378 | File thebombzenAPI = new File(versionFolder, fileName); 379 | 380 | if (thebombzenAPI.exists()) { 381 | return; 382 | } 383 | 384 | URL downloadURL = new URL(url); 385 | 386 | File[] subFiles = modsFolder.listFiles(); 387 | File[] versionSubFiles = versionFolder.listFiles(); 388 | for (File file : subFiles) { 389 | if (file.getName() 390 | .matches( 391 | "^ThebombzenAPI-v\\d+\\.\\d+(\\.\\d+)?-mc\\d+\\.\\d+(\\.\\d+)?\\.(zip|jar)$")) { 392 | file.delete(); 393 | } 394 | } 395 | for (File file : versionSubFiles) { 396 | if (file.getName() 397 | .matches( 398 | "^ThebombzenAPI-v\\d+\\.\\d+(\\.\\d+)?-mc\\d+\\.\\d+(\\.\\d+)?\\.(zip|jar)$")) { 399 | file.delete(); 400 | } 401 | } 402 | 403 | FileOutputStream fos = new FileOutputStream(thebombzenAPI); 404 | ReadableByteChannel channel = Channels.newChannel(downloadURL 405 | .openStream()); 406 | fos.getChannel().transferFrom(channel, 0, Long.MAX_VALUE); 407 | channel.close(); 408 | fos.close(); 409 | } 410 | } 411 | -------------------------------------------------------------------------------- /resources/AutoSwitch_Overrides.txt: -------------------------------------------------------------------------------- 1 | # This is the AutoSwitch advanced config file 2 | # This allows you to override AutoSwitch's calculations 3 | # to completely customize its behavior 4 | 5 | # Basic options are not stored in this file 6 | # Basic options are obtained by going to the main menu of Minecraft 7 | # - Main Menu 8 | # - Go to Mods 9 | # - Scroll down and click AutoSwitch 10 | # - Click Config 11 | 12 | # If you need to change some things such as localization strings, 13 | # then you can open AutoSwitch.txt and change those settings 14 | 15 | # ########## GENERAL RULES ########### 16 | 17 | # Any errors in the config will be printed to .minecraft/mods/AutoSwitch/DEBUG.txt 18 | # Even if DEBUG is turned off. 19 | # An error on one line won't jeopardize your entire config file 20 | # It will just cause that one line to be ignored 21 | 22 | # Lines beginning with # are ignored 23 | # You can say whatever you want as long as it starts with a # 24 | 25 | # All numbers can be specified as normal numbers 26 | # but could also be specified as hexadecimal numbers by adding 0x to the front 27 | # or as binary numbers by adding 0b to the front. (e.g. 0b10001 = 0x11 = 17) 28 | 29 | # All numbers can contain underscores (_) 30 | # All numbers can have a - in front, to mean negative 31 | # All numbers can have a ~ in front, to indicate the one's complement or bitwise inverse 32 | # (~ is useful for masks) 33 | 34 | # Whitespace is irrelevant 35 | # The parser will strip away any whitespace before it parses the config line 36 | # This means that you can even put whitespace in the middle of a word 37 | 38 | # All config lines begin with a single character which indicates the command 39 | # R indicates the config Revision 40 | # T indicates a silk Touch override 41 | # F indicates a Fortune override 42 | # D indicates a Damageability override 43 | # H indicates a Harvestability override 44 | # S indicates a Standardness override 45 | # M indicates a tool selection Mode override 46 | # W indicates a Weapon damage override 47 | # Anything else will be ignored and an error will be printed to .minecraft/mods/AutoSwitch/DEBUG.txt 48 | 49 | 50 | # ########### CONFIG VERSION NUMBER ######### 51 | 52 | # If this is not found or does not match the current number, AutoSwitch will replace your config with the default one. 53 | R4 54 | 55 | # ===== HOW TO SPECIFY A BLOCK OR ITEM ===== 56 | 57 | # Use the block or item name 58 | # The same name you'd use in /give 59 | # For example, diamond_pickaxe 60 | 61 | # To specify a specific damage value, use a + 62 | # For example, an unbroken gold axe is gold_axe+0 63 | 64 | # To specify a specific block state, use +property:value 65 | # For example, birch planks are planks+variant:birch 66 | 67 | # For a more detailed description on how to specify blocks, see the bottom of the config file. 68 | 69 | # To specify all blocks or all items use a single @ instead of the name 70 | 71 | # To specify all similar blocks, material-wise, put a $ in front of the block name 72 | # For example, $planks means every block that's made out of a wooden material. 73 | 74 | # To specify all similar blocks or items, behavior-wise, put an @ in front of the block or item name 75 | # For example, @redstone_ore means both lit and unlit redstone ore 76 | # For example, @diamond_pickaxe means all pickaxes 77 | 78 | # If the above thing is too narrow, you can broaden the definition of similarity 79 | # To specify a broader similarity put [N] in front of the block or item name where N is some number 80 | # where bigger N means broader 81 | # (note that @name is a synonym for [0]name) 82 | # [0]log means all logs of the first type 83 | # [1]log means all logs 84 | # [2]log means all rotated pillars (like quartz pillars) 85 | # [3]log means all blocks 86 | # [0]diamond_pickaxe means all pickaxes 87 | # [1]diamond_pickaxe means all tools 88 | # [2]diamond_pickaxe means all items 89 | 90 | # ========= SILK TOUCH ============= 91 | 92 | # AutoSwitch normally calculates whether silk touch works on a block. 93 | # You don't need to add in all the silk touch blocks by hand. 94 | # If you want to override AutoSwitch's calculation then do it here: 95 | # Add T < block to tell AutoSwitch that silk touch doesn't work on that block. 96 | # Add T > block to tell AutoSwitch that silk touch does work on that block. 97 | # Also, you can use T << block to skip the silk touch calculation for that block. 98 | 99 | # Note: No always takes priority over yes. 100 | 101 | # This one tells AS to assume Silk Touch does not work on smooth stone. 102 | # Without it, AS will correctly calculate that silk touch works. 103 | T < stone 104 | 105 | # This one tells AS to assume Silk Touch does not work on gravel. 106 | # This line is necessary because silk touch does actually work on gravel: 107 | # - Without Silk Touch, gravel drops flint 1/10 of the time 108 | # - With Silk Touch, gravel never drops flint and always drops itself 109 | T < gravel 110 | 111 | # ========= FORTUNE ================= 112 | 113 | # AutoSwitch normally calculates whether fortune works on a block. 114 | # You don't need to add in all the fortune blocks by hand. 115 | # If you want to override AutoSwitch's calculation then do it here: 116 | # Add F < block to tell AutoSwitch that fortune doesn't work on that block. 117 | # Add F > block to tell AutoSwitch that fortune does work on that block. 118 | # Also, you can use F << block to skip the fortune calculation for that block. 119 | 120 | # Note: No always takes priority over yes. 121 | 122 | # This one tells AS that fortune works on crops 123 | F > @wheat 124 | 125 | # This one makes AS treat coal like a nonfortune block (remove the # to add) 126 | # F < coal_ore 127 | 128 | # This one tells AS to skip the fortune calculation on blocks with a wooden material 129 | # Otherwise it's kinda annoying using a fortune axe 130 | F << $log 131 | 132 | # ========= STANDARD TOOLS ========== 133 | 134 | # One of the core parts of AutoSwitch is determining how "standard" 135 | # a particular tool is against a particular block. 136 | 137 | # AutoSwitch calculates certain things, such as whether the tool can harvest the block, 138 | # how fast the tool digs the block, and if the tool is damageable against the block, 139 | # in order to determine how "standard" a tool is 140 | 141 | # If you want to override the harvestability calculation 142 | # or override the damageablility calculation 143 | # or override the standardness calculation as a whole then do it here 144 | 145 | # Use H tool > block to tell AutoSwitch that the tool can harvest the block 146 | # Use H tool < block to tell AutoSwitch that the tool cannot harvest the block 147 | # Use D tool > block to tell AutoSwitch that the tool can be damaged by digging the block 148 | # Use D tool < block to tell AutoSwitch that the tool cannot be damaged by digging the block 149 | # Use S tool > block to tell AutoSwitch that the tool is standard no matter what 150 | # Use S tool < block to tell AutoSwitch that the tool is not standard no matter what 151 | 152 | # Note: No always takes priority over yes. 153 | 154 | # This one tells AS that shears can harvest vines 155 | H shears > vine 156 | 157 | # This one tells AS that shears can harvest tall grass 158 | H shears > tallgrass 159 | 160 | # This one tells AS that hoes are standard on crops (for mcMMO) 161 | S @diamond_hoe > @wheat 162 | 163 | # This one tells AS that all fortune-enchanted items are standard on crops 164 | # (this way you'll use them instead of a hoe if you have both) 165 | S @+E35 > @wheat 166 | 167 | # Note that if you have both a fortune item and a hoe, AS will pick the fortune item 168 | # Because fortune works (as is overridden above) 169 | 170 | # This one tells AS that shovels are not standard tools against farmland 171 | # (this is so you don't dig up your farmland when harvesting crops) 172 | S @diamond_shovel < farmland 173 | 174 | # This one tells AS that shears are not damageable against blocks that 175 | # don't have the same material as vines, tallgrass, or leaves 176 | D shears < !($vine|$tallgrass|$leaves) 177 | 178 | # This one tells AS that tools within 4 hits of breaking are not standard against any block 179 | # (meaning you won't use them) 180 | # (remove the # to add) 181 | # S @+0:~0b11-0 < @ 182 | 183 | # ======== WEAPON OVERRIDES ========= 184 | 185 | # AutoSwitch normally calculates how much damage a weapon does 186 | # You can override this for specific mobs or in general 187 | 188 | # To override this in general add W item = damage 189 | # For example if your server plugin makes feathers do 10 damage then add 190 | # W feather = 10 191 | # To override this for specific mobs use network IDs, Savegame IDs, or Localized Names 192 | # That is, W item = ID = damage 193 | # To say that a diamond sword does only one damage against a pig use 194 | # W diamond_sword = 90 = 1 195 | # or 196 | # W diamond_sword = Pig = 1 197 | # (This is not true, but AS will think it is, provided you override it) 198 | 199 | # To create a "standardness" override 200 | # that is to always use a particular weapon against a particular mob 201 | # Use W item > ID to override a weapon as "standard" 202 | # Use W item < ID to override a weapon as "nonstandard" 203 | # For example, to avoid using a diamond sword on a pig, use 204 | # W diamond_sword < Pig 205 | 206 | # You can also combine network IDs with (), &, |, ^, and ! the same way you can for blocks and items 207 | # You can also use @ to mean all entities 208 | # You can mix and match ID types as well 209 | # 90|Zombie means "Pig or zombie" 210 | 211 | # Use silver swords on Mo' Creatures Werewolves 212 | W mocreatures:silversword > MoCreatures.Werewolf 213 | 214 | # ========== TOOL SELECTION MODES =========== 215 | 216 | # AutoSwitch has a "tool selection mode" option which allows you to set it to Fast Standard, Slow Standard, or Fast Nonstandard. 217 | # Fast Standard picks the best standard tool where faster is better 218 | # Slow Standard picks the best standard tool where slower is better 219 | # Fast Nonstandard skips the standardness calculation and you might use a faster tool that's worthless but is faster 220 | # (Slow Nonstandard doesn't exist because you'd always use a fist) 221 | 222 | # The tool selection mode is set in general in the config screen 223 | # Sometimes you want to override the mode in the config screen with different behavior on specific blocks 224 | # Use M block = mode to override behavior 225 | # For example you can use 226 | # M glowstone = Fast Nonstandard 227 | # to set AutoSwitch to use the Fast Nonstandard method on glowstone, even if something else is selected in the Config Screen. 228 | 229 | # ==== HOW TO SPECIFY A BLOCK OR ITEM (BEWARE: ULTRA-ADVANCED) ===== 230 | 231 | # You specify a block or item using a block/item identifier 232 | # Any block or item that matches the block/item identifier will count 233 | 234 | # A Simple Block/Item Identifier consists of an optional prefix, and optional modid, a mandatory* name, and 0 or more ValueSets. 235 | # The format is prefix modid:name ValueSet... 236 | # You can use spaces or no spaces; it doesn't matter because the parser strips all whitespace before parsing 237 | # The name can be an empty string but only if the prefix is universal; see prefixes for details 238 | 239 | # ########### modid:name ############ 240 | # The modid:name is the String name of the block or item. This is the one you would use in /give commands, etc. 241 | # e.g. Diamonds are minecraft:diamond 242 | # To figure out the name, look it up on the minecraft wiki 243 | # To figure out the name for modded blocks/items, type /give and where you would type in an item 244 | # hit tab and it will pop up a list of things that start with what you entered 245 | 246 | # All Vanilla names have a modid of minecraft; thus, all vanilla blocks/items will be minecraft:name where name is the name 247 | # As a result, if you leave out the modid: in front of the block, AS will assume you mean minecraft: 248 | # Thus, minecraft:diamond and diamond are the same 249 | 250 | # If the block or item comes from a mod, you need to use the mod's modid. 251 | # As in, ic2:wrench for an industrialcraft2 wrench 252 | # Just using wrench will not work because AS will assume minecraft:wrench, which doesn't exist 253 | 254 | # ########### prefix ########### 255 | 256 | # If the prefix is missing then the block/item identifier will only indicate blocks or items with the same name 257 | # as the one given in the identifier 258 | 259 | # *The prefix can be a universal prefix which is a single @ followed by an empty string in place of the modid:name 260 | # Just an @ by itself means all blocks or items (which one depends on context) 261 | 262 | # A $ in front of the name indicates material similarity (only works for blocks). 263 | # This means the block/item identifier indicates any block that has the 264 | # same material as the block whose name appears in the block/item identifier 265 | # For example, $log indicates any block whose material is the same as that of a log 266 | # This would mean all wooden blocks 267 | 268 | # A @ in front of the name indicates behavioral similarity. 269 | 270 | # (Programmer's info: A @name indicates all blocks whose Java class is a subclass 271 | # of the one indicated by name. So @wheat is all blocks whose class is a subclass of 272 | # BlockCrops.) 273 | 274 | # For example, @wheat indicates all crops, @diamond_pickaxe is all pickaxes, 275 | # and @redstone_ore is any redstone ore, not just unlit 276 | 277 | # The last kind of prefix is a number between brackets 278 | # e.g. [1]log 279 | # A number between brackets indicates wider behavioral similarity. For example, 280 | # @log still only means oak,spruce,birch,jungle 281 | # and @log2 means acacia,darkoak 282 | # Using [1]log gets both of them 283 | # and [2]log indicates all rotated pillar blocks 284 | # (Note that [0]name is the same as @name) 285 | # For example, [0]diamond_pickaxe means all pickaxes 286 | # but [1]diamond_pickaxe means all main tools 287 | # and [2]diamond_pickaxe is all items 288 | 289 | # (Programmer's info: [N]name grabs anything whose Java class is a subclass of super^N ( name.class ). 290 | # AutoSwitch will take N superclasses of the name's class before making a subclass comparison. ) 291 | 292 | # ################ Value Sets ############## 293 | 294 | # Value Sets filter the identifier to only indicate blocks or items with certain values 295 | # There are three types of value sets: 296 | # State value sets (blocks only), Damage value sets and Enchantment value sets (both are items only) 297 | 298 | # A damage value set restricts the damage values that the identifier identifies 299 | # The format is a + or - followed by a damage value 300 | # Or a + or - followed by a damage value and a mask 301 | # gold_axe+2 indicates only a golden axe with a damage value of 2. 302 | # gold_axe-2 indicates all golden axes with a damage value that's NOT 2. 303 | 304 | # You can also use a damage and a mask, as in damage:mask 305 | # AS will calculate (testvalue & mask) == damage 306 | # Rather than (testvalue == damage) 307 | # For example, gold_axe+0b1:0b1 308 | # This one tests for all golden axes with an odd damage value 309 | # (In case you just can't even) 310 | 311 | # If the mask is negative (obtained via - or ~) and the tool has a maximum damage, 312 | # AS will check the tool's damage based on how far away it is from breaking rather than its damage value 313 | # so @+0:~0b11 indicates all tools that are within 4 of breaking, or have 0 damage 314 | 315 | # The second kind of value set is an enchantment value set. 316 | # You specify these by putting an E in front of the first number, like in a damage value set 317 | 318 | # For example, fortune has enchantment ID 35. 319 | # To indicate diamond pickaxes with fortune enchantments, use 320 | # diamond_pickaxe+E35 321 | # To indicate diamond pickaxes without fortune enchantments, use 322 | # diamond_pickaxe-E35 323 | # To add a minimum level use Eid:minimumlevel 324 | # As in E32:2 means Efficiency, but only level 2+ 325 | # To add a maximum level then use two :s 326 | # As in, E32:2:4 means Efficiency, but only level 2-4 327 | # As usual, a + means add and a - means subtract 328 | 329 | # To do a block state value set, use property:value 330 | # planks+variant:birch is birch planks 331 | # leaves+decay:false gives leaves that won't naturally decay 332 | 333 | # One big thing about ValueSets is you can chain them together. Adding additional Value Sets that start with + will add to the list, 334 | # and adding additional value sets that start with a - with subtract from the list 335 | # For example, diamond_pickaxe+E35-0 means fortune enchanted diamond pickaxes, but not ones with a damage value of 0 (which is unbroken) 336 | # The precedence is right-to-left, so the ones furthest to the right have higher priority 337 | # Think of it as moving along the line left-to-right and adding or subtracting from the list you already have 338 | # One rule to know though: If the first Value Set is a +, it starts out with just that valueset 339 | # If the first ValueSet is a -, then it starts out with everything but that valueset 340 | # Meaning, planks+variant:oak means only oak planks, and planks-variant:oak means all nonoak planks 341 | # Then, after the first one, +s add to the list and -s subtract from the list 342 | # So diamond_pickaxe-0+0 is the same as all diamond pickaxes 343 | # and diamond_pickaxe+0-0 won't match anything 344 | # Note than you can combine enchantment and damage sets in one chain (but not states as those are block-only) 345 | 346 | # This finally explains our set @+0:~0b11-0, which means all tools within 4 of breaking 347 | # The @ signifies all blocks/items, in this case items 348 | # +0:~0b11 means a damage value of 0 and a mask of ~0b11 349 | # ~0b11 has every bit set except the last two, so when AS checks if the damage value matches 0, it ignores the last two bits 350 | # Thus anything within 4 counts 351 | # Also, AS checks the damage from breaking rather than the damage because ~0b11 is negative. 352 | # However, this will always indicate items with a 0 damage value as well, so we add -0 at the end to fix this problem. Thus, 353 | # @+0:~0b11-0 indicates all blocks within 4 of breaking. 354 | 355 | # ################ compound block/item identifiers ############# 356 | 357 | # All of the above information comprises a simple block/item identifier 358 | # Stuff like [1]minecraft:diamond_pickaxe+E32:2-0 359 | # are all a single block/item identifier 360 | 361 | # You can combine multiple simple block/item identifiers by using &, |, ^, !, and parentheses in their 362 | # traditional logical sense 363 | # E.g. lapis_ore|diamond_ore means "lapis ore, or diamond ore" 364 | # !lapis_ore means "not lapis ore" 365 | # and !(lapis_ore|diamond_ore) means "not (lapis ore or diamond ore)" 366 | # this is the same as !lapis_ore & !diamond_ore 367 | # The order of precedence is 368 | # () > ! > & > | > ^ 369 | # All of the binary operations are associative so associativity is irrelevant 370 | 371 | # This allows for some very complex rules 372 | # $stone&!(emerald_ore|diamond_ore|lapis_ore) 373 | # means any block that has a stone material and is not emerald ore, diamond ore, or lapis ore 374 | # meaning: 375 | # S diamond_pickaxe+E35:2 < $stone&!(emerald_ore|diamond_ore|lapis_ore) 376 | # This line tells AS not to use any diamond pickaxe with fortune level at least 2 on any stone-materialed block that isn't 377 | # diamond_ore, lapis_ore, or emerald_ore 378 | 379 | # Well, enjoy configuring AS! -------------------------------------------------------------------------------- /src/com/thebombzen/mods/autoswitch/Tests.java: -------------------------------------------------------------------------------- 1 | package com.thebombzen.mods.autoswitch; 2 | 3 | import java.lang.reflect.Field; 4 | import java.lang.reflect.Modifier; 5 | import java.util.Collections; 6 | import java.util.HashSet; 7 | import java.util.Iterator; 8 | import java.util.List; 9 | import java.util.Random; 10 | import java.util.Set; 11 | 12 | import com.thebombzen.mods.autoswitch.configuration.Configuration; 13 | import com.thebombzen.mods.thebombzenapi.ComparableTuple; 14 | import com.thebombzen.mods.thebombzenapi.FieldNotFoundException; 15 | import com.thebombzen.mods.thebombzenapi.ThebombzenAPI; 16 | 17 | import net.minecraft.block.Block; 18 | import net.minecraft.block.state.IBlockState; 19 | import net.minecraft.client.Minecraft; 20 | import net.minecraft.enchantment.Enchantment; 21 | import net.minecraft.enchantment.EnchantmentHelper; 22 | import net.minecraft.entity.Entity; 23 | import net.minecraft.entity.EntityLivingBase; 24 | import net.minecraft.entity.SharedMonsterAttributes; 25 | import net.minecraft.inventory.EntityEquipmentSlot; 26 | import net.minecraft.item.Item; 27 | import net.minecraft.item.ItemShears; 28 | import net.minecraft.item.ItemStack; 29 | import net.minecraft.item.ItemSword; 30 | import net.minecraft.nbt.NBTTagCompound; 31 | import net.minecraft.potion.Potion; 32 | import net.minecraft.util.NonNullList; 33 | import net.minecraft.util.ResourceLocation; 34 | import net.minecraft.util.math.BlockPos; 35 | import net.minecraft.util.math.MathHelper; 36 | import net.minecraft.world.World; 37 | import net.minecraftforge.common.ForgeHooks; 38 | import net.minecraftforge.common.IShearable; 39 | import net.minecraftforge.fml.common.Loader; 40 | import net.minecraftforge.fml.relauncher.Side; 41 | import net.minecraftforge.fml.relauncher.SideOnly; 42 | 43 | @SideOnly(Side.CLIENT) 44 | public final class Tests { 45 | 46 | private static final Minecraft mc = Minecraft.getMinecraft(); 47 | 48 | public static final ItemStack EMPTY_ITEMSTACK; 49 | 50 | private static ItemStack prevHeldItem; 51 | 52 | static { 53 | ItemStack tempEmpty; 54 | try { 55 | tempEmpty = ItemStack.EMPTY; 56 | } catch (NoSuchFieldError err) { 57 | tempEmpty = null; 58 | } 59 | EMPTY_ITEMSTACK = tempEmpty; 60 | prevHeldItem = EMPTY_ITEMSTACK; 61 | } 62 | 63 | private static Random prevRandom = null; 64 | 65 | private static final String[] randomNames = {"rand", "field_73012_v", "v"}; 66 | private static final String[] getSilkTouchDropNames = {"getSilkTouchDrop", "createStackedBlock", "func_180643_i", "i"}; 67 | 68 | /** 69 | * Anything strictly greater than this is considered to be "standard" 70 | */ 71 | public static final ComparableTuple standardThreshold = new ComparableTuple(0, 0); 72 | 73 | public static ItemStack getSilkTouchDrop(Block block, IBlockState state) { 74 | return ThebombzenAPI.invokePrivateMethod(block, Block.class, getSilkTouchDropNames, 75 | new Class[] { IBlockState.class }, state); 76 | } 77 | 78 | 79 | public static boolean doesFortuneWorkOnBlock(World world, BlockPos pos) { 80 | 81 | IBlockState blockState = world.getBlockState(pos); 82 | 83 | int state = AutoSwitch.instance.getConfiguration().getFortuneOverrideState(blockState); 84 | if (state == Configuration.OVERRIDDEN_NO){ 85 | return false; 86 | } else if (state == Configuration.OVERRIDDEN_YES){ 87 | return true; 88 | } 89 | 90 | Random maxRandom = new NotSoRandom(false); 91 | Random zeroRandom = new NotSoRandom(true); 92 | 93 | List defaultMaxRandom; 94 | List defaultZeroRandom; 95 | List fortuneMaxRandom; 96 | List fortuneZeroRandom; 97 | 98 | fakeRandomForWorld(world, maxRandom); 99 | defaultMaxRandom = getDrops(world, pos, 0); 100 | fortuneMaxRandom = getDrops(world, pos, 3); 101 | unFakeRandomForWorld(world); 102 | 103 | fakeRandomForWorld(world, zeroRandom); 104 | defaultZeroRandom = getDrops(world, pos, 0); 105 | fortuneZeroRandom = getDrops(world, pos, 3); 106 | unFakeRandomForWorld(world); 107 | 108 | if (!ThebombzenAPI.areItemStackCollectionsEqual(defaultMaxRandom, 109 | fortuneMaxRandom) 110 | || !ThebombzenAPI.areItemStackCollectionsEqual( 111 | defaultZeroRandom, fortuneZeroRandom)) { 112 | return true; 113 | } else { 114 | return false; 115 | } 116 | } 117 | 118 | public static boolean doesItemStackMimicSilk(World world, BlockPos pos, ItemStack itemstack) { 119 | 120 | IBlockState blockState = world.getBlockState(pos); 121 | 122 | if (blockState.getBlock() instanceof IShearable && !Tests.isItemStackEmpty(itemstack) && itemstack.getItem() instanceof ItemShears) { 123 | IShearable shearable = (IShearable) blockState.getBlock(); 124 | if (shearable.isShearable(itemstack, world, pos)) { 125 | List drops = shearable.onSheared(itemstack, world, pos, 0); 126 | if (ThebombzenAPI.areItemStackCollectionsEqual(drops, Collections.singletonList(Tests.getSilkTouchDrop(blockState.getBlock(), blockState)))) { 127 | return true; 128 | } 129 | } 130 | } 131 | 132 | return false; 133 | 134 | } 135 | 136 | @SuppressWarnings("deprecation") 137 | public static List getDrops(World world, BlockPos pos, int fortune){ 138 | IBlockState blockState = world.getBlockState(pos); 139 | try { 140 | NonNullList ret = NonNullList.create(); 141 | blockState.getBlock().getDrops(ret, world, pos, blockState, fortune); 142 | return ret; 143 | } catch (NoClassDefFoundError | NoSuchMethodError error) { 144 | return blockState.getBlock().getDrops(world, pos, blockState, fortune); 145 | } 146 | } 147 | 148 | public static boolean doesSilkTouchWorkOnBlock(World world, BlockPos pos) { 149 | 150 | IBlockState blockState = world.getBlockState(pos); 151 | 152 | int state = AutoSwitch.instance.getConfiguration().getSilkTouchOverrideState(blockState); 153 | 154 | if (state == Configuration.OVERRIDDEN_NO){ 155 | return false; 156 | } else if (state == Configuration.OVERRIDDEN_YES){ 157 | return true; 158 | } 159 | 160 | boolean silkHarvest = blockState.getBlock().canSilkHarvest(world, pos, blockState, mc.player); 161 | if (!silkHarvest){ 162 | return false; 163 | } 164 | 165 | Random zeroRandom = new NotSoRandom(true); 166 | Random maxRandom = new NotSoRandom(false); 167 | 168 | ItemStack stackedBlock = getSilkTouchDrop(blockState.getBlock(), blockState); 169 | List stackedBlockList = Collections.singletonList(stackedBlock); 170 | 171 | List defaultMaxRandom; 172 | List defaultZeroRandom; 173 | 174 | fakeRandomForWorld(world, maxRandom); 175 | defaultMaxRandom = getDrops(world, pos, 0); 176 | unFakeRandomForWorld(world); 177 | 178 | fakeRandomForWorld(world, zeroRandom); 179 | defaultZeroRandom = getDrops(world, pos, 0); 180 | unFakeRandomForWorld(world); 181 | 182 | if (!ThebombzenAPI.areItemStackCollectionsEqual(stackedBlockList, defaultMaxRandom) || !ThebombzenAPI.areItemStackCollectionsEqual(stackedBlockList, defaultZeroRandom)){ 183 | return true; 184 | } else { 185 | return false; 186 | } 187 | } 188 | 189 | private static void fakeItemForPlayer(ItemStack itemstack) { 190 | prevHeldItem = mc.player.inventory.getStackInSlot(mc.player.inventory.currentItem); 191 | mc.player.inventory.setInventorySlotContents(mc.player.inventory.currentItem, itemstack); 192 | if (!Tests.isItemStackEmpty(prevHeldItem)) { 193 | mc.player.getAttributeMap().removeAttributeModifiers(prevHeldItem.getAttributeModifiers(EntityEquipmentSlot.MAINHAND)); 194 | } 195 | if (!Tests.isItemStackEmpty(itemstack)) { 196 | mc.player.getAttributeMap().applyAttributeModifiers(itemstack.getAttributeModifiers(EntityEquipmentSlot.MAINHAND)); 197 | } 198 | } 199 | 200 | private static void fakeRandomForWorld(World world, final Random random) { 201 | prevRandom = world.rand; 202 | for (String name : randomNames) { 203 | try { 204 | Field field = World.class.getDeclaredField(name); 205 | field.setAccessible(true); 206 | try { 207 | Field modifiersField = Field.class.getDeclaredField("modifiers"); 208 | modifiersField.setAccessible(true); 209 | modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL); 210 | field.set(world, random); 211 | modifiersField.setInt(field, field.getModifiers() | Modifier.FINAL); 212 | return; 213 | } catch (Exception e) { 214 | throw new FieldNotFoundException("Error setting field", e); 215 | } 216 | } catch (NoSuchFieldException nsfe) { 217 | continue; 218 | } 219 | } 220 | } 221 | 222 | public static int getAdjustedBlockStr(double blockStr){ 223 | if (blockStr <= 0){ 224 | return Integer.MIN_VALUE; 225 | } else { 226 | return -MathHelper.ceil(1D / blockStr); 227 | } 228 | } 229 | 230 | public static float getBlockHardness(World world, BlockPos pos) { 231 | IBlockState blockState = world.getBlockState(pos); 232 | if (blockState == null) { 233 | return 0; 234 | } else { 235 | return blockState.getBlockHardness(world, pos); 236 | } 237 | } 238 | 239 | public static float getBlockStrength(ItemStack itemstack, World world, BlockPos pos) { 240 | IBlockState blockState = world.getBlockState(pos); 241 | fakeItemForPlayer(itemstack); 242 | float str = blockState.getPlayerRelativeBlockHardness(mc.player, world, pos); 243 | unFakeItemForPlayer(); 244 | return str; 245 | } 246 | 247 | public static float getEff(float str, ItemStack itemstack) { 248 | if (str <= 1.5F) { 249 | return str; 250 | } 251 | fakeItemForPlayer(itemstack); 252 | float effLevel = EnchantmentHelper.getEfficiencyModifier(mc.player); 253 | unFakeItemForPlayer(); 254 | if (effLevel == 0) { 255 | return str; 256 | } 257 | return str + effLevel * effLevel + 1; 258 | } 259 | 260 | public static int getHarvestLevel(ItemStack itemstack, World world, BlockPos pos) { 261 | IBlockState blockState = world.getBlockState(pos); 262 | int state = AutoSwitch.instance.getConfiguration().getHarvestOverrideState(itemstack, blockState); 263 | if (state == Configuration.OVERRIDDEN_NO){ 264 | return -2; 265 | } else if (state == Configuration.OVERRIDDEN_YES){ 266 | return 2; 267 | } 268 | fakeItemForPlayer(Tests.EMPTY_ITEMSTACK); 269 | boolean noTool = mc.player.canHarvestBlock(blockState); 270 | unFakeItemForPlayer(); 271 | if (noTool){ 272 | return 0; 273 | } 274 | fakeItemForPlayer(itemstack); 275 | boolean can = blockState.getBlock().canHarvestBlock(world, pos, mc.player); 276 | unFakeItemForPlayer(); 277 | return can ? 1 : -1; 278 | } 279 | 280 | public static int getFullTinkersConstructItemStackDamage(ItemStack stack, 281 | EntityLivingBase entity) throws ClassNotFoundException { 282 | NBTTagCompound tags = stack.getTagCompound(); 283 | NBTTagCompound toolTags = stack.getTagCompound().getCompoundTag( 284 | "InfiTool"); 285 | int damage = toolTags.getInteger("Attack"); 286 | boolean broken = toolTags.getBoolean("Broken"); 287 | int durability = tags.getCompoundTag("InfiTool").getInteger("Damage"); 288 | float stonebound = tags.getCompoundTag("InfiTool").getFloat("Shoddy"); 289 | float stoneboundDamage = (float) Math.log(durability / 72f + 1) * -2 290 | * stonebound; 291 | int earlyModDamage = 0; 292 | Iterable activeModifiers = ThebombzenAPI.getPrivateField(null, 293 | Class.forName("tconstruct.library.TConstructRegistry"), 294 | "activeModifiers"); 295 | Class activeToolModClass = Class 296 | .forName("tconstruct.library.ActiveToolMod"); 297 | Class toolCoreClass = Class 298 | .forName("tconstruct.library.tools.ToolCore"); 299 | Class[] attackModClasses = new Class[] { int.class, int.class, 300 | toolCoreClass, NBTTagCompound.class, NBTTagCompound.class, 301 | ItemStack.class, EntityLivingBase.class, Entity.class }; 302 | Item tool = stack.getItem(); 303 | for (Object activeToolMod : activeModifiers) { 304 | earlyModDamage = ThebombzenAPI.invokePrivateMethod(activeToolMod, 305 | activeToolModClass, "baseAttackDamage", attackModClasses, 306 | earlyModDamage, damage, tool, tags, toolTags, stack, 307 | mc.player, entity); 308 | } 309 | damage += earlyModDamage; 310 | if (mc.player.isPotionActive(Potion.getPotionFromResourceLocation("strength"))) { 311 | damage += 3 << mc.player.getActivePotionEffect(Potion.getPotionFromResourceLocation("strength")).getAmplifier(); 312 | } 313 | if (mc.player.isPotionActive(Potion.getPotionFromResourceLocation("weakness"))) { 314 | damage -= 2 << mc.player.getActivePotionEffect(Potion.getPotionFromResourceLocation("weakness")).getAmplifier(); 315 | } 316 | float enchantDamage = 0; 317 | if (entity instanceof EntityLivingBase) { 318 | enchantDamage = EnchantmentHelper.getModifierForCreature(stack, entity.getCreatureAttribute()); 319 | } 320 | damage += stoneboundDamage; 321 | if (damage < 1) { 322 | damage = 1; 323 | } 324 | if (mc.player.isSprinting()) { 325 | float lunge = ThebombzenAPI.invokePrivateMethod(tool, 326 | toolCoreClass, "chargeAttack", new Class[] {}); 327 | if (lunge > 1f) { 328 | damage *= lunge; 329 | } 330 | } 331 | int modDamage = 0; 332 | for (Object activeToolMod : activeModifiers) { 333 | modDamage = ThebombzenAPI.invokePrivateMethod(activeToolMod, 334 | activeToolModClass, "attackDamage", attackModClasses, 335 | modDamage, damage, tool, tags, toolTags, stack, 336 | mc.player, entity); 337 | } 338 | damage += modDamage; 339 | if (damage > 0 || enchantDamage > 0) { 340 | boolean criticalHit = mc.player.fallDistance > 0.0F 341 | && !mc.player.onGround && !mc.player.isOnLadder() 342 | && !mc.player.isInWater() 343 | && !mc.player.isPotionActive(Potion.getPotionFromResourceLocation("blindness")) 344 | && !mc.player.isRiding(); 345 | for (Object activeToolMod : activeModifiers) { 346 | if (ThebombzenAPI.invokePrivateMethod(activeToolMod, activeToolModClass, "doesCriticalHit", 347 | new Class[] { toolCoreClass, NBTTagCompound.class, NBTTagCompound.class, ItemStack.class, 348 | EntityLivingBase.class, Entity.class }, 349 | tool, tags, toolTags, stack, mc.player, entity)) { 350 | criticalHit = true; 351 | } 352 | } 353 | if (criticalHit) { 354 | damage *= 1.5F; // This is not actually accurate. It just makes 355 | // it fully objective. Also, this is how it is 356 | // in vanilla now. 357 | } 358 | damage += enchantDamage; 359 | float damageModifier = ThebombzenAPI.invokePrivateMethod(tool, 360 | toolCoreClass, "getDamageModifier", new Class[] {}); 361 | if (damageModifier != 1f) { 362 | damage *= damageModifier; 363 | } 364 | if (broken) { 365 | damage = 1; 366 | } 367 | return damage; 368 | } else { 369 | return 0; 370 | } 371 | } 372 | 373 | public static boolean isItemStackEmpty(ItemStack itemstack) { 374 | if (Tests.EMPTY_ITEMSTACK == null) { 375 | return itemstack == null; 376 | } else { 377 | return itemstack.isEmpty(); 378 | } 379 | } 380 | 381 | public static double getFullRegularItemStackDamage(ItemStack itemStack, 382 | EntityLivingBase entity) { 383 | fakeItemForPlayer(itemStack); 384 | double damage = AutoSwitch.instance.getConfiguration() 385 | .getCustomWeaponDamage(itemStack, entity); 386 | if (damage < 0) { 387 | damage = mc.player.getEntityAttribute( 388 | SharedMonsterAttributes.ATTACK_DAMAGE).getAttributeValue(); 389 | } 390 | // getEnchantmentModifierDamage or getEnchantmentModifierLiving 391 | double enchDamage = EnchantmentHelper.getModifierForCreature(itemStack, entity.getCreatureAttribute()); 392 | 393 | if (damage > 0.0D || enchDamage > 0.0D) { 394 | boolean critical = mc.player.fallDistance > 0.0F 395 | && !mc.player.onGround && !mc.player.isOnLadder() 396 | && !mc.player.isInWater() 397 | && !mc.player.isPotionActive(Potion.getPotionFromResourceLocation("blindness")) 398 | && !mc.player.isRiding(); 399 | 400 | if (critical && damage > 0) { 401 | 402 | damage *= 1.5D; 403 | } 404 | damage += enchDamage; 405 | } 406 | unFakeItemForPlayer(); 407 | return damage; 408 | } 409 | 410 | public static double getFullItemStackDamage(ItemStack itemStack, EntityLivingBase entity){ 411 | try { 412 | if (!Tests.isItemStackEmpty(itemStack) && itemStack.getTagCompound() != null && Loader.isModLoaded("TConstruct")){ 413 | Class clazz = Class.forName("tconstruct.library.tools.ToolCore"); 414 | if (clazz.isAssignableFrom(itemStack.getItem().getClass())){ 415 | return Tests.getFullTinkersConstructItemStackDamage(itemStack, entity); 416 | } 417 | } 418 | } catch (ClassNotFoundException e){ 419 | AutoSwitch.instance.throwException("Error in Tinkers Construct Compatability", e, false); 420 | } 421 | return Tests.getFullRegularItemStackDamage(itemStack, entity); 422 | } 423 | 424 | public static Set getNonstandardNondamageEnchantmentsOnBothStacks( 425 | ItemStack stack1, ItemStack stack2) { 426 | 427 | Set bothItemsEnchantments = new HashSet(); 428 | 429 | if (!Tests.isItemStackEmpty(stack1)) { 430 | bothItemsEnchantments.addAll(EnchantmentHelper.getEnchantments( 431 | stack1).keySet()); 432 | } 433 | if (!Tests.isItemStackEmpty(stack2)) { 434 | bothItemsEnchantments.addAll(EnchantmentHelper.getEnchantments( 435 | stack2).keySet()); 436 | } 437 | 438 | Iterator iterator = bothItemsEnchantments.iterator(); 439 | while (iterator.hasNext()) { 440 | Enchantment enchantment = iterator.next(); 441 | if (enchantment == null) { 442 | iterator.remove(); 443 | continue; 444 | } 445 | ResourceLocation location = Enchantment.REGISTRY.getNameForObject(enchantment); 446 | if (location == null || "minecraft".equals(location.getResourceDomain())) { 447 | iterator.remove(); 448 | } 449 | } 450 | 451 | return bothItemsEnchantments; 452 | } 453 | 454 | private static int getToolOverrideStandardness(ItemStack itemstack, World world, BlockPos pos) { 455 | IBlockState blockState = world.getBlockState(pos); 456 | int state = AutoSwitch.instance.getConfiguration().getStandardToolOverrideState(itemstack, blockState); 457 | if (state == Configuration.OVERRIDDEN_NO){ 458 | return -3; 459 | } else if (state == Configuration.OVERRIDDEN_YES){ 460 | return 3; 461 | } 462 | return 0; 463 | } 464 | 465 | public static ComparableTuple getToolStandardness(ItemStack itemstack, World world, BlockPos pos){ 466 | int override = getToolOverrideStandardness(itemstack, world, pos); 467 | int harvest = getHarvestLevel(itemstack, world, pos); 468 | return new ComparableTuple(override, harvest); 469 | } 470 | 471 | public static ComparableTuple getToolEffectiveness(ItemStack itemStack, World world, BlockPos pos){ 472 | int weakStrength = getToolSpeedEffectiveness(itemStack, world, pos); 473 | int forgeStandard = (!Tests.isItemStackEmpty(itemStack) && ForgeHooks.isToolEffective(world, pos, itemStack)) ? 1 : 0; 474 | return new ComparableTuple(weakStrength, forgeStandard); 475 | } 476 | 477 | public static ComparableTuple getToolDamageability(ItemStack itemStack, World world, BlockPos pos){ 478 | int damageable = Tests.isItemStackDamageableOnBlock(itemStack, world, pos) ? -1 : 0; 479 | return new ComparableTuple(damageable); 480 | } 481 | 482 | public static int getToolSpeedEffectiveness(ItemStack itemstack, World world, BlockPos pos){ 483 | IBlockState blockState = world.getBlockState(pos); 484 | float hardness = Tests.getBlockHardness(world, pos); 485 | if (hardness <= 0F){ 486 | return 0; 487 | } 488 | 489 | float blockStrForNull = Tests.getBlockStrength(Tests.EMPTY_ITEMSTACK, world, pos); 490 | fakeItemForPlayer(Tests.EMPTY_ITEMSTACK); 491 | boolean harvestable = blockState.getBlock().canHarvestBlock(world, pos, mc.player); 492 | unFakeItemForPlayer(); 493 | 494 | float blockStr = Tests.getBlockStrength(itemstack, world, pos); 495 | fakeItemForPlayer(itemstack); 496 | boolean harvest = blockState.getBlock().canHarvestBlock(world, pos, mc.player); 497 | unFakeItemForPlayer(); 498 | 499 | if (harvest && !harvestable){ 500 | blockStr *= 0.3F; 501 | } 502 | 503 | if (blockStr > blockStrForNull * 1.5F){ 504 | return 1; 505 | } else { 506 | return 0; 507 | } 508 | } 509 | 510 | public static boolean isItemStackDamageable(ItemStack itemstack) { 511 | return !Tests.isItemStackEmpty(itemstack) && itemstack.isItemStackDamageable(); 512 | } 513 | 514 | public static boolean isItemStackDamageableOnBlock(ItemStack itemstack, 515 | World world, BlockPos pos) { 516 | IBlockState blockState = world.getBlockState(pos); 517 | int state = AutoSwitch.instance.getConfiguration().getDamageableOverrideState(itemstack, blockState); 518 | if (state == Configuration.OVERRIDDEN_NO){ 519 | return false; 520 | } else if (state == Configuration.OVERRIDDEN_YES){ 521 | return true; 522 | } 523 | if (!isItemStackDamageable(itemstack)) { 524 | return false; 525 | } 526 | return getBlockHardness(world, pos) != 0.0F; 527 | } 528 | 529 | public static boolean isSword(ItemStack itemstack) { 530 | if (Tests.isItemStackEmpty(itemstack)){ 531 | return false; 532 | } 533 | if (itemstack.getItem() instanceof ItemSword){ 534 | return true; 535 | } 536 | String name = Item.REGISTRY.getNameForObject(itemstack.getItem()).toString(); 537 | if (name.endsWith("sword")){ 538 | return true; 539 | } 540 | return false; 541 | } 542 | 543 | private static void unFakeItemForPlayer() { 544 | ItemStack fakedStack = mc.player.inventory.getStackInSlot(mc.player.inventory.currentItem); 545 | mc.player.inventory.setInventorySlotContents(mc.player.inventory.currentItem, prevHeldItem); 546 | if (!Tests.isItemStackEmpty(fakedStack)) { 547 | mc.player.getAttributeMap().removeAttributeModifiers( 548 | fakedStack.getAttributeModifiers(EntityEquipmentSlot.MAINHAND)); 549 | } 550 | if (!Tests.isItemStackEmpty(prevHeldItem)) { 551 | mc.player.getAttributeMap().applyAttributeModifiers( 552 | prevHeldItem.getAttributeModifiers(EntityEquipmentSlot.MAINHAND)); 553 | } 554 | } 555 | 556 | private static void unFakeRandomForWorld(World world) { 557 | for (String name : randomNames) { 558 | try { 559 | Field field = World.class.getDeclaredField(name); 560 | field.setAccessible(true); 561 | try { 562 | Field modifiersField = Field.class.getDeclaredField("modifiers"); 563 | modifiersField.setAccessible(true); 564 | modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL); 565 | field.set(world, prevRandom); 566 | modifiersField.setInt(field, field.getModifiers() | Modifier.FINAL); 567 | return; 568 | } catch (Exception e) { 569 | throw new FieldNotFoundException("Error setting field", e); 570 | } 571 | } catch (NoSuchFieldException nsfe) { 572 | continue; 573 | } 574 | } 575 | prevRandom = null; 576 | } 577 | 578 | private Tests() { 579 | 580 | } 581 | } 582 | -------------------------------------------------------------------------------- /src/com/thebombzen/mods/autoswitch/configuration/Configuration.java: -------------------------------------------------------------------------------- 1 | package com.thebombzen.mods.autoswitch.configuration; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.File; 5 | import java.io.FileReader; 6 | import java.io.FileWriter; 7 | import java.io.IOException; 8 | import java.io.InputStreamReader; 9 | import java.util.HashMap; 10 | import java.util.HashSet; 11 | import java.util.Map; 12 | import java.util.Scanner; 13 | import java.util.Set; 14 | 15 | import org.lwjgl.input.Keyboard; 16 | 17 | import com.thebombzen.mods.autoswitch.AutoSwitch; 18 | import com.thebombzen.mods.thebombzenapi.ThebombzenAPI; 19 | import com.thebombzen.mods.thebombzenapi.configuration.ConfigFormatException; 20 | import com.thebombzen.mods.thebombzenapi.configuration.ConfigOption; 21 | import com.thebombzen.mods.thebombzenapi.configuration.SingleMultiBoolean; 22 | import com.thebombzen.mods.thebombzenapi.configuration.ThebombzenAPIConfiguration; 23 | 24 | import net.minecraft.block.state.IBlockState; 25 | import net.minecraft.entity.EntityLivingBase; 26 | import net.minecraft.item.ItemStack; 27 | import net.minecraftforge.fml.relauncher.Side; 28 | import net.minecraftforge.fml.relauncher.SideOnly; 29 | 30 | /** 31 | * This class oversees the configuration of AutoSwitch 32 | * This uses the basic properties as well as its own advanced configuration 33 | * @author thebombzen 34 | */ 35 | @SideOnly(Side.CLIENT) 36 | public class Configuration extends ThebombzenAPIConfiguration { 37 | 38 | /** 39 | * The current version of the configuration. 40 | * 0: AS 4.0.0-4.2.0 41 | * 1: AS 4.3.0 42 | * 2: AS 4.3.1-4.3.4 // I placed a big bug in the config. Had to bump the revision. 43 | * 3: AS 4.4.0+ // More config configurability! :D 44 | */ 45 | public static final int CONFIG_VERSION = 4; 46 | 47 | public static final int OVERRIDDEN_YES = 1; 48 | public static final int OVERRIDDEN_NO = -1; 49 | public static final int NOT_OVERRIDDEN = 0; 50 | 51 | public static final ConfigOption TOGGLE_KEY = new ConfigOption(Keyboard.getKeyIndex("F10"), "TOGGLE_KEY", "Toggle Key", 52 | "This key toggles AutoSwitch."); 53 | public static final ConfigOption PULSE_KEY = new ConfigOption(Keyboard.getKeyIndex("V"), "PULSE_KEY", "Pulse Key", 54 | "This key temporarily toggles", 55 | "AutoSwitch while it's held down."); 56 | public static final ConfigOption DEFAULT_ENABLED = new ConfigOption(0, true, "DEFAULT_ENABED", "Enabled by default", 57 | "This option determines whether to", 58 | "enable AutoSwitch on new worlds", 59 | "and on worlds AutoSwitch hasn't", 60 | "been used on before."); 61 | public static final ConfigOption TOOL_SELECTION_MODE = new ConfigOption("FAST STANDARD", new String[]{"Fast Standard", "Slow Standard", "Fast Nonstandard"},"TOOL_SELECTION_MODE", "Tool Selection Mode", 62 | "Fast Standard picks the best standard tool,", 63 | " where faster is better.", 64 | "Slow Standard picks the best standard tool,", 65 | " where slower is better.", 66 | "Fast Nonstandard picks the best tool,", 67 | " ignoring what's standard."); 68 | public static final ConfigOption BLOCKS = new ConfigOption(SingleMultiBoolean.ALWAYS, "BLOCKS", "Use on blocks", 69 | "Use AutoSwitch when digging blocks."); 70 | public static final ConfigOption MOBS = new ConfigOption(SingleMultiBoolean.ALWAYS, "MOBS", "Use on mobs", 71 | "Use AutoSwitch when attacking mobs."); 72 | public static final ConfigOption SWITCHBACK_BLOCKS = new ConfigOption(SingleMultiBoolean.ALWAYS, "SWITCHBACK_BLOCKS", "Unswitch on blocks", 73 | "Switch back after digging a block."); 74 | public static final ConfigOption SWITCHBACK_MOBS = new ConfigOption(SingleMultiBoolean.ALWAYS, "SWITCHBACK_MOBS", "Unswitch on mobs", 75 | "Switch back after attacking a mob."); 76 | public static final ConfigOption DEBUG = new ConfigOption(false, "DEBUG", "Debug Logging", 77 | "Log debug output to", 78 | ".minecraft/mods/AutoSwitch/DEBUG.txt"); 79 | public static final ConfigOption USE_IN_CREATIVE = new ConfigOption(true, "USE_IN_CREATIVE", "Use in creative", 80 | "Use AutoSwitch when in creative mode"); 81 | public static final ConfigOption TREEFELLER_COMPAT = new ConfigOption(false, "TREEFELLER_COMPAT", "Detect Tree Feller", 82 | "Automatically detect when", 83 | "mcMMO Tree Feller is activated", 84 | "and temporarily set the tool selection", 85 | "mode to SLOW STANDARD."); 86 | public static final ConfigOption TREEFELLER_READY_AXE = new ConfigOption("**YOU READY YOUR AXE**", "TREEFELLER_READY_AXE", "mcMMO Ready Axe", 87 | "When detecting tree feller,", 88 | "this says you've readied your axe.", 89 | "Some servers might have this in a different language."); 90 | public static final ConfigOption TREEFELLER_READY_OTHER = new ConfigOption("^\\*\\*YOU READY YOUR [A-Z]+\\*\\*$", "TREEFELLER_READY_OTHER", "mcMMO Ready Other", 91 | "When detecting tree feller,", 92 | "this says another tool is ready.", 93 | "This is a regular expression."); 94 | public static final ConfigOption TREEFELLER_LOWER_AXE = new ConfigOption("**YOU LOWER YOUR AXE**", "TREEFELLER_LOWER_AXE", "mcMMO Lower Axe", 95 | "When detecting tree feller,", 96 | "this says you've lowered your axe."); 97 | public static final ConfigOption TREEFELLER_WORNOFF = new ConfigOption("**Tree Feller has worn off**", "TREEFELLER_WORNOFF", "mcMMO Treefeller Worn Off", 98 | "When detecting tree feller,", 99 | "this says treefeller has worn off."); 100 | public static final ConfigOption TREEFELLER_AXE_SPLINTER = new ConfigOption("YOUR AXE SPLINTERS INTO DOZENS OF PIECES!", "TREEFELLER_AXE_SPLINTER", "mcMMO Axe Splinter", 101 | "When detecting tree feller,", 102 | "this says your axe has splintered."); 103 | public static final ConfigOption TREEFELLER_TOO_TIRED = new ConfigOption("^You are too tired to use that ability again\\. \\(\\d+s\\)$", "TREEFELLER_TOO_TIRED", "mcMMO Too Tired", 104 | "When detecting tree feller,", 105 | "this says you are too tired.", 106 | "This is a regular expression."); 107 | public static final ConfigOption TREEFELLER_SKULL_SPLITTER = new ConfigOption("**Skull Splitter ACTIVATED**", "TREEFELLER_SKULL_SPLITTER", "mcMMO Skull Splitter", 108 | "When detecting tree feller,", 109 | "this says you have used skull splitter", 110 | "instead of treefeller."); 111 | 112 | 113 | private static boolean doesSetContainBlock(Set set, IBlockState state){ 114 | for (BlockItemIdentifier test : set) { 115 | if (test.contains(state)){ 116 | return true; 117 | } 118 | } 119 | return false; 120 | } 121 | 122 | private static boolean doesSetContainToolAndBlock(Set set, ItemStack tool, IBlockState state){ 123 | for (BlockToolPair pair : set){ 124 | //AutoSwitch.instance.debug(pair.toString()); 125 | if (pair.getBlock().contains(state) && pair.getTool().contains(tool)){ 126 | return true; 127 | } 128 | } 129 | return false; 130 | } 131 | 132 | private static boolean doesSetContainEntityAndWeapon(Set set, ItemStack weapon, EntityLivingBase entity){ 133 | for (EntityWeaponPair pair : set){ 134 | //AutoSwitch.instance.debug(pair.toString()); 135 | if (pair.getEntity().contains(entity) && pair.getWeapon().contains(weapon)){ 136 | return true; 137 | } 138 | } 139 | return false; 140 | } 141 | 142 | private static int doesYesNoSetContainBlock(Set no, Set yes, IBlockState state){ 143 | if (doesSetContainBlock(no, state)){ 144 | return OVERRIDDEN_NO; 145 | } else if (doesSetContainBlock(yes, state)){ 146 | return OVERRIDDEN_YES; 147 | } else { 148 | return NOT_OVERRIDDEN; 149 | } 150 | } 151 | 152 | private static int doesYesNoSetContainToolAndBlock(Set no, Set yes, ItemStack tool, IBlockState state){ 153 | if (doesSetContainToolAndBlock(no, tool, state)){ 154 | return OVERRIDDEN_NO; 155 | } else if (doesSetContainToolAndBlock(yes, tool, state)){ 156 | return OVERRIDDEN_YES; 157 | } else { 158 | return NOT_OVERRIDDEN; 159 | } 160 | } 161 | 162 | private static int doesYesNoSetContainEntityAndWeapon(Set no, Set yes, ItemStack weapon, EntityLivingBase entity){ 163 | if (doesSetContainEntityAndWeapon(no, weapon, entity)){ 164 | return OVERRIDDEN_NO; 165 | } else if (doesSetContainEntityAndWeapon(yes, weapon, entity)){ 166 | return OVERRIDDEN_YES; 167 | } else { 168 | return NOT_OVERRIDDEN; 169 | } 170 | } 171 | 172 | private final String defaultConfig; 173 | 174 | private File extraConfigFile; 175 | private long extraConfigLastModified; 176 | private Set fortuneNoWorks = new HashSet(); 177 | private Set fortuneWorks = new HashSet(); 178 | private Set standardBlocksAndTools = new HashSet(); 179 | private Set notStandardBlocksAndTools = new HashSet(); 180 | private Set silkTouchNoWorks = new HashSet(); 181 | private Set silkTouchWorks = new HashSet(); 182 | private Set ignoreFortune = new HashSet(); 183 | private Set ignoreSilkTouch = new HashSet(); 184 | private Set fastStandardOverrides = new HashSet(); 185 | private Set slowStandardOverrides = new HashSet(); 186 | private Set fastNonStandardOverrides = new HashSet(); 187 | private Set harvestWorks = new HashSet(); 188 | private Set harvestNoWorks = new HashSet(); 189 | private Set damageableYes = new HashSet(); 190 | private Set damageableNo = new HashSet(); 191 | private Set standardWeapons = new HashSet(); 192 | private Set nonStandardWeapons = new HashSet(); 193 | private Map damageOverrides = new HashMap(); 194 | 195 | public Configuration(AutoSwitch autoSwitch) { 196 | super(autoSwitch); 197 | extraConfigFile = new File(ThebombzenAPI.sideSpecificUtilities.getMinecraftDirectory() + File.separator + "config" + File.separator + "AutoSwitch_Overrides.txt"); 198 | File oldExtraConfigFile = new File(extraConfigFile.getParentFile(), "AutoSwitch_Overrides.cfg"); 199 | StringBuilder builder = new StringBuilder(); 200 | try { 201 | BufferedReader reader = new BufferedReader(new InputStreamReader(ThebombzenAPI.getResourceAsStream(autoSwitch, "AutoSwitch_Overrides.txt"))); 202 | String line; 203 | while (null != (line = reader.readLine())){ 204 | builder.append(line).append(ThebombzenAPI.NEWLINE); 205 | } 206 | reader.close(); 207 | } catch (IOException ioe){ 208 | autoSwitch.throwException("Could not read default config!", ioe, true); 209 | } finally { 210 | defaultConfig = builder.toString(); 211 | } 212 | if (oldExtraConfigFile.exists()){ 213 | oldExtraConfigFile.delete(); 214 | } 215 | } 216 | 217 | @Override 218 | public ConfigOption[] getAllOptions() { 219 | return new ConfigOption[]{TOGGLE_KEY, PULSE_KEY, DEFAULT_ENABLED, TOOL_SELECTION_MODE, BLOCKS, MOBS, SWITCHBACK_BLOCKS, SWITCHBACK_MOBS, DEBUG, USE_IN_CREATIVE, TREEFELLER_COMPAT, TREEFELLER_READY_AXE, TREEFELLER_READY_OTHER, TREEFELLER_LOWER_AXE, TREEFELLER_WORNOFF, TREEFELLER_AXE_SPLINTER, TREEFELLER_TOO_TIRED, TREEFELLER_SKULL_SPLITTER}; 220 | } 221 | 222 | public int getCustomWeaponDamage(ItemStack itemstack, EntityLivingBase entityOver) { 223 | 224 | if (itemstack == null) { 225 | return -1; 226 | } 227 | 228 | for (EntityWeaponPair pair : damageOverrides.keySet()){ 229 | if (pair.getWeapon().contains(itemstack) && pair.getEntity().contains(entityOver)){ 230 | return damageOverrides.get(pair); 231 | } 232 | } 233 | 234 | return -1; 235 | } 236 | 237 | public int getDamageableOverrideState(ItemStack tool, IBlockState state){ 238 | return doesYesNoSetContainToolAndBlock(damageableNo, damageableYes, tool, state); 239 | } 240 | 241 | public ToolSelectionMode getDefaultToolSelectionMode(){ 242 | ToolSelectionMode mode = ToolSelectionMode.parse(getStringProperty(TOOL_SELECTION_MODE)); 243 | if (mode == null){ 244 | mode = ToolSelectionMode.FAST_STANDARD; 245 | setToolSelectionMode(mode); 246 | } 247 | return mode; 248 | } 249 | 250 | public File getExtraConfigFile() { 251 | return extraConfigFile; 252 | } 253 | 254 | public int getFortuneOverrideState(IBlockState state){ 255 | return doesYesNoSetContainBlock(fortuneNoWorks, fortuneWorks, state); 256 | } 257 | 258 | public int getHarvestOverrideState(ItemStack tool, IBlockState state){ 259 | return doesYesNoSetContainToolAndBlock(harvestNoWorks, harvestWorks, tool, state); 260 | } 261 | 262 | public int getSilkTouchOverrideState(IBlockState state){ 263 | return doesYesNoSetContainBlock(silkTouchNoWorks, silkTouchWorks, state); 264 | } 265 | 266 | public int getStandardToolOverrideState(ItemStack tool, IBlockState state){ 267 | return doesYesNoSetContainToolAndBlock(notStandardBlocksAndTools, standardBlocksAndTools, tool, state); 268 | } 269 | 270 | public int getWeaponOverrideState(ItemStack weapon, EntityLivingBase entity){ 271 | return doesYesNoSetContainEntityAndWeapon(nonStandardWeapons, standardWeapons, weapon, entity); 272 | } 273 | 274 | public ToolSelectionMode getToolSelectionMode(IBlockState state) { 275 | if (AutoSwitch.instance.isTreefellerOn() && getBooleanProperty(TREEFELLER_COMPAT)){ 276 | return ToolSelectionMode.SLOW_STANDARD; 277 | } 278 | if (this.isSlowStandardOverridden(state)){ 279 | return ToolSelectionMode.SLOW_STANDARD; 280 | } else if (this.isFastNonStandardOverridden(state)){ 281 | return ToolSelectionMode.FAST_NONSTANDARD; 282 | } else if (this.isFastStandardOverridden(state)){ 283 | return ToolSelectionMode.FAST_STANDARD; 284 | } 285 | return this.getDefaultToolSelectionMode(); 286 | } 287 | 288 | private boolean isFastNonStandardOverridden(IBlockState state){ 289 | return doesSetContainBlock(fastNonStandardOverrides, state); 290 | } 291 | 292 | private boolean isFastStandardOverridden(IBlockState state){ 293 | return doesSetContainBlock(fastStandardOverrides, state); 294 | } 295 | 296 | public boolean isSilkTouchOverriddenToWork(IBlockState state) { 297 | return doesSetContainBlock(silkTouchWorks, state); 298 | } 299 | 300 | private boolean isSlowStandardOverridden(IBlockState state){ 301 | return doesSetContainBlock(slowStandardOverrides, state); 302 | } 303 | 304 | @Override 305 | protected void loadProperties() throws IOException { 306 | super.loadProperties(); 307 | AutoSwitch.instance.setToggleKeyCode(DEFAULT_ENABLED.getDefaultToggleIndex(), getKeyCodeProperty(TOGGLE_KEY)); 308 | if (!extraConfigFile.exists()) { 309 | writeExtraConfig(); 310 | parseConfig(defaultConfig); 311 | return; 312 | } 313 | StringBuilder sb = new StringBuilder(); 314 | BufferedReader reader = new BufferedReader(new FileReader(extraConfigFile)); 315 | String s; 316 | while (null != (s = reader.readLine())) { 317 | sb.append(s).append(ThebombzenAPI.NEWLINE); 318 | } 319 | reader.close(); 320 | parseConfig(sb.toString()); 321 | extraConfigLastModified = getExtraConfigFile().lastModified(); 322 | } 323 | 324 | 325 | private void parseBlockToolPairOverride(String line, Set no, Set yes){ 326 | int indexGreaterThan = line.indexOf('>'); 327 | int indexLessThan = line.indexOf('<'); 328 | String toolSub = ""; 329 | String blockSub = ""; 330 | boolean plus = false; 331 | if (indexGreaterThan > 0 && indexLessThan < 0) { 332 | toolSub = line.substring(1, indexGreaterThan); 333 | blockSub = line.substring(indexGreaterThan + 1); 334 | plus = true; 335 | } else if (indexLessThan > 0 && indexGreaterThan < 0) { 336 | toolSub = line.substring(1, indexLessThan); 337 | blockSub = line.substring(indexLessThan + 1); 338 | plus = false; 339 | } else { 340 | AutoSwitch.instance.forceDebug("Error on line: %s", line); 341 | AutoSwitch.instance.forceDebug("Error caused by: Expected > or < but not both."); 342 | return; 343 | } 344 | try { 345 | BlockItemIdentifier block = BlockItemIdentifier.parseBlockItemIdentifier(blockSub); 346 | BlockItemIdentifier tool = BlockItemIdentifier.parseBlockItemIdentifier(toolSub); 347 | (plus ? yes : no).add(new BlockToolPair(block, tool)); 348 | } catch (ConfigFormatException e){ 349 | AutoSwitch.instance.forceDebug("Error on line: %s", line); 350 | AutoSwitch.instance.forceDebug("Error caused by: %s", e.toString()); 351 | AutoSwitch.instance.debugException(e); 352 | } 353 | } 354 | 355 | private void parseEntityWeaponPairOverride(String line, Set no, Set yes){ 356 | int indexGreaterThan = line.indexOf('>'); 357 | int indexLessThan = line.indexOf('<'); 358 | String weaponSub = ""; 359 | String entitySub = ""; 360 | boolean plus = false; 361 | if (indexGreaterThan > 0 && indexLessThan < 0) { 362 | weaponSub = line.substring(1, indexGreaterThan); 363 | entitySub = line.substring(indexGreaterThan + 1); 364 | plus = true; 365 | } else if (indexLessThan > 0 && indexGreaterThan < 0) { 366 | weaponSub = line.substring(1, indexLessThan); 367 | entitySub = line.substring(indexLessThan + 1); 368 | plus = false; 369 | } else { 370 | AutoSwitch.instance.forceDebug("Error on line: %s", line); 371 | AutoSwitch.instance.forceDebug("Error caused by: Expected > or < but not both."); 372 | return; 373 | } 374 | try { 375 | EntityIdentifier entity = EntityIdentifier.parseEntityIdentifier(entitySub); 376 | BlockItemIdentifier weapon = BlockItemIdentifier.parseBlockItemIdentifier(weaponSub); 377 | (plus ? yes : no).add(new EntityWeaponPair(entity, weapon)); 378 | } catch (ConfigFormatException e){ 379 | AutoSwitch.instance.forceDebug("Error on line: %s", line); 380 | AutoSwitch.instance.forceDebug("Error caused by: %s", e.toString()); 381 | AutoSwitch.instance.debugException(e); 382 | } 383 | } 384 | 385 | protected void parseConfig(String config) { 386 | fortuneNoWorks.clear(); 387 | fortuneWorks.clear(); 388 | notStandardBlocksAndTools.clear(); 389 | standardBlocksAndTools.clear(); 390 | silkTouchNoWorks.clear(); 391 | silkTouchWorks.clear(); 392 | damageOverrides.clear(); 393 | standardWeapons.clear(); 394 | nonStandardWeapons.clear(); 395 | ignoreFortune.clear(); 396 | ignoreSilkTouch.clear(); 397 | fastStandardOverrides.clear(); 398 | fastNonStandardOverrides.clear(); 399 | slowStandardOverrides.clear(); 400 | harvestWorks.clear(); 401 | harvestNoWorks.clear(); 402 | damageableYes.clear(); 403 | damageableNo.clear(); 404 | Scanner s = new Scanner(config); 405 | s.useDelimiter(ThebombzenAPI.NEWLINE); 406 | int version = -1; 407 | while (s.hasNext()) { 408 | String line = s.next(); 409 | int index = line.indexOf('#'); 410 | if (index >= 0) { 411 | line = line.substring(0, index); 412 | } 413 | line = line.replaceAll("\\s", ""); 414 | if (line.length() == 0) { 415 | continue; 416 | } else if (line.length() < 2) { 417 | AutoSwitch.instance.forceDebug("Error on line: %s", line); 418 | AutoSwitch.instance.forceDebug("Error caused by: Line is too short"); 419 | continue; 420 | } 421 | char first = line.charAt(0); 422 | switch (first) { 423 | case 'R': 424 | case 'r': 425 | String sub = line.substring(1); 426 | try { 427 | version = ThebombzenAPI.parseInteger(sub); 428 | } catch (NumberFormatException nfe) { 429 | version = -1; 430 | } 431 | if (version != CONFIG_VERSION) { 432 | try { 433 | writeExtraConfig(); 434 | } catch (IOException ioe) { 435 | mod.throwException("Could not write config file!", ioe, 436 | true); 437 | } 438 | parseConfig(defaultConfig); 439 | s.close(); 440 | return; 441 | } 442 | break; 443 | case 'T': 444 | case 't': 445 | char second = line.charAt(1); 446 | if (second == '>') { 447 | try { 448 | silkTouchWorks.add(BlockItemIdentifier.parseBlockItemIdentifier(line.substring(2))); 449 | } catch (ConfigFormatException e){ 450 | AutoSwitch.instance.forceDebug("Error on line: %s", line); 451 | AutoSwitch.instance.forceDebug("Error caused by: %s", e.toString()); 452 | AutoSwitch.instance.debugException(e); 453 | } 454 | } else if (second == '<') { 455 | try { 456 | if (line.length() < 3){ 457 | throw new ConfigFormatException("Line is too short"); 458 | } 459 | if (line.charAt(2) == '<'){ 460 | ignoreSilkTouch.add(BlockItemIdentifier.parseBlockItemIdentifier(line.substring(3))); 461 | } else { 462 | silkTouchNoWorks.add(BlockItemIdentifier.parseBlockItemIdentifier(line.substring(2))); 463 | } 464 | 465 | } catch (ConfigFormatException e){ 466 | AutoSwitch.instance.forceDebug("Error on line: %s", line); 467 | AutoSwitch.instance.forceDebug("Error caused by: %s", e.toString()); 468 | AutoSwitch.instance.debugException(e); 469 | } 470 | 471 | } else { 472 | AutoSwitch.instance.forceDebug("Error on line: %s", line); 473 | AutoSwitch.instance.forceDebug("Error caused by: Expected < or >. Found %c", second); 474 | continue; 475 | } 476 | break; 477 | case 'F': 478 | case 'f': 479 | second = line.charAt(1); 480 | if (second == '>') { 481 | try { 482 | fortuneWorks.add(BlockItemIdentifier.parseBlockItemIdentifier(line.substring(2))); 483 | } catch (ConfigFormatException e){ 484 | AutoSwitch.instance.forceDebug("Error on line: %s", line); 485 | AutoSwitch.instance.forceDebug("Error caused by: %s", e.toString()); 486 | } 487 | } else if (second == '<') { 488 | try { 489 | if (line.length() < 3){ 490 | throw new ConfigFormatException("Line is too short"); 491 | } 492 | if (line.charAt(2) == '<'){ 493 | ignoreFortune.add(BlockItemIdentifier.parseBlockItemIdentifier(line.substring(3))); 494 | } else { 495 | fortuneNoWorks.add(BlockItemIdentifier.parseBlockItemIdentifier(line.substring(2))); 496 | } 497 | } catch (ConfigFormatException e){ 498 | AutoSwitch.instance.forceDebug("Error on line: %s", line); 499 | AutoSwitch.instance.forceDebug("Error caused by: %s", e.toString()); 500 | AutoSwitch.instance.debugException(e); 501 | } 502 | } else { 503 | AutoSwitch.instance.forceDebug("Error on line: %s", line); 504 | AutoSwitch.instance.forceDebug("Error caused by: Expected < or >. Found %c", second); 505 | continue; 506 | } 507 | break; 508 | case 'S': 509 | case 's': 510 | this.parseBlockToolPairOverride(line, notStandardBlocksAndTools, standardBlocksAndTools); 511 | break; 512 | case 'H': 513 | case 'h': 514 | this.parseBlockToolPairOverride(line, harvestNoWorks, harvestWorks); 515 | break; 516 | case 'D': 517 | case 'd': 518 | this.parseBlockToolPairOverride(line, damageableNo, damageableYes); 519 | break; 520 | case 'M': 521 | case 'm': 522 | int indexE = line.indexOf('='); 523 | if (indexE < 0 || indexE >= line.length() - 1) { 524 | AutoSwitch.instance.forceDebug("Error on line: %s", line); 525 | AutoSwitch.instance.forceDebug("Error caused by: Expected = in middle of line."); 526 | continue; 527 | } 528 | String blockSub = line.substring(1, indexE); 529 | String typeSub = line.substring(indexE + 1).toLowerCase(); 530 | try { 531 | BlockItemIdentifier block = BlockItemIdentifier.parseBlockItemIdentifier(blockSub); 532 | Set setToAdd; 533 | if (typeSub.contains("fast")){ 534 | if (typeSub.contains("non")){ 535 | setToAdd = fastNonStandardOverrides; 536 | } else { 537 | setToAdd = fastStandardOverrides; 538 | } 539 | } else if (typeSub.contains("slow")){ 540 | setToAdd = slowStandardOverrides; 541 | } else { 542 | throw new ConfigFormatException("Invalid Tool Selection Mode."); 543 | } 544 | setToAdd.add(block); 545 | } catch (ConfigFormatException e){ 546 | AutoSwitch.instance.forceDebug("Error on line: %s", line); 547 | AutoSwitch.instance.forceDebug("Error caused by: %s", e.toString()); 548 | AutoSwitch.instance.debugException(e); 549 | continue; 550 | } 551 | break; 552 | case 'W': 553 | case 'w': 554 | indexE = line.lastIndexOf('='); 555 | int indexE1 = line.indexOf('='); 556 | int indexP = line.indexOf('>'); 557 | int indexM = line.indexOf('<'); 558 | boolean foundE = !(indexE < 0 || indexE >= line.length() - 1); 559 | boolean foundP = !(indexP < 0 || indexP >= line.length() - 1); 560 | boolean foundM = !(indexM < 0 || indexM >= line.length() - 1); 561 | if (!foundE && !foundP && !foundM) { 562 | AutoSwitch.instance.forceDebug("Error on line: %s", line); 563 | AutoSwitch.instance.forceDebug("Error caused by: Expected =, >, or < in middle of line."); 564 | continue; 565 | } else if (foundP && foundM || foundE && foundM || foundE && foundP) { 566 | AutoSwitch.instance.forceDebug("Error on line: %s", line); 567 | AutoSwitch.instance.forceDebug("Error caused by: Found two symbols among =, >, and < on line."); 568 | continue; 569 | } 570 | EntityIdentifier id = null; 571 | String damageString; 572 | if (foundE){ 573 | if (indexE1 != indexE){ 574 | sub = line.substring(1, indexE1); 575 | try { 576 | id = EntityIdentifier.parseEntityIdentifier(line.substring(indexE1 + 1, indexE)); 577 | } catch (ConfigFormatException e){ 578 | AutoSwitch.instance.forceDebug("Error on line: %s", line); 579 | AutoSwitch.instance.forceDebug("Error caused by: %s", e.toString()); 580 | AutoSwitch.instance.debugException(e); 581 | continue; 582 | } 583 | damageString = line.substring(indexE + 1); 584 | } else { 585 | sub = line.substring(indexE); 586 | try { 587 | id = EntityIdentifier.parseEntityIdentifier("@"); 588 | } catch (ConfigFormatException e) { 589 | AutoSwitch.instance.throwException("Fatal error: parsing.", e, true); 590 | } 591 | damageString = line.substring(indexE + 1); 592 | } 593 | Integer damage = null; 594 | try { 595 | damage = ThebombzenAPI.parseInteger(damageString); 596 | } catch (NumberFormatException nfe) { 597 | AutoSwitch.instance.forceDebug("Error on line: %s", line); 598 | AutoSwitch.instance.forceDebug("Error caused by: Invalid Number: %s", nfe.toString()); 599 | continue; 600 | } 601 | try { 602 | BlockItemIdentifier weapon = BlockItemIdentifier.parseBlockItemIdentifier(sub); 603 | damageOverrides.put(new EntityWeaponPair(id, weapon), damage); 604 | } catch (ConfigFormatException e){ 605 | AutoSwitch.instance.forceDebug("Error on line: %s", line); 606 | AutoSwitch.instance.forceDebug("Error caused by: %s", e.toString()); 607 | AutoSwitch.instance.debugException(e); 608 | } 609 | } else { 610 | parseEntityWeaponPairOverride(line, nonStandardWeapons, standardWeapons); 611 | } 612 | break; 613 | default: 614 | AutoSwitch.instance.forceDebug("Error on line: %s", line); 615 | AutoSwitch.instance.forceDebug("Error caused by: Unrecognized first character."); 616 | } 617 | } 618 | if (version != CONFIG_VERSION) { 619 | try { 620 | writeExtraConfig(); 621 | } catch (IOException ioe) { 622 | mod.throwException("Could not write config file!", ioe, true); 623 | } finally { 624 | parseConfig(defaultConfig); 625 | } 626 | } 627 | s.close(); 628 | } 629 | 630 | @Override 631 | protected void setPropertyWithoutSave(ConfigOption option, 632 | String value) { 633 | super.setPropertyWithoutSave(option, value); 634 | if (option.equals(TOGGLE_KEY)) { 635 | mod.setToggleKeyCode(DEFAULT_ENABLED.getDefaultToggleIndex(), ThebombzenAPI.getExtendedKeyIndex(value)); 636 | } 637 | } 638 | 639 | public void setToolSelectionMode(ToolSelectionMode mode) { 640 | setProperty(TOOL_SELECTION_MODE, mode.toString()); 641 | } 642 | 643 | public boolean shouldIgnoreFortune(IBlockState state){ 644 | return doesSetContainBlock(ignoreFortune, state); 645 | } 646 | 647 | public boolean shouldIgnoreSilkTouch(IBlockState state){ 648 | return doesSetContainBlock(ignoreSilkTouch, state); 649 | } 650 | 651 | @Override 652 | public boolean shouldRefreshConfig() { 653 | if (super.shouldRefreshConfig()) { 654 | return true; 655 | } 656 | if (extraConfigLastModified != getExtraConfigFile().lastModified()) { 657 | return true; 658 | } else { 659 | return false; 660 | } 661 | } 662 | 663 | private void writeExtraConfig() throws IOException { 664 | FileWriter writer = new FileWriter(extraConfigFile); 665 | writer.write(defaultConfig); 666 | writer.flush(); 667 | writer.close(); 668 | } 669 | 670 | } 671 | -------------------------------------------------------------------------------- /src/com/thebombzen/mods/autoswitch/AutoSwitch.java: -------------------------------------------------------------------------------- 1 | package com.thebombzen.mods.autoswitch; 2 | 3 | import java.util.Arrays; 4 | import java.util.Set; 5 | 6 | import com.thebombzen.mods.autoswitch.configuration.Configuration; 7 | import com.thebombzen.mods.autoswitch.configuration.ToolSelectionMode; 8 | import com.thebombzen.mods.thebombzenapi.ComparableTuple; 9 | import com.thebombzen.mods.thebombzenapi.ThebombzenAPI; 10 | import com.thebombzen.mods.thebombzenapi.ThebombzenAPIBaseMod; 11 | 12 | import net.minecraft.block.Block; 13 | import net.minecraft.block.state.IBlockState; 14 | import net.minecraft.client.Minecraft; 15 | import net.minecraft.enchantment.Enchantment; 16 | import net.minecraft.enchantment.EnchantmentHelper; 17 | import net.minecraft.entity.EntityLivingBase; 18 | import net.minecraft.entity.player.EntityPlayer; 19 | import net.minecraft.init.Enchantments; 20 | import net.minecraft.item.Item; 21 | import net.minecraft.item.ItemStack; 22 | import net.minecraft.util.EnumHand; 23 | import net.minecraft.util.ResourceLocation; 24 | import net.minecraft.util.math.BlockPos; 25 | import net.minecraft.util.math.MathHelper; 26 | import net.minecraft.util.math.RayTraceResult; 27 | import net.minecraft.world.World; 28 | import net.minecraftforge.client.event.ClientChatReceivedEvent; 29 | import net.minecraftforge.common.MinecraftForge; 30 | import net.minecraftforge.event.entity.player.AttackEntityEvent; 31 | import net.minecraftforge.fml.common.FMLCommonHandler; 32 | import net.minecraftforge.fml.common.Mod; 33 | import net.minecraftforge.fml.common.Mod.Instance; 34 | import net.minecraftforge.fml.common.event.FMLPreInitializationEvent; 35 | import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; 36 | import net.minecraftforge.fml.common.gameevent.TickEvent.ClientTickEvent; 37 | import net.minecraftforge.fml.common.gameevent.TickEvent.Phase; 38 | import net.minecraftforge.fml.relauncher.Side; 39 | import net.minecraftforge.fml.relauncher.SideOnly; 40 | 41 | /** 42 | * The main AutoSwitch mod 43 | * 44 | * @author thebombzen 45 | */ 46 | @SideOnly(Side.CLIENT) 47 | @Mod(modid = "autoswitch", name = "AutoSwitch", version = Constants.VERSION, dependencies = "required-after:thebombzenapi", guiFactory = "com.thebombzen.mods.autoswitch.configuration.ConfigGuiFactory", clientSideOnly = true, acceptedMinecraftVersions = "[1.9.4, 1.12.2]") 48 | public class AutoSwitch extends ThebombzenAPIBaseMod { 49 | 50 | /** 51 | * This is the default stage for TDAI. Used under normal conditions. 52 | */ 53 | public static final int STAGE_H0 = 0; 54 | /** 55 | * This is the TDAI stage after AS has switched but before the event has been canceled. 56 | */ 57 | public static final int STAGE_SWITCHED = 1; 58 | /** 59 | * This is the TDAI stage after AS has canceled the attack event. 60 | */ 61 | public static final int STAGE_CANCELED = 2; 62 | 63 | /** 64 | * A convenience field storing the current Minecraft object 65 | */ 66 | public static final Minecraft mc = Minecraft.getMinecraft(); 67 | 68 | /** 69 | * The current TDAI attack stage 70 | */ 71 | private int entityAttackStage = STAGE_H0; 72 | 73 | /** 74 | * The current TDAI entity we're tracking 75 | */ 76 | private EntityLivingBase entitySwitchedOn = null; 77 | 78 | /** 79 | * The ThebombzenAPIConfiguration for AS 80 | */ 81 | private Configuration configuration; 82 | 83 | /** 84 | * Was the mouse down last tick? 85 | */ 86 | private boolean prevMouseDown = false; 87 | 88 | /** 89 | * Was the pulse key down last tick? 90 | */ 91 | private boolean prevPulse = false; 92 | 93 | /** 94 | * This slot index contained the previous tool before switching 95 | */ 96 | private int prevtool = 0; 97 | 98 | /** 99 | * Is the pulse key down? 100 | */ 101 | private boolean pulseOn = false; 102 | 103 | /** 104 | * Should we switch back after switching? 105 | */ 106 | private boolean switchback = false; 107 | 108 | /** 109 | * Is mcMMO treefeller currently being readied? 110 | */ 111 | private boolean treefellerOn = false; 112 | 113 | /** 114 | * The instance field for AS 115 | */ 116 | @Instance("autoswitch") 117 | public static AutoSwitch instance; 118 | 119 | /** 120 | * Used to detect if treefeller has been readied. 121 | * If treefeller is activated and DETECT_TREEFELLER is on, then AS will switch to Slow Standard in the meantime. 122 | * @param event The event containing the content of the chat message 123 | */ 124 | @SubscribeEvent 125 | public void clientChat(ClientChatReceivedEvent event){ 126 | String text = event.getMessage().getUnformattedText(); 127 | if (text.equals(configuration.getStringProperty(Configuration.TREEFELLER_READY_AXE))){ 128 | treefellerOn = true; 129 | } else if (text.matches(configuration.getStringProperty(Configuration.TREEFELLER_READY_OTHER))) { 130 | treefellerOn = false; 131 | } else if (text.equals(configuration.getStringProperty(Configuration.TREEFELLER_LOWER_AXE))){ 132 | treefellerOn = false; 133 | } else if (text.equals(configuration.getStringProperty(Configuration.TREEFELLER_WORNOFF))){ 134 | treefellerOn = false; 135 | } else if (text.equals(configuration.getStringProperty(Configuration.TREEFELLER_AXE_SPLINTER))){ 136 | treefellerOn = false; 137 | } else if (text.matches(configuration.getStringProperty(Configuration.TREEFELLER_TOO_TIRED))){ 138 | treefellerOn = false; 139 | } else if (text.equals(configuration.getStringProperty(Configuration.TREEFELLER_SKULL_SPLITTER))){ 140 | treefellerOn = false; 141 | } 142 | } 143 | 144 | /** 145 | * This is our main client tick loop, used to run most AS functions, such as switching. 146 | */ 147 | @SubscribeEvent 148 | public void clientTick(ClientTickEvent event) { 149 | if (!event.phase.equals(Phase.START)) { 150 | return; 151 | } 152 | 153 | if (mc.world == null) { 154 | return; 155 | } 156 | 157 | if (ThebombzenAPI.hasWorldChanged()){ 158 | treefellerOn = false; 159 | } 160 | 161 | // If we canceled the attack last tick, then go ahead and attack now. 162 | if (entityAttackStage == STAGE_CANCELED) { 163 | mc.player.swingArm(EnumHand.MAIN_HAND);; 164 | mc.playerController.attackEntity(mc.player, entitySwitchedOn); 165 | entityAttackStage = STAGE_H0; 166 | entitySwitchedOn = null; 167 | return; 168 | } 169 | 170 | pulseOn = ThebombzenAPI.isExtendedKeyDown(configuration.getKeyCodeProperty(Configuration.PULSE_KEY)); 171 | boolean mouseDown = ThebombzenAPI.isExtendedKeyDown(mc.gameSettings.keyBindAttack.getKeyCode()); 172 | if (!mouseDown && prevMouseDown || mouseDown && pulseOn ^ prevPulse) { 173 | switchBack(); 174 | } 175 | if (mouseDown && !prevMouseDown || mouseDown && pulseOn ^ prevPulse) { 176 | prevtool = mc.player.inventory.currentItem; 177 | } 178 | if (mouseDown) { 179 | if (mc.objectMouseOver != null 180 | && mc.objectMouseOver.typeOfHit == RayTraceResult.Type.BLOCK) { 181 | potentiallySwitchTools(mc.world, mc.objectMouseOver.getBlockPos()); 182 | } else if (mc.objectMouseOver != null 183 | && mc.objectMouseOver.typeOfHit == RayTraceResult.Type.ENTITY 184 | && mc.objectMouseOver.entityHit instanceof EntityLivingBase) { 185 | potentiallySwitchWeapons((EntityLivingBase) mc.objectMouseOver.entityHit); 186 | } 187 | } 188 | prevMouseDown = mouseDown; 189 | prevPulse = pulseOn; 190 | } 191 | 192 | /** 193 | * Send a string to the debug logger, but only if debugging is enabled. 194 | * @param string The debug message to send 195 | */ 196 | public void debug(String string) { 197 | debug("%s", string); 198 | } 199 | 200 | /** 201 | * Send a string to the debug logger, but only if debugging is enabled. 202 | * Uses the same notation as String.format() or PrintStream.format() 203 | * @param format A printf-style format string 204 | * @param args The arguments for the format string 205 | */ 206 | public void debug(String format, Object... args) { 207 | if (configuration.getBooleanProperty(Configuration.DEBUG)) { 208 | forceDebug(format, args); 209 | } 210 | } 211 | 212 | /** 213 | * Print an exception to the debug logger, but only if debugging is enabled. 214 | * Prints a short info message, as well as the stack trace. 215 | * @param exception the Throwable to print 216 | */ 217 | public void debugException(Throwable exception){ 218 | if (configuration.getBooleanProperty(Configuration.DEBUG)){ 219 | forceDebugException(exception); 220 | } 221 | } 222 | 223 | @SuppressWarnings("unchecked") 224 | @Override 225 | public Configuration getConfiguration() { 226 | return configuration; 227 | } 228 | 229 | @Override 230 | public String getDownloadLocationURLString() { 231 | return "https://thebombzen.com/AutoSwitch/thread/"; 232 | } 233 | 234 | @Override 235 | public String getLongName() { 236 | return "AutoSwitch"; 237 | } 238 | 239 | @Override 240 | public String getLongVersionString() { 241 | return "AutoSwitch, version " + Constants.VERSION + ", Minecraft " + Constants.SUPPORTED_MC_VERSIONS; 242 | } 243 | 244 | @Override 245 | public int getNumToggleKeys() { 246 | return 1; 247 | } 248 | 249 | @Override 250 | public String getShortName() { 251 | return "AS"; 252 | } 253 | 254 | @Override 255 | protected String getToggleMessageString(int index, boolean enabled) { 256 | if (enabled) { 257 | return "AutoSwitch is now enabled."; 258 | } else { 259 | return "AutoSwitch is now disabled."; 260 | } 261 | } 262 | 263 | @Override 264 | protected String getVersionFileURLString() { 265 | return "https://thebombzen.com/" + this.getLongName() + "/release/" + this.getShortName() + "Version" + ( ThebombzenAPI.getCheckAllMinecraftVersions() ? "" : "-" + MinecraftForge.MC_VERSION ) + ".txt"; 266 | } 267 | 268 | @Override 269 | public void init1(FMLPreInitializationEvent event) { 270 | MinecraftForge.EVENT_BUS.register(this); 271 | configuration = new Configuration(this); 272 | FMLCommonHandler.instance().findContainerFor(this).getMetadata().authorList = Arrays.asList("Thebombzen"); 273 | } 274 | 275 | /** 276 | * Compares two tools using the AS Algorithm 277 | * @param newItemStack The ItemStack containing the new tool 278 | * @param oldItemStack The ItemStack containing the old tool 279 | * @param world The relevant World object 280 | * @param pos The position of the block to test inside the World object 281 | * @return true if the new tool is better than the old one, false otherwise or if they are equal 282 | */ 283 | public boolean isToolBetter(ItemStack newItemStack, ItemStack oldItemStack, 284 | World world, BlockPos pos) { 285 | 286 | IBlockState blockState = world.getBlockState(pos); 287 | 288 | if (blockState.getBlock().isAir(blockState, world, pos)){ 289 | debug("Not switching because air."); 290 | return false; 291 | } 292 | 293 | int newAdjustedBlockStr = Tests.getAdjustedBlockStr(Tests.getBlockStrength(newItemStack, world, pos)); 294 | int oldAdjustedBlockStr = Tests.getAdjustedBlockStr(Tests.getBlockStrength(oldItemStack, world, pos)); 295 | 296 | if (newAdjustedBlockStr == Integer.MIN_VALUE && oldAdjustedBlockStr == Integer.MIN_VALUE) { 297 | debug("Not switching because block is unbreakable by either item."); 298 | return false; 299 | } 300 | 301 | float newBlockStr = Tests.getBlockStrength(newItemStack, world, pos); 302 | float oldBlockStr = Tests.getBlockStrength(oldItemStack, world, pos); 303 | 304 | debug("newAdjustedBlockStr: %d, oldAdjustedBlockStr: %d", newAdjustedBlockStr, oldAdjustedBlockStr); 305 | 306 | ToolSelectionMode toolSelectionMode = configuration.getToolSelectionMode(blockState); 307 | 308 | debug("Tool Selection Mode: %s", toolSelectionMode.toString()); 309 | 310 | ComparableTuple newStandard = Tests.getToolStandardness(newItemStack, world, pos); 311 | ComparableTuple oldStandard = Tests.getToolStandardness(oldItemStack, world, pos); 312 | ComparableTuple newEffectiveness = Tests.getToolEffectiveness(newItemStack, world, pos); 313 | ComparableTuple oldEffectiveness = Tests.getToolEffectiveness(oldItemStack, world, pos); 314 | 315 | debug("newStandard: %s, oldStandard: %s", newStandard.toString(), oldStandard.toString()); 316 | 317 | boolean newDamageable = Tests.isItemStackDamageableOnBlock( 318 | newItemStack, world, pos); 319 | boolean oldDamageable = Tests.isItemStackDamageableOnBlock( 320 | oldItemStack, world, pos); 321 | 322 | int adjustedBlockStrComparison = new Integer(newAdjustedBlockStr).compareTo(oldAdjustedBlockStr); 323 | int blockStrComparison = Float.compare(newBlockStr, oldBlockStr); 324 | int standardComparison = newStandard.compareTo(oldStandard); 325 | int effectivenessComparison = newEffectiveness.compareTo(oldEffectiveness); 326 | boolean isNewStandard = newStandard.compareTo(Tests.standardThreshold) > 0; 327 | boolean isOldStandard = oldStandard.compareTo(Tests.standardThreshold) > 0; 328 | 329 | if (toolSelectionMode.isStandard() || configuration.getStandardToolOverrideState(newItemStack, blockState) != configuration.getStandardToolOverrideState(oldItemStack, blockState)) { 330 | if (standardComparison > 0) { 331 | debug("Switching because new item is more standard than old."); 332 | return true; 333 | } else if (standardComparison < 0) { 334 | debug("Not switching because old item is more standard than new."); 335 | return false; 336 | } 337 | } else { 338 | if (toolSelectionMode.isFast()) { 339 | if (adjustedBlockStrComparison > 0) { 340 | debug("Switching because new tool is stronger."); 341 | return true; 342 | } else if (adjustedBlockStrComparison < 0) { 343 | debug("Not switching because old tool is stronger."); 344 | return false; 345 | } 346 | } else { // This should never happen. Slow nonstandard isn't a thing. 347 | debug("Something went wrong. It appears Slow Nonstandard is on."); 348 | } 349 | } 350 | 351 | boolean silkWorks = Tests.doesSilkTouchWorkOnBlock(world, pos); 352 | boolean newMimicsSilk = Tests.doesItemStackMimicSilk(world, pos, newItemStack); 353 | boolean oldMimicsSilk = Tests.doesItemStackMimicSilk(world, pos, oldItemStack); 354 | boolean newHasSilk = EnchantmentHelper.getEnchantmentLevel( 355 | Enchantments.SILK_TOUCH, newItemStack) > 0; 356 | boolean oldHasSilk = EnchantmentHelper.getEnchantmentLevel( 357 | Enchantments.SILK_TOUCH, oldItemStack) > 0; 358 | 359 | if (configuration.shouldIgnoreSilkTouch(blockState)){ 360 | debug("Ignoring Silk Touch."); 361 | } else { 362 | debug("silkWorks: %b, newHasSilk: %b, oldHasSilk: %b", silkWorks, 363 | newHasSilk, oldHasSilk); 364 | if (newHasSilk && !oldHasSilk) { 365 | if (silkWorks && !oldMimicsSilk) { 366 | debug("Switching because new has silk touch and old doesn't, and new works."); 367 | return true; 368 | } else { 369 | if (isOldStandard) { 370 | debug("Not switching because new has silk touch and old doesn't, and old replaces new."); 371 | return false; 372 | } else if (!isNewStandard) { 373 | debug("Not switching because new has silk touch and old doesn't, and new is weak."); 374 | return false; 375 | } 376 | } 377 | } else if (oldHasSilk && !newHasSilk) { 378 | if (silkWorks && !newMimicsSilk) { 379 | debug("Not switching because old has silk touch and new doesn't, and old works."); 380 | return false; 381 | } else { 382 | if (isNewStandard) { 383 | debug("Switching because old has silk touch and new doesn't, and new replaces old."); 384 | return true; 385 | } else if (!isOldStandard) { 386 | debug("Switching because old has silk touch and new doesn't, and old is weak."); 387 | return true; 388 | } 389 | } 390 | } 391 | } 392 | 393 | 394 | boolean fortuneWorks = Tests.doesFortuneWorkOnBlock(world, pos); 395 | int newFortuneLevel = EnchantmentHelper.getEnchantmentLevel( 396 | Enchantments.FORTUNE, newItemStack); 397 | int oldFortuneLevel = EnchantmentHelper.getEnchantmentLevel( 398 | Enchantments.FORTUNE, oldItemStack); 399 | 400 | if (configuration.shouldIgnoreFortune(blockState)){ 401 | debug("Ignoring Fortune."); 402 | } else { 403 | debug("fortuneWorks: %b, newFortuneLevel: %d, oldFortuneLevel: %d", 404 | fortuneWorks, newFortuneLevel, oldFortuneLevel); 405 | if (newFortuneLevel > oldFortuneLevel) { 406 | if (fortuneWorks) { 407 | debug("Switching because new fortune is more than old, and new works."); 408 | return true; 409 | } else { 410 | if (isOldStandard) { 411 | debug("Not switching because new fortune is more than old, and old replaces new."); 412 | return false; 413 | } else if (!isNewStandard) { 414 | debug("Not switching because new fortune is more than old, and new is weak."); 415 | return false; 416 | } 417 | } 418 | } else if (oldFortuneLevel > newFortuneLevel) { 419 | if (fortuneWorks) { 420 | debug("Not switching because old fortune is more than new, and old works."); 421 | return false; 422 | } else { 423 | if (isNewStandard) { 424 | debug("Switching because old fortune is more than new, and new replaces old."); 425 | return true; 426 | } else if (!isOldStandard) { 427 | debug("Switching because old fortune is more than new, and old is weak."); 428 | return true; 429 | } 430 | } 431 | } 432 | } 433 | 434 | if (toolSelectionMode.isStandard()) { 435 | if (effectivenessComparison > 0) { 436 | debug("Switching because new item is more effective than old."); 437 | return true; 438 | } else if (effectivenessComparison < 0) { 439 | debug("Not switching because old item is more effective than new."); 440 | return false; 441 | } 442 | } 443 | 444 | if (toolSelectionMode.isFast()) { 445 | if (adjustedBlockStrComparison > 0) { 446 | debug("Switching because new tool is stronger."); 447 | return true; 448 | } else if (adjustedBlockStrComparison < 0) { 449 | debug("Not switching because old tool is stronger."); 450 | return false; 451 | } 452 | } else { 453 | if (adjustedBlockStrComparison < 0) { 454 | debug("Switching because new tool is weaker."); 455 | return true; 456 | } else if (adjustedBlockStrComparison > 0) { 457 | debug("Not switching because old tool is weaker."); 458 | return false; 459 | } 460 | } 461 | 462 | Set bothItemsEnchantments = Tests 463 | .getNonstandardNondamageEnchantmentsOnBothStacks(newItemStack, 464 | oldItemStack); 465 | 466 | for (Enchantment enchantment : bothItemsEnchantments) { 467 | int oldLevel = EnchantmentHelper.getEnchantmentLevel( 468 | enchantment, oldItemStack); 469 | int newLevel = EnchantmentHelper.getEnchantmentLevel( 470 | enchantment, newItemStack); 471 | if (newLevel > oldLevel) { 472 | debug("Switching because new %s level, %d, is more than old, %d.", 473 | enchantment.getName(), newLevel, oldLevel); 474 | return true; 475 | } else if (newLevel < oldLevel) { 476 | debug("Switching because old %s level, %d, is more than new, %d.", 477 | enchantment.getName(), oldLevel, newLevel); 478 | return false; 479 | } 480 | } 481 | 482 | if (newDamageable && !oldDamageable) { 483 | debug("Not switching because new tool is damageable and old isn't."); 484 | return false; 485 | } else if (oldDamageable && !newDamageable) { 486 | debug("Switching because old tool is damageable and new isn't."); 487 | return true; 488 | } 489 | 490 | if (newDamageable && oldDamageable) { 491 | if (newFortuneLevel > oldFortuneLevel) { 492 | debug("Not switching because new fortune is bad and items are damageable."); 493 | return false; 494 | } else if (oldFortuneLevel > newFortuneLevel) { 495 | debug("Switching because old fortune is bad and items are damageable."); 496 | return true; 497 | } 498 | 499 | int newUnbreakingLevel = EnchantmentHelper.getEnchantmentLevel( 500 | Enchantments.UNBREAKING, newItemStack); 501 | int oldUnbreakingLevel = EnchantmentHelper.getEnchantmentLevel( 502 | Enchantments.UNBREAKING, oldItemStack); 503 | 504 | if (newUnbreakingLevel > oldUnbreakingLevel) { 505 | debug("Switching because new unbreaking is more than old unbreaking."); 506 | return true; 507 | } else if (oldUnbreakingLevel > newUnbreakingLevel) { 508 | debug("Not switching because old unbreaking is more than new unbreaking."); 509 | return false; 510 | } 511 | } 512 | 513 | if (toolSelectionMode.isFast() && !(newDamageable && oldDamageable)) { 514 | if (blockStrComparison > 0) { 515 | debug("Switching because new tool is stronger."); 516 | return true; 517 | } else if (blockStrComparison < 0) { 518 | debug("Not switching because old tool is stronger."); 519 | return false; 520 | } 521 | } else { 522 | if (blockStrComparison < 0) { 523 | debug("Switching because new item is worse than old item and Slow Standard is on."); 524 | return true; 525 | } else if (blockStrComparison > 0) { 526 | debug("Not switching because new item is better than old item and Slow Standard is on."); 527 | return false; 528 | } 529 | } 530 | 531 | debug("Not switching because tools are equal."); 532 | return false; 533 | 534 | } 535 | 536 | /** 537 | * Returns the current treefeller detection state. 538 | * @return true if AS has detected that treefeller is on, false otherwise 539 | */ 540 | public boolean isTreefellerOn(){ 541 | return treefellerOn; 542 | } 543 | 544 | /** 545 | * Compares two weapons using the AS Algorithm 546 | * @param newItemStack The ItemStack containing the new weapon 547 | * @param oldItemStack The ItemStack containing the old weapon 548 | * @param entityover The entity against which to test the weaposn 549 | * @return true if the new weapon is better, false otherwise or if they are equal 550 | */ 551 | public boolean isWeaponBetter(ItemStack newItemStack, 552 | ItemStack oldItemStack, EntityLivingBase entityover) { 553 | 554 | int oldState = configuration.getWeaponOverrideState(oldItemStack, entityover); 555 | int newState = configuration.getWeaponOverrideState(newItemStack, entityover); 556 | 557 | if (newState == Configuration.OVERRIDDEN_NO && oldState != Configuration.OVERRIDDEN_NO){ 558 | debug("Not switching because new is overridden no."); 559 | return false; 560 | } else if (newState != Configuration.OVERRIDDEN_NO && oldState == Configuration.OVERRIDDEN_NO){ 561 | debug("Switching because old is overridden no."); 562 | return true; 563 | } 564 | if (newState == Configuration.OVERRIDDEN_YES && oldState != Configuration.OVERRIDDEN_YES){ 565 | debug("Switching because new is ovverridden yes."); 566 | return true; 567 | } else if (oldState == Configuration.OVERRIDDEN_YES && newState != Configuration.OVERRIDDEN_YES){ 568 | debug("Not switching because old is overridden yes."); 569 | return false; 570 | } 571 | 572 | boolean isPlayer = entityover instanceof EntityPlayer; 573 | double oldDamage = Tests.getFullItemStackDamage(oldItemStack, entityover); 574 | double newDamage = Tests.getFullItemStackDamage(newItemStack, entityover); 575 | 576 | debug("Old damage is %f, new damage is %f.", oldDamage, newDamage); 577 | 578 | if (isPlayer) { 579 | if (newDamage > oldDamage) { 580 | debug("Switching because new damage is more."); 581 | return true; 582 | } else if (newDamage < oldDamage) { 583 | debug("Not switching because old damage is more."); 584 | return false; 585 | } 586 | } else { 587 | 588 | int oldHits; 589 | int newHits; 590 | 591 | if (oldDamage == 0) { 592 | oldHits = Integer.MAX_VALUE; 593 | } else { 594 | oldHits = MathHelper.ceil(entityover.getMaxHealth() 595 | / oldDamage); 596 | } 597 | 598 | if (newDamage == 0) { 599 | newHits = Integer.MAX_VALUE; 600 | } else { 601 | newHits = MathHelper.ceil(entityover.getMaxHealth() 602 | / newDamage); 603 | } 604 | 605 | debug("Old hits are %d, new hits are %d", oldHits, newHits); 606 | 607 | if (newHits < oldHits) { 608 | debug("Switching because new hits are fewer."); 609 | return true; 610 | } else if (newHits > oldHits) { 611 | debug("Not switching because old hits are fewer."); 612 | return false; 613 | } 614 | 615 | } 616 | 617 | int newLootingLevel = EnchantmentHelper.getEnchantmentLevel( 618 | Enchantments.LOOTING, newItemStack); 619 | int newFireAspectLevel = EnchantmentHelper.getEnchantmentLevel( 620 | Enchantments.FIRE_ASPECT, newItemStack); 621 | int newKnockbackLevel = EnchantmentHelper.getEnchantmentLevel( 622 | Enchantments.KNOCKBACK, newItemStack); 623 | int newUnbreakingLevel = EnchantmentHelper.getEnchantmentLevel( 624 | Enchantments.UNBREAKING, newItemStack); 625 | 626 | int oldLootingLevel = EnchantmentHelper.getEnchantmentLevel( 627 | Enchantments.LOOTING, oldItemStack); 628 | int oldFireAspectLevel = EnchantmentHelper.getEnchantmentLevel( 629 | Enchantments.FIRE_ASPECT, oldItemStack); 630 | int oldKnockbackLevel = EnchantmentHelper.getEnchantmentLevel( 631 | Enchantments.KNOCKBACK, oldItemStack); 632 | int oldUnbreakingLevel = EnchantmentHelper.getEnchantmentLevel( 633 | Enchantments.UNBREAKING, oldItemStack); 634 | 635 | if (!isPlayer) { 636 | if (newLootingLevel > oldLootingLevel) { 637 | debug("Switching because new looting, %d, is more than old, %d.", 638 | newLootingLevel, oldLootingLevel); 639 | return true; 640 | } else if (oldLootingLevel > newLootingLevel) { 641 | debug("Not switching because old looting, %d, is more than new, %d.", 642 | oldLootingLevel, newLootingLevel); 643 | return false; 644 | } 645 | } 646 | 647 | if (newFireAspectLevel > oldFireAspectLevel) { 648 | debug("Switching because new fire aspect, %d, is more than old, %d.", 649 | newFireAspectLevel, oldFireAspectLevel); 650 | return true; 651 | } else if (oldFireAspectLevel > newFireAspectLevel) { 652 | debug("Not switching because old fire aspect, %d, is more than new, %d.", 653 | oldFireAspectLevel, newFireAspectLevel); 654 | return false; 655 | } 656 | 657 | if (newKnockbackLevel > oldKnockbackLevel) { 658 | debug("Switching because new knockback, %d, is more than old, %d.", 659 | newKnockbackLevel, oldKnockbackLevel); 660 | return true; 661 | } else if (oldKnockbackLevel > newKnockbackLevel) { 662 | debug("Not switching because old knockback, %d, is more than new, %d.", 663 | oldKnockbackLevel, newKnockbackLevel); 664 | return false; 665 | } 666 | 667 | Set bothItemsEnchantments = Tests 668 | .getNonstandardNondamageEnchantmentsOnBothStacks(newItemStack, 669 | oldItemStack); 670 | 671 | for (Enchantment enchantment : bothItemsEnchantments) { 672 | int oldLevel = EnchantmentHelper.getEnchantmentLevel( 673 | enchantment, oldItemStack); 674 | int newLevel = EnchantmentHelper.getEnchantmentLevel( 675 | enchantment, newItemStack); 676 | if (newLevel > oldLevel) { 677 | debug("Switching because new %s level, %d, is more than old, %d.", 678 | enchantment.getName(), newLevel, oldLevel); 679 | return true; 680 | } else if (newLevel < oldLevel) { 681 | debug("Switching because old %s level, %d, is more than new, %d.", 682 | enchantment.getName(), oldLevel, newLevel); 683 | return false; 684 | } 685 | } 686 | 687 | if (Tests.isSword(newItemStack) && !Tests.isSword(oldItemStack)) { 688 | debug("Switching because new weapon is sword and old isn't."); 689 | return true; 690 | } 691 | if (Tests.isSword(oldItemStack) && !Tests.isSword(newItemStack)) { 692 | debug("Not switching because old weapon is sword and new isn't."); 693 | return false; 694 | } 695 | 696 | if (newDamage > oldDamage) { 697 | debug("Switching because new damage is more and all else is equal."); 698 | return true; 699 | } else if (newDamage < oldDamage) { 700 | debug("Not switching because old damage is more and all else is equal."); 701 | return false; 702 | } 703 | 704 | boolean newDamageable = Tests.isItemStackDamageable(newItemStack); 705 | boolean oldDamageable = Tests.isItemStackDamageable(oldItemStack); 706 | debug("newDamageable: %b, oldDamageable: %b", newDamageable, 707 | oldDamageable); 708 | 709 | if (newDamageable && !oldDamageable) { 710 | debug("Not switching because new weapon is damageable and old isn't."); 711 | return false; 712 | } 713 | 714 | if (oldDamageable && !newDamageable) { 715 | debug("Switching because new weapon is not damageable and old is."); 716 | return true; 717 | } 718 | 719 | if (newDamageable && oldDamageable 720 | && newUnbreakingLevel > oldUnbreakingLevel) { 721 | debug("Switching because new unbreaking, %d, is more than old, %d.", 722 | newUnbreakingLevel, oldUnbreakingLevel); 723 | return true; 724 | } else if (newDamageable && oldDamageable 725 | && oldUnbreakingLevel > newUnbreakingLevel) { 726 | debug("Not switching because old unbreaking, %d, is more than new, %d.", 727 | oldUnbreakingLevel, newUnbreakingLevel); 728 | return false; 729 | } 730 | 731 | if (Tests.isItemStackEmpty(newItemStack) && !Tests.isItemStackEmpty(oldItemStack)) { 732 | debug("Switching because new tool is fist and old is useless."); 733 | return true; 734 | } else if (Tests.isItemStackEmpty(oldItemStack) && !Tests.isItemStackEmpty(newItemStack)) { 735 | debug("Not switching because old tool is fist and new is useless."); 736 | return false; 737 | } 738 | 739 | debug("Not switching because weapons are equal."); 740 | return false; 741 | } 742 | 743 | /** 744 | * Initiates TDAI, or Tick-Delay Attack Interception. 745 | * Because of bug MC-28289, AutoSwitch will intercept any attack and then switch weapons. 746 | * It will cancel the attack and then wait exactly one tick to execute it. 747 | * This allows the new weapon's damage to register. 748 | */ 749 | @SubscribeEvent 750 | public void onEntityAttack(AttackEntityEvent event) { 751 | if (!event.getEntity().world.isRemote) { 752 | return; 753 | } 754 | if (entityAttackStage == STAGE_SWITCHED 755 | && entitySwitchedOn == event.getTarget()) { 756 | entityAttackStage = STAGE_CANCELED; 757 | event.setCanceled(true); 758 | } else if (entityAttackStage != STAGE_CANCELED) { 759 | entitySwitchedOn = null; 760 | entityAttackStage = STAGE_H0; 761 | } 762 | } 763 | 764 | /** 765 | * Switch tools as long as all criteria hold. 766 | * Criteria include various levels of enabled. 767 | * @param world The relevant World object 768 | * @param pos The position of the block within the World 769 | * @return true if switching actually happened error-free, false otherwise 770 | */ 771 | public boolean potentiallySwitchTools(World world, BlockPos pos) { 772 | if (pulseOn == isToggleEnabled(Configuration.DEFAULT_ENABLED.getDefaultToggleIndex()) 773 | || mc.playerController.isInCreativeMode() 774 | && !configuration 775 | .getBooleanProperty(Configuration.USE_IN_CREATIVE) 776 | || mc.currentScreen != null || !configuration.getSingleMultiProperty(Configuration.BLOCKS)) { 777 | return false; 778 | } 779 | debug("====START===="); 780 | debug(getLongVersionString()); 781 | try { 782 | switchToBestTool(mc.world, pos); 783 | return true; 784 | } catch (Throwable e) { 785 | throwException("Error switching tools", e, false); 786 | return false; 787 | } finally { 788 | debug("====END===="); 789 | } 790 | } 791 | 792 | /** 793 | * Switch weapons as long as all criteria hold. 794 | * Criteria include various levels of enabled. 795 | * Also changes the state of the TDAI. 796 | * @param entity The EntityLivingBase against which we're switching 797 | * @return true if switching actually happened error-free, false otherwise 798 | */ 799 | public boolean potentiallySwitchWeapons(EntityLivingBase entity) { 800 | if (pulseOn == isToggleEnabled(Configuration.DEFAULT_ENABLED.getDefaultToggleIndex()) 801 | || mc.playerController.isInCreativeMode() 802 | && !configuration 803 | .getBooleanProperty(Configuration.USE_IN_CREATIVE) 804 | || mc.currentScreen != null || !configuration.getSingleMultiProperty(Configuration.MOBS)) { 805 | return false; 806 | } 807 | debug("====START===="); 808 | debug(getLongVersionString()); 809 | debug("Switching on an entity, %s", entity.toString()); 810 | try { 811 | entitySwitchedOn = entity; 812 | entityAttackStage = STAGE_SWITCHED; 813 | switchToBestWeapon(mc.player, entity); 814 | return true; 815 | } catch (Throwable e) { 816 | throwException("Error switching weapons", e, false); 817 | return false; 818 | } finally { 819 | debug("====END===="); 820 | } 821 | } 822 | 823 | /** 824 | * Switch items back to the previous item, the one from before switching 825 | */ 826 | private void switchBack() { 827 | if (switchback) { 828 | mc.player.inventory.currentItem = prevtool; 829 | switchback = false; 830 | debug("Switching tools back to %d", prevtool); 831 | } 832 | } 833 | 834 | /** 835 | * Returns a UniqueIdentifier for a particular item, accordinate to GameData 836 | */ 837 | public static ResourceLocation findUniqueIdentifierFor(Item item){ 838 | return Item.REGISTRY.getNameForObject(item); 839 | } 840 | 841 | /** 842 | * Returns a UniqueIdentifier for a particular block, according to GameData 843 | */ 844 | public static ResourceLocation findUniqueIdentifierFor(Block block){ 845 | return Block.REGISTRY.getNameForObject(block); 846 | } 847 | 848 | /** 849 | * If switching actually happens, go ahead and switch to the best tool. 850 | * @param world The relevant World object 851 | * @param pos The location of the block against which we're switching 852 | */ 853 | private void switchToBestTool(World world, BlockPos pos) { 854 | 855 | IBlockState state = world.getBlockState(pos); 856 | ResourceLocation location = findUniqueIdentifierFor(state.getBlock()); 857 | 858 | String name = location.toString(); 859 | 860 | debug("Testing vs block %s", name); 861 | String[] names = new String[9]; 862 | for (int i = 0; i < 9; i++) { 863 | if (Tests.isItemStackEmpty(mc.player.inventory.getStackInSlot(i))) { 864 | names[i] = "Empty"; 865 | } else { 866 | ResourceLocation itemLocation = findUniqueIdentifierFor(mc.player.inventory.getStackInSlot(i).getItem()); 867 | names[i] = itemLocation.toString(); 868 | } 869 | debug("Hotbar slot %d contains item %s", i, names[i]); 870 | } 871 | 872 | int currentBest = prevtool; 873 | 874 | debug("Block hardness is %f", Tests.getBlockHardness(world, pos)); 875 | 876 | for (int i = 0; i < 9; i++) { 877 | 878 | if (i == currentBest) { 879 | continue; 880 | } 881 | 882 | debug("Checking if tool %d, which is %s, is better than %d, which is %s", 883 | i, names[i], currentBest, names[currentBest]); 884 | if (isToolBetter(mc.player.inventory.getStackInSlot(i), 885 | mc.player.inventory.getStackInSlot(currentBest), world, 886 | pos)) { 887 | debug("Changing possible best tool."); 888 | currentBest = i; 889 | } 890 | } 891 | debug("Current best is %d, which is %s", currentBest, 892 | names[currentBest]); 893 | switchToolsToN(currentBest); 894 | if (configuration.getSingleMultiProperty(Configuration.SWITCHBACK_BLOCKS)){ 895 | switchback = true; 896 | } else { 897 | prevtool = currentBest; 898 | } 899 | } 900 | 901 | /** 902 | * If switching actually happens, go ahead and switch to the best weapon. 903 | * @param entity The entity against which we're switching 904 | */ 905 | private void switchToBestWeapon(EntityPlayer entityplayer, 906 | EntityLivingBase entityover) { 907 | 908 | String[] names = new String[9]; 909 | for (int i = 0; i < 9; i++) { 910 | if (Tests.isItemStackEmpty(mc.player.inventory.getStackInSlot(i))) { 911 | names[i] = "Empty"; 912 | } else { 913 | ResourceLocation itemLocation = findUniqueIdentifierFor(mc.player.inventory.getStackInSlot(i) 914 | .getItem()); 915 | names[i] = itemLocation.toString(); 916 | } 917 | debug("Hotbar slot %d contains item %s", i, names[i]); 918 | } 919 | 920 | int currentBest = prevtool; 921 | 922 | debug("Current item is %d", entityplayer.inventory.currentItem); 923 | debug("Setting possible best weapon to %d, which is %s", currentBest, 924 | names[currentBest]); 925 | 926 | for (int i = 0; i < 9; i++) { 927 | debug("Checking if weapon %d, which is %s, is better than %d, which is %s", 928 | i, names[i], currentBest, names[currentBest]); 929 | if (isWeaponBetter(mc.player.inventory.getStackInSlot(i), mc.player.inventory.getStackInSlot(currentBest), entityover)) { 930 | debug("Changing possible best weapon because weapon is better."); 931 | currentBest = i; 932 | } 933 | } 934 | switchToolsToN(currentBest); 935 | if (configuration.getSingleMultiProperty(Configuration.SWITCHBACK_MOBS)){ 936 | switchback = true; 937 | } else { 938 | prevtool = currentBest; 939 | } 940 | } 941 | 942 | /** 943 | * Actually switch tools to a given slot, and do the necessary storage and debug routines 944 | * @param n The slot to which we must switch 945 | */ 946 | private void switchToolsToN(int n) { 947 | EntityPlayer entityplayer = mc.player; 948 | entityplayer.inventory.currentItem = n; 949 | String name; 950 | if (Tests.isItemStackEmpty(entityplayer.inventory.getStackInSlot(n))) { 951 | name = "Empty"; 952 | } else { 953 | ResourceLocation itemLocation = findUniqueIdentifierFor(entityplayer.inventory.getStackInSlot(n) 954 | .getItem()); 955 | name = itemLocation.toString(); 956 | } 957 | debug("Switching tools to %d, which is %s", n, name); 958 | } 959 | 960 | } 961 | --------------------------------------------------------------------------------