diff --git a/build.properties b/build.properties index a7527d4c..ad4f2b03 100644 --- a/build.properties +++ b/build.properties @@ -1,4 +1,4 @@ -minecraft_version=1.9.4 -forge_version=12.17.0.1968 +minecraft_version=1.10.2 +forge_version=12.18.3.2316 mod_version=1.1.1 mappings_version=snapshot_nodoc_20160519 \ No newline at end of file diff --git a/src/main/java/toughasnails/api/config/GameplayOption.java b/src/main/java/toughasnails/api/config/GameplayOption.java index 508e53de..224d6289 100644 --- a/src/main/java/toughasnails/api/config/GameplayOption.java +++ b/src/main/java/toughasnails/api/config/GameplayOption.java @@ -8,11 +8,36 @@ package toughasnails.api.config; public enum GameplayOption implements ISyncedOption { - ENABLE_LOWERED_STARTING_HEALTH("Enable Lowered Starting Health"), - ENABLE_THIRST("Enable Thirst"), - ENABLE_TEMPERATURE("Enable Body Temperature"), - ENABLE_SEASONS("Enable Seasons"), - DRINKS("Drinks"); + ENABLE_LOWERED_STARTING_HEALTH("Enable Lowered Starting Health"), // + ENABLE_THIRST("Enable Thirst"), // + ENABLE_TEMPERATURE("Enable Body Temperature"), // + ENABLE_SEASONS("Enable Seasons"), // + DRINKS("Drinks"), // + OVERRIDE_THERMOMETER_LIMITS("Override Thermometer Limits"), // + THERMOMETER_LOWER_BOUND("Thermometer Lower Bound"), // + THERMOMETER_UPPER_BOUND("Thermometer Upper Bound"), // + RAIN_CHILL("Enable Rain Chill on World Blocks"), // + BIOME_TEMP_MODIFIER("Biome Temperature Modification Scaling"), // + ALTITUDE_TEMP_MODIFIER("Altitude Temperature Modification Scaling"), // + WET_TEMP_MODIFIER("Temperature Modifier for being Wet"), // + SNOW_TEMP_MODIFIER("Temperature Modifier for Snow"), // + TIME_TEMP_MODIFIER("Time of Day Temperature Modification Scaling"), // + TIME_EXTREMITY_MODIFIER("Temperature Scaling for Time of Day Extremities"), // + EARLY_AUTUMN_MODIFIER("Temperature Modifier for the EARLY_AUTUMN Season"), // + MID_AUTUMN_MODIFIER("Temperature Modifier for the MID_AUTUMN Season"), // + LATE_AUTUMN_MODIFIER("Temperature Modifier for the LATE_AUTUMN Season"), // + EARLY_WINTER_MODIFIER("Temperature Modifier for the EARLY_WINTER Season"), // + MID_WINTER_MODIFIER("Temperature Modifier for the MID_WINTER Season"), // + LATE_WINTER_MODIFIER("Temperature Modifier for the LATE_WINTER Season"), // + EARLY_SPRING_MODIFIER("Temperature Modifier for the EARLY_SPRING Season"), // + MID_SPRING_MODIFIER("Temperature Modifier for the MID_SPRING Season"), // + LATE_SPRING_MODIFIER("Temperature Modifier for the LATE_SPRING Season"), // + EARLY_SUMMER_MODIFIER("Temperature Modifier for the EARLY_SUMMER Season"), // + MID_SUMMER_MODIFIER("Temperature Modifier for the MID_SUMMER Season"), // + LATE_SUMMER_MODIFIER("Temperature Modifier for the LATE_SUMMER Season"), // + TEMPERATURE_WITHERING("Crops Wither by Temperature"), // + HIBERNATING("Crops which Hibernate and don't Decay"), // + CROPS("Crops"); private final String optionName; diff --git a/src/main/java/toughasnails/api/config/SyncedConfig.java b/src/main/java/toughasnails/api/config/SyncedConfig.java index cb767945..e5f25a32 100644 --- a/src/main/java/toughasnails/api/config/SyncedConfig.java +++ b/src/main/java/toughasnails/api/config/SyncedConfig.java @@ -19,6 +19,14 @@ public static boolean getBooleanValue(ISyncedOption option) { return Boolean.valueOf(optionsToSync.get(option.getOptionName()).value); } + public static int getIntegerValue(ISyncedOption option) { + return Integer.valueOf(optionsToSync.get(option.getOptionName()).value); + } + + public static float getFloatValue(ISyncedOption option) { + return Float.valueOf(optionsToSync.get(option.getOptionName()).value); + } + public static List getListValue(ISyncedOption option) { SyncedConfigEntry value = optionsToSync.get(option.getOptionName()); String rawList = value.value; diff --git a/src/main/java/toughasnails/api/season/IHibernatingCrop.java b/src/main/java/toughasnails/api/season/IHibernatingCrop.java index 07d93f0b..95cda4ac 100644 --- a/src/main/java/toughasnails/api/season/IHibernatingCrop.java +++ b/src/main/java/toughasnails/api/season/IHibernatingCrop.java @@ -5,16 +5,20 @@ * * To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-nd/4.0/. ******************************************************************************/ + package toughasnails.api.season; /** * An interface which should be implemented by crops which become inactive in * the winter in the absence of proper heating. * - * Crops using this interface should make sure they use Forge's crop growth - * events and appropriately cancel the crop growth when the event result is set - * to DENY. (See net.minecraftforge.event.BlockEvent$CropGrowEvent or + * Crops using this interface in 1.10 should make sure they use Forge's crop + * growth events and appropriately cancel the crop growth when the event result + * is set to DENY. (See net.minecraftforge.event.BlockEvent$CropGrowEvent or * alternatively net.minecraftforge.common.ForgeHooks.onCropsGrowPre) + * + * Please note that due to how Java bytecode works, you must explicitly + * implement this interface if your class overrides updateTick. */ public interface IHibernatingCrop { diff --git a/src/main/java/toughasnails/asm/ASMHelper.java b/src/main/java/toughasnails/asm/ASMHelper.java index bf851efd..3382f9b8 100644 --- a/src/main/java/toughasnails/asm/ASMHelper.java +++ b/src/main/java/toughasnails/asm/ASMHelper.java @@ -25,96 +25,101 @@ import com.google.common.collect.Lists; -public class ASMHelper -{ - public static final Logger LOGGER = LogManager.getLogger("ToughAsNails Transformer"); - - public static boolean methodEquals(MethodNode methodNode, String[] names, String desc) - { - boolean nameMatches = false; - - for (String name : names) - { - if (methodNode.name.equals(name)) - { - nameMatches = true; - break; - } - } - - return nameMatches && methodNode.desc.equals(desc); - } - - public static void clearNextInstructions(MethodNode methodNode, AbstractInsnNode insnNode) - { - Iterator iterator = methodNode.instructions.iterator(methodNode.instructions.indexOf(insnNode)); - - while (iterator.hasNext()) - { - iterator.next(); - iterator.remove(); - } - } - - public static MethodInsnNode getUniqueMethodInsnNode(MethodNode methodNode, int opcode, String owner, String[] names, String desc) - { - List matchedMethodNodes = matchMethodInsnNodes(methodNode, opcode, owner, names, desc); - - if (matchedMethodNodes.isEmpty()) throw new RuntimeException("No method instruction node found matching " + owner + " " + names[0] + " " + desc); - if (matchedMethodNodes.size() > 1) LOGGER.warn("Too many matched instructions were found in " + methodNode.name + " for " + owner + " " + names[0] + " " + desc + ". Crashes or bugs may occur!"); - - return matchedMethodNodes.get(matchedMethodNodes.size() - 1); - } - - public static List matchMethodInsnNodes(MethodNode methodNode, int opcode, String owner, String[] names, String desc) - { - ArrayList matches = Lists.newArrayList(); - ArrayList validMethodNames = Lists.newArrayList(names); - - for (AbstractInsnNode insnNode : methodNode.instructions.toArray()) - { - if (insnNode instanceof MethodInsnNode && insnNode.getOpcode() == opcode) - { - MethodInsnNode methodInsnNode = (MethodInsnNode)insnNode; - - if (methodInsnNode.owner.equals(owner) & validMethodNames.contains(methodInsnNode.name) && methodInsnNode.desc.equals(desc)) - { - matches.add(methodInsnNode); - } - } - } - - return matches; - } - - public static void verifyClassHash(String className, byte[] bytes, String... expectedHashes) - { - String currentHash = DigestUtils.md5Hex(bytes); - - if (!Lists.newArrayList(expectedHashes).contains(currentHash)) - { - String error = String.format("Unexpected hash %s detected for class %s. Crashes or bugs may occur!", currentHash, className); - LOGGER.error(error); - } - else - { - LOGGER.info(String.format("Valid hash %s found for class %s.", currentHash, className)); - } - } - - private static Printer printer = new Textifier(); - private static TraceMethodVisitor methodVisitor = new TraceMethodVisitor(printer); - - public static void printMethod(MethodNode methodNode) - { - for (AbstractInsnNode insnNode : methodNode.instructions.toArray()) - { - insnNode.accept(methodVisitor); - StringWriter stringWriter = new StringWriter(); - printer.print(new PrintWriter(stringWriter)); - printer.getText().clear(); - - LOGGER.info(stringWriter.toString().replace("\n", "")); - } - } +public class ASMHelper { + public static final Logger LOGGER = LogManager + .getLogger("ToughAsNails Transformer"); + + public static boolean methodEquals(MethodNode methodNode, String[] names, + String desc) { + boolean nameMatches = false; + + for (String name : names) { + if (methodNode.name.equals(name)) { + nameMatches = true; + break; + } + } + + return nameMatches && methodNode.desc.equals(desc); + } + + public static void clearNextInstructions(MethodNode methodNode, + AbstractInsnNode insnNode) { + Iterator iterator = methodNode.instructions + .iterator(methodNode.instructions.indexOf(insnNode)); + + while (iterator.hasNext()) { + iterator.next(); + iterator.remove(); + } + } + + public static MethodInsnNode getUniqueMethodInsnNode(MethodNode methodNode, + int opcode, String owner, String[] names, String desc) { + List matchedMethodNodes = matchMethodInsnNodes( + methodNode, opcode, owner, names, desc); + + if (matchedMethodNodes.isEmpty()) + throw new RuntimeException( + "No method instruction node found matching " + owner + " " + + names[0] + " " + desc); + if (matchedMethodNodes.size() > 1) + LOGGER.warn("Too many matched instructions were found in " + + methodNode.name + " for " + owner + " " + names[0] + " " + + desc + ". Crashes or bugs may occur!"); + + return matchedMethodNodes.get(matchedMethodNodes.size() - 1); + } + + public static List matchMethodInsnNodes( + MethodNode methodNode, int opcode, String owner, String[] names, + String desc) { + ArrayList matches = Lists.newArrayList(); + ArrayList validMethodNames = Lists.newArrayList(names); + + for (AbstractInsnNode insnNode : methodNode.instructions.toArray()) { + if (insnNode instanceof MethodInsnNode + && insnNode.getOpcode() == opcode) { + MethodInsnNode methodInsnNode = (MethodInsnNode) insnNode; + + if (methodInsnNode.owner.equals(owner) + & validMethodNames.contains(methodInsnNode.name) + && methodInsnNode.desc.equals(desc)) { + matches.add(methodInsnNode); + } + } + } + + return matches; + } + + public static void verifyClassHash(String className, byte[] bytes, + String... expectedHashes) { + String currentHash = DigestUtils.md5Hex(bytes); + + if (!Lists.newArrayList(expectedHashes).contains(currentHash)) { + String error = String.format( + "Unexpected hash %s detected for class %s. Crashes or bugs may occur!", + currentHash, className); + LOGGER.error(error); + } else { + LOGGER.info(String.format("Valid hash %s found for class %s.", + currentHash, className)); + } + } + + private static Printer printer = new Textifier(); + private static TraceMethodVisitor methodVisitor = new TraceMethodVisitor( + printer); + + public static void printMethod(MethodNode methodNode) { + for (AbstractInsnNode insnNode : methodNode.instructions.toArray()) { + insnNode.accept(methodVisitor); + StringWriter stringWriter = new StringWriter(); + printer.print(new PrintWriter(stringWriter)); + printer.getText().clear(); + + LOGGER.info(stringWriter.toString().replace("\n", "")); + } + } } diff --git a/src/main/java/toughasnails/asm/ObfHelper.java b/src/main/java/toughasnails/asm/ObfHelper.java index e94ec51a..2da4727b 100644 --- a/src/main/java/toughasnails/asm/ObfHelper.java +++ b/src/main/java/toughasnails/asm/ObfHelper.java @@ -9,33 +9,32 @@ import net.minecraftforge.fml.common.asm.transformers.deobf.FMLDeobfuscatingRemapper; -public class ObfHelper -{ - public static String createMethodDescriptor(boolean obfuscated, String returnType, String... types) - { - String result = "("; - - for (String type : types) - { - if (type.length() == 1) result += type; - else - { - result += "L" + (obfuscated ? FMLDeobfuscatingRemapper.INSTANCE.unmap(type) : type) + ";"; - } - } - - if (returnType.length() > 1) - { - returnType = "L" + unmapType(obfuscated, returnType) + ";"; - } - - result += ")" + returnType; - - return result; - } - - public static String unmapType(boolean obfuscated, String type) - { - return obfuscated ? FMLDeobfuscatingRemapper.INSTANCE.unmap(type) : type; - } +public class ObfHelper { + public static String createMethodDescriptor(boolean obfuscated, + String returnType, String... types) { + String result = "("; + + for (String type : types) { + if (type.length() == 1) + result += type; + else { + result += "L" + (obfuscated + ? FMLDeobfuscatingRemapper.INSTANCE.unmap(type) : type) + + ";"; + } + } + + if (returnType.length() > 1) { + returnType = "L" + unmapType(obfuscated, returnType) + ";"; + } + + result += ")" + returnType; + + return result; + } + + public static String unmapType(boolean obfuscated, String type) { + return obfuscated ? FMLDeobfuscatingRemapper.INSTANCE.unmap(type) + : type; + } } diff --git a/src/main/java/toughasnails/asm/TANLoadingPlugin.java b/src/main/java/toughasnails/asm/TANLoadingPlugin.java index 3db8e686..808d7eff 100644 --- a/src/main/java/toughasnails/asm/TANLoadingPlugin.java +++ b/src/main/java/toughasnails/asm/TANLoadingPlugin.java @@ -11,41 +11,31 @@ import net.minecraftforge.fml.relauncher.IFMLLoadingPlugin; -public class TANLoadingPlugin implements IFMLLoadingPlugin -{ - @Override - public String[] getASMTransformerClass() - { - return new String[] { - "toughasnails.asm.transformer.BlockCropsTransformer", - "toughasnails.asm.transformer.BlockStemTransformer", - "toughasnails.asm.transformer.PamCropTransformer", - "toughasnails.asm.transformer.MysticalCropTransformer", - "toughasnails.asm.transformer.EntityRendererTransformer", - "toughasnails.asm.transformer.WorldTransformer" - }; - } +public class TANLoadingPlugin implements IFMLLoadingPlugin { + @Override + public String[] getASMTransformerClass() { + return new String[] { + "toughasnails.asm.transformer.CropDecayTransformer", + "toughasnails.asm.transformer.EntityRendererTransformer", + "toughasnails.asm.transformer.WorldTransformer" }; + } - @Override - public String getModContainerClass() - { - return null; - } + @Override + public String getModContainerClass() { + return null; + } - @Override - public String getSetupClass() - { - return null; - } + @Override + public String getSetupClass() { + return null; + } - @Override - public void injectData(Map data) - { - } + @Override + public void injectData(Map data) { + } - @Override - public String getAccessTransformerClass() - { - return null; - } + @Override + public String getAccessTransformerClass() { + return null; + } } diff --git a/src/main/java/toughasnails/asm/transformer/AbstractCropTransformer.java b/src/main/java/toughasnails/asm/transformer/AbstractCropTransformer.java deleted file mode 100644 index 949e11b7..00000000 --- a/src/main/java/toughasnails/asm/transformer/AbstractCropTransformer.java +++ /dev/null @@ -1,98 +0,0 @@ -package toughasnails.asm.transformer; - -import java.util.List; - -import org.objectweb.asm.ClassReader; -import org.objectweb.asm.ClassWriter; -import org.objectweb.asm.Opcodes; -import org.objectweb.asm.tree.ClassNode; -import org.objectweb.asm.tree.InsnList; -import org.objectweb.asm.tree.InsnNode; -import org.objectweb.asm.tree.JumpInsnNode; -import org.objectweb.asm.tree.LabelNode; -import org.objectweb.asm.tree.MethodInsnNode; -import org.objectweb.asm.tree.MethodNode; -import org.objectweb.asm.tree.VarInsnNode; - -import com.google.common.collect.Lists; - -import net.minecraft.launchwrapper.IClassTransformer; -import toughasnails.asm.ASMHelper; -import toughasnails.asm.ObfHelper; - -public abstract class AbstractCropTransformer implements IClassTransformer -{ - private static final String[] UPDATE_TICK_NAMES = new String[] { "updateTick", "func_180650_b", "b" }; - - public static enum WinterBehavior { - DECAY, - HIBERNATE; - } - - protected byte[] transformCrop(byte[] bytes, boolean obfuscatedClass, String shortClassName, String[] validHashes, WinterBehavior transformType) - { - //Decode the class from bytes - ClassNode classNode = new ClassNode(); - ClassReader classReader = new ClassReader(bytes); - classReader.accept(classNode, 0); - - //Check this class is unmodified - ASMHelper.verifyClassHash(shortClassName, bytes, validHashes); - - if (transformType == WinterBehavior.DECAY) - { - //Instances of IDecayableCrop decay into dead crops when cold - classNode.interfaces.add("toughasnails/api/season/IDecayableCrop"); - } - else if (transformType == WinterBehavior.HIBERNATE) - { - //Instances of IHibernatingCrop do not tick when cold - classNode.interfaces.add("toughasnails/api/season/IHibernatingCrop"); - } - - List successfulTransformations = Lists.newArrayList(); - - //Iterate over the methods in the class - for (MethodNode methodNode : classNode.methods) - { - if (ASMHelper.methodEquals(methodNode, UPDATE_TICK_NAMES, ObfHelper.createMethodDescriptor(obfuscatedClass, "V", "net/minecraft/world/World", "net/minecraft/util/math/BlockPos", "net/minecraft/block/state/IBlockState", "java/util/Random"))) - { - InsnList insnList = new InsnList(); - - //Determine crop behavior based on the current season - insnList.add(new VarInsnNode(Opcodes.ALOAD, 0)); - insnList.add(new VarInsnNode(Opcodes.ALOAD, 1)); - insnList.add(new VarInsnNode(Opcodes.ALOAD, 2)); - if (transformType == WinterBehavior.DECAY) - { - insnList.add(new MethodInsnNode(Opcodes.INVOKESTATIC, "toughasnails/season/SeasonASMHelper", "onUpdateTick", ObfHelper.createMethodDescriptor(obfuscatedClass, "V", "net/minecraft/block/Block", "net/minecraft/world/World", "net/minecraft/util/math/BlockPos"), false)); - //Insert our new instructions before returning - methodNode.instructions.insertBefore(methodNode.instructions.get(methodNode.instructions.indexOf(methodNode.instructions.getLast()) - 1), insnList); - } - else if (transformType == WinterBehavior.HIBERNATE) - { - insnList.add(new MethodInsnNode(Opcodes.INVOKESTATIC, "toughasnails/season/SeasonASMHelper", "shouldHibernate", ObfHelper.createMethodDescriptor(obfuscatedClass, "Z", "net/minecraft/block/Block", "net/minecraft/world/World", "net/minecraft/util/math/BlockPos"), false)); - //If above statement is true, don't let the function do anything else (i.e. stop the crop from ticking into a ripe block state) - LabelNode resumeUpdateTick = new LabelNode(); - insnList.add(new JumpInsnNode(Opcodes.IFEQ, resumeUpdateTick)); - insnList.add(new InsnNode(Opcodes.RETURN)); - insnList.add(resumeUpdateTick); - //Insert our new instructions before anything else happens - methodNode.instructions.insertBefore(methodNode.instructions.getFirst(), insnList); - } - - - successfulTransformations.add(methodNode.name + " " + methodNode.desc); - } - } - - if (successfulTransformations.size() != 1) throw new RuntimeException("An error occurred transforming " + shortClassName + ". Applied transformations: " + successfulTransformations.toString()); - - //Encode the altered class back into bytes - ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES); - classNode.accept(writer); - bytes = writer.toByteArray(); - - return bytes; - } -} diff --git a/src/main/java/toughasnails/asm/transformer/BlockCropsTransformer.java b/src/main/java/toughasnails/asm/transformer/BlockCropsTransformer.java deleted file mode 100644 index 76bf9a3e..00000000 --- a/src/main/java/toughasnails/asm/transformer/BlockCropsTransformer.java +++ /dev/null @@ -1,24 +0,0 @@ -/******************************************************************************* - * Copyright 2016, the Biomes O' Plenty Team - * - * This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International Public License. - * - * To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-nd/4.0/. - ******************************************************************************/ -package toughasnails.asm.transformer; - -public class BlockCropsTransformer extends AbstractCropTransformer -{ - private static final String[] VALID_HASHES = new String[] { "3d74307bb515539176e7a84967b10a28", "b835f0bbb24031fee6ad804d8c48d2dc" }; - - @Override - public byte[] transform(String name, String transformedName, byte[] basicClass) - { - if (transformedName.equals("net.minecraft.block.BlockCrops")) - { - return transformCrop(basicClass, !transformedName.equals(name), "BlockCrops", VALID_HASHES, WinterBehavior.DECAY); - } - - return basicClass; - } -} diff --git a/src/main/java/toughasnails/asm/transformer/BlockStemTransformer.java b/src/main/java/toughasnails/asm/transformer/BlockStemTransformer.java deleted file mode 100644 index f64336ae..00000000 --- a/src/main/java/toughasnails/asm/transformer/BlockStemTransformer.java +++ /dev/null @@ -1,18 +0,0 @@ -package toughasnails.asm.transformer; - -//Melons and pumpkins -public class BlockStemTransformer extends AbstractCropTransformer -{ - private static final String[] VALID_HASHES = new String[] { "6a28b8cb3a448cb0b9fa7f5c5d7df8d9" }; - - @Override - public byte[] transform(String name, String transformedName, byte[] basicClass) - { - if (transformedName.equals("net.minecraft.block.BlockStem")) - { - return transformCrop(basicClass, !transformedName.equals(name), "BlockStem", VALID_HASHES, WinterBehavior.DECAY); - } - - return basicClass; - } -} \ No newline at end of file diff --git a/src/main/java/toughasnails/asm/transformer/CropDecayTransformer.java b/src/main/java/toughasnails/asm/transformer/CropDecayTransformer.java new file mode 100644 index 00000000..7def2311 --- /dev/null +++ b/src/main/java/toughasnails/asm/transformer/CropDecayTransformer.java @@ -0,0 +1,145 @@ +/******************************************************************************* + * Copyright 2016, the Biomes O' Plenty Team + * + * This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International Public License. + * + * To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-nd/4.0/. + ******************************************************************************/ +package toughasnails.asm.transformer; + +import java.util.List; + +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.tree.ClassNode; +import org.objectweb.asm.tree.InsnList; +import org.objectweb.asm.tree.InsnNode; +import org.objectweb.asm.tree.LdcInsnNode; +import org.objectweb.asm.tree.MethodInsnNode; +import org.objectweb.asm.tree.MethodNode; +import org.objectweb.asm.tree.VarInsnNode; + +import com.google.common.collect.Lists; + +import net.minecraft.launchwrapper.IClassTransformer; +import net.minecraftforge.classloading.FMLForgePlugin; +import toughasnails.asm.ASMHelper; +import toughasnails.asm.ObfHelper; + +public class CropDecayTransformer implements IClassTransformer { + private static final String[] VALID_HASHES = new String[] { + // BlockCrops + "3d74307bb515539176e7a84967b10a28", + "b835f0bbb24031fee6ad804d8c48d2dc", + // BlockStem + "6a28b8cb3a448cb0b9fa7f5c5d7df8d9" }; + + private static final String[] UPDATE_TICK_NAMES = new String[] { + "updateTick", "func_180650_b", "b" }; + + @Override + public byte[] transform(String name, String transformedName, + byte[] basicClass) { + if (transformedName.equals("net.minecraft.block.BlockCrops") + || transformedName.equals("net.minecraft.block.BlockStem")) { + + // This is a vanilla crop; let's implement the interface and inject + // the crop decay hook + return transformToDecay(basicClass, !FMLForgePlugin.RUNTIME_DEOBF, + transformedName, true); + } else { + // Check if some crop implements the interface, and if it does then + // inject the crop decay hook + ClassReader classReader = new ClassReader(basicClass); + InterfaceCheckVisitor visitor = new InterfaceCheckVisitor( + "toughasnails/api/season/IDecayableCrop"); + classReader.accept(visitor, ClassReader.SKIP_CODE); + if (visitor.isInterfaceFound) { + return transformToDecay(basicClass, + !FMLForgePlugin.RUNTIME_DEOBF, transformedName, false); + } + } + + return basicClass; + } + + private byte[] transformToDecay(byte[] bytes, boolean obfuscatedClass, + String name, boolean isVanilla) { + // Decode the class from bytes + ClassNode classNode = new ClassNode(); + ClassReader classReader = new ClassReader(bytes); + classReader.accept(classNode, 0); + + if (isVanilla) { + // Check this class is unmodified + ASMHelper.verifyClassHash(name, bytes, VALID_HASHES); + // Vanilla crops need the interface added + classNode.interfaces.add("toughasnails/api/season/IDecayableCrop"); + } + + List successfulTransformations = Lists.newArrayList(); + + // Iterate over the methods in the class + for (MethodNode methodNode : classNode.methods) { + if (ASMHelper.methodEquals(methodNode, UPDATE_TICK_NAMES, + ObfHelper.createMethodDescriptor(obfuscatedClass, "V", + "net/minecraft/world/World", + "net/minecraft/util/math/BlockPos", + "net/minecraft/block/state/IBlockState", + "java/util/Random"))) { + InsnList insnList = new InsnList(); + + // Get the current season + insnList.add(new VarInsnNode(Opcodes.ALOAD, 0)); + insnList.add(new VarInsnNode(Opcodes.ALOAD, 1)); + insnList.add(new VarInsnNode(Opcodes.ALOAD, 2)); + insnList.add(new MethodInsnNode(Opcodes.INVOKESTATIC, + "toughasnails/season/SeasonASMHelper", "onUpdateTick", + ObfHelper.createMethodDescriptor(obfuscatedClass, "V", + "net/minecraft/block/Block", + "net/minecraft/world/World", + "net/minecraft/util/math/BlockPos"), + false)); + + // Insert our new instructions before returning + methodNode.instructions + .insertBefore( + methodNode.instructions + .get(methodNode.instructions + .indexOf(methodNode.instructions + .getLast()) + - 1), + insnList); + + successfulTransformations + .add(methodNode.name + " " + methodNode.desc); + } + } + + if (isVanilla) { + // The vanilla method does not exist? What is this sorcery?!? + if (successfulTransformations.size() != 1) + throw new RuntimeException("An error occurred transforming " + + name + ". Applied transformations: " + + successfulTransformations.toString()); + + // Implement shouldDecay() method, which simply returns true. The + // method allows subclasses to override behavior. + MethodNode decayMethod = new MethodNode(Opcodes.ACC_PUBLIC, + "shouldDecay", "()Z", null, null); + InsnList decayInsns = new InsnList(); + decayInsns.add(new LdcInsnNode(new Integer(1))); + decayInsns.add(new InsnNode(Opcodes.IRETURN)); + decayMethod.instructions.add(decayInsns); + classNode.methods.add(decayMethod); + } + + // Encode the altered class back into bytes + ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS); + classNode.accept(writer); + bytes = writer.toByteArray(); + + return bytes; + } +} \ No newline at end of file diff --git a/src/main/java/toughasnails/asm/transformer/InterfaceCheckVisitor.java b/src/main/java/toughasnails/asm/transformer/InterfaceCheckVisitor.java new file mode 100644 index 00000000..0dfef6fc --- /dev/null +++ b/src/main/java/toughasnails/asm/transformer/InterfaceCheckVisitor.java @@ -0,0 +1,26 @@ +package toughasnails.asm.transformer; + +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.Opcodes; + +public class InterfaceCheckVisitor extends ClassVisitor { + public boolean isInterfaceFound; + public String searchInterface; + + public InterfaceCheckVisitor(String searchInterface) { + super(Opcodes.ASM5); + this.isInterfaceFound = false; + this.searchInterface = searchInterface; + } + + @Override + public void visit(int version, int access, String name, String signature, + String superName, String[] interfaces) { + for (String iface : interfaces) { + if (iface.equals(searchInterface)) { + isInterfaceFound = true; + return; + } + } + } +} diff --git a/src/main/java/toughasnails/asm/transformer/MysticalCropTransformer.java b/src/main/java/toughasnails/asm/transformer/MysticalCropTransformer.java deleted file mode 100644 index f17d1ad5..00000000 --- a/src/main/java/toughasnails/asm/transformer/MysticalCropTransformer.java +++ /dev/null @@ -1,43 +0,0 @@ -package toughasnails.asm.transformer; - -//Mystical Agriculture compatibility -public class MysticalCropTransformer extends AbstractCropTransformer -{ - private static final String[] VALID_HASHES_RESOURCE = new String[] { "5fa681a833e27d1b0a1f8d1d62583fae" }; - private static final String[] VALID_HASHES_INFERIUM_1 = new String[] { "56de0b086621ff38e7df680e290cef16" }; - private static final String[] VALID_HASHES_INFERIUM_2 = new String[] { "28d1acc3f789144a51980e60ded64331" }; - private static final String[] VALID_HASHES_INFERIUM_3 = new String[] { "6aa39049a594e7a398a480acc17a6fa3" }; - private static final String[] VALID_HASHES_INFERIUM_4 = new String[] { "0b322f265216c53c7df39c9ebf9af850" }; - private static final String[] VALID_HASHES_INFERIUM_5 = new String[] { "05cdc4dc01b3fb08e242c5fc59fbb13b" }; - - @Override - public byte[] transform(String name, String transformedName, byte[] basicClass) - { - if (transformedName.equals("com.blakebr0.mysticalagriculture.blocks.crop.BlockMysticalCrop")) - { - return transformCrop(basicClass, !transformedName.equals(name), "BlockMysticalCrop", VALID_HASHES_RESOURCE, WinterBehavior.DECAY); - } - if (transformedName.equals("com.blakebr0.mysticalagriculture.blocks.crop.BlockTier1InferiumCrop")) - { - return transformCrop(basicClass, !transformedName.equals(name), "BlockTier1InferiumCrop", VALID_HASHES_INFERIUM_1, WinterBehavior.DECAY); - } - if (transformedName.equals("com.blakebr0.mysticalagriculture.blocks.crop.BlockTier2InferiumCrop")) - { - return transformCrop(basicClass, !transformedName.equals(name), "BlockTier2InferiumCrop", VALID_HASHES_INFERIUM_2, WinterBehavior.DECAY); - } - if (transformedName.equals("com.blakebr0.mysticalagriculture.blocks.crop.BlockTier3InferiumCrop")) - { - return transformCrop(basicClass, !transformedName.equals(name), "BlockTier3InferiumCrop", VALID_HASHES_INFERIUM_3, WinterBehavior.DECAY); - } - if (transformedName.equals("com.blakebr0.mysticalagriculture.blocks.crop.BlockTier4InferiumCrop")) - { - return transformCrop(basicClass, !transformedName.equals(name), "BlockTier4InferiumCrop", VALID_HASHES_INFERIUM_4, WinterBehavior.DECAY); - } - if (transformedName.equals("com.blakebr0.mysticalagriculture.blocks.crop.BlockTier5InferiumCrop")) - { - return transformCrop(basicClass, !transformedName.equals(name), "BlockTier5InferiumCrop", VALID_HASHES_INFERIUM_5, WinterBehavior.DECAY); - } - - return basicClass; - } -} \ No newline at end of file diff --git a/src/main/java/toughasnails/asm/transformer/PamCropTransformer.java b/src/main/java/toughasnails/asm/transformer/PamCropTransformer.java deleted file mode 100644 index a17e4ce6..00000000 --- a/src/main/java/toughasnails/asm/transformer/PamCropTransformer.java +++ /dev/null @@ -1,44 +0,0 @@ -/******************************************************************************* - * Copyright 2016, the Biomes O' Plenty Team - * - * This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International Public License. - * - * To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-nd/4.0/. - ******************************************************************************/ -package toughasnails.asm.transformer; - -import toughasnails.asm.transformer.AbstractCropTransformer.WinterBehavior; - -// Pam's Harvestcraft compatibility -public class PamCropTransformer extends AbstractCropTransformer { - private static final String[] VALID_HASHES_CROP = new String[] { - "03263774b5cda6bfeccc1622fd344710" }; - private static final String[] VALID_HASHES_FRUIT = new String[] { - "da4a453d0bcd36b8db130e372865cb24" }; - private static final String[] VALID_HASHES_FRUIT_LOG = new String[] { - "ce0043f49abbe83982be20cbf4989bb3" }; - - @Override - public byte[] transform(String name, String transformedName, - byte[] basicClass) { - if (transformedName - .equals("com.pam.harvestcraft.blocks.growables.BlockPamCrop")) { - return transformCrop(basicClass, !transformedName.equals(name), - "BlockPamCrop", VALID_HASHES_CROP, WinterBehavior.DECAY); - } - if (transformedName.equals( - "com.pam.harvestcraft.blocks.growables.BlockPamFruit")) { - return transformCrop(basicClass, !transformedName.equals(name), - "BlockPamFruit", VALID_HASHES_FRUIT, - WinterBehavior.HIBERNATE); - } - if (transformedName.equals( - "com.pam.harvestcraft.blocks.growables.BlockPamFruitLog")) { - return transformCrop(basicClass, !transformedName.equals(name), - "BlockPamFruitLog", VALID_HASHES_FRUIT_LOG, - WinterBehavior.HIBERNATE); - } - - return basicClass; - } -} diff --git a/src/main/java/toughasnails/asm/transformer/WorldTransformer.java b/src/main/java/toughasnails/asm/transformer/WorldTransformer.java index 6e29192a..cb69d04c 100644 --- a/src/main/java/toughasnails/asm/transformer/WorldTransformer.java +++ b/src/main/java/toughasnails/asm/transformer/WorldTransformer.java @@ -26,129 +26,201 @@ import toughasnails.asm.ASMHelper; import toughasnails.asm.ObfHelper; -public class WorldTransformer implements IClassTransformer -{ - private static final String[] VALID_HASHES = new String[] { "547d356661b3b86facf7043fb930bcfb", "a812fff5e65c73ca82f3f2c9ddd2fb03" }; - - private static final String[] CAN_SNOW_AT_NAMES = new String[] { "canSnowAt", "func_175708_f", "f" }; - private static final String[] CAN_BLOCK_FREEZE_NAMES = new String[] { "canBlockFreeze", "func_175670_e", "e" }; - private static final String[] IS_RAINING_AT_NAMES = new String[] { "isRainingAt", "func_175727_C", "B" }; - private static final String[] GET_BIOME_GEN_FOR_COORDS_NAMES = new String[] { "getBiome", "func_180494_b", "b" }; - - @Override - public byte[] transform(String name, String transformedName, byte[] basicClass) - { - if (transformedName.equals("net.minecraft.world.World")) - { - return transformWorld(basicClass, !transformedName.equals(name)); - } - - return basicClass; - } - - private byte[] transformWorld(byte[] bytes, boolean obfuscatedClass) - { - //Decode the class from bytes - ClassNode classNode = new ClassNode(); - ClassReader classReader = new ClassReader(bytes); - classReader.accept(classNode, 0); - - //Check this class is unmodified - ASMHelper.verifyClassHash("World", bytes, VALID_HASHES); - - List successfulTransformations = Lists.newArrayList(); - - //Iterate over the methods in the class - for (MethodNode methodNode : classNode.methods) - { - if (ASMHelper.methodEquals(methodNode, CAN_SNOW_AT_NAMES, ObfHelper.createMethodDescriptor(obfuscatedClass, "Z", "net/minecraft/util/math/BlockPos", "Z"))) - { - InsnList insnList = new InsnList(); - - //Get the current season - insnList.add(new VarInsnNode(Opcodes.ALOAD, 0)); - insnList.add(new MethodInsnNode(Opcodes.INVOKESTATIC, "toughasnails/api/season/SeasonHelper", "getSeasonData", ObfHelper.createMethodDescriptor(obfuscatedClass, "toughasnails/api/season/ISeasonData", "net/minecraft/world/World"), false)); - insnList.add(new MethodInsnNode(Opcodes.INVOKEINTERFACE, "toughasnails/api/season/ISeasonData", "getSubSeason", "()Ltoughasnails/api/season/Season$SubSeason;", true)); - insnList.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, "toughasnails/api/season/Season$SubSeason", "getSeason", "()Ltoughasnails/api/season/Season;", false)); - insnList.add(new VarInsnNode(Opcodes.ASTORE, 3)); - - //Invoke our replacement method - insnList.add(new VarInsnNode(Opcodes.ALOAD, 0)); - insnList.add(new VarInsnNode(Opcodes.ALOAD, 1)); - insnList.add(new VarInsnNode(Opcodes.ILOAD, 2)); - insnList.add(new VarInsnNode(Opcodes.ALOAD, 3)); - insnList.add(new MethodInsnNode(Opcodes.INVOKESTATIC, "toughasnails/season/SeasonASMHelper", "canSnowAtInSeason", ObfHelper.createMethodDescriptor(obfuscatedClass, "Z", "net/minecraft/world/World", "net/minecraft/util/math/BlockPos", "Z", "toughasnails/api/season/Season"), false)); - insnList.add(new InsnNode(Opcodes.IRETURN)); - - //Substitute existing instructions with our new ones - methodNode.instructions.clear(); - methodNode.instructions.insert(insnList); - - successfulTransformations.add(methodNode.name + " " + methodNode.desc); - } - else if (ASMHelper.methodEquals(methodNode, CAN_BLOCK_FREEZE_NAMES, ObfHelper.createMethodDescriptor(obfuscatedClass, "Z", "net/minecraft/util/math/BlockPos", "Z"))) - { - InsnList insnList = new InsnList(); - - //Get the current season - insnList.add(new VarInsnNode(Opcodes.ALOAD, 0)); - insnList.add(new MethodInsnNode(Opcodes.INVOKESTATIC, "toughasnails/api/season/SeasonHelper", "getSeasonData", ObfHelper.createMethodDescriptor(obfuscatedClass, "toughasnails/api/season/ISeasonData", "net/minecraft/world/World"), false)); - insnList.add(new MethodInsnNode(Opcodes.INVOKEINTERFACE, "toughasnails/api/season/ISeasonData", "getSubSeason", "()Ltoughasnails/api/season/Season$SubSeason;", true)); - insnList.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, "toughasnails/api/season/Season$SubSeason", "getSeason", "()Ltoughasnails/api/season/Season;", false)); - insnList.add(new VarInsnNode(Opcodes.ASTORE, 3)); - - //Invoke our replacement method - insnList.add(new VarInsnNode(Opcodes.ALOAD, 0)); - insnList.add(new VarInsnNode(Opcodes.ALOAD, 1)); - insnList.add(new VarInsnNode(Opcodes.ILOAD, 2)); - insnList.add(new VarInsnNode(Opcodes.ALOAD, 3)); - insnList.add(new MethodInsnNode(Opcodes.INVOKESTATIC, "toughasnails/season/SeasonASMHelper", "canBlockFreezeInSeason", ObfHelper.createMethodDescriptor(obfuscatedClass, "Z", "net/minecraft/world/World", "net/minecraft/util/math/BlockPos", "Z", "toughasnails/api/season/Season"), false)); - insnList.add(new InsnNode(Opcodes.IRETURN)); - - //Substitute existing instructions with our new ones - methodNode.instructions.clear(); - methodNode.instructions.insert(insnList); - - successfulTransformations.add(methodNode.name + " " + methodNode.desc); - } - else if (ASMHelper.methodEquals(methodNode, IS_RAINING_AT_NAMES, ObfHelper.createMethodDescriptor(obfuscatedClass, "Z", "net/minecraft/util/math/BlockPos"))) - { - InsnList insnList = new InsnList(); - - //Get the current season - insnList.add(new VarInsnNode(Opcodes.ALOAD, 0)); - insnList.add(new MethodInsnNode(Opcodes.INVOKESTATIC, "toughasnails/api/season/SeasonHelper", "getSeasonData", ObfHelper.createMethodDescriptor(obfuscatedClass, "toughasnails/api/season/ISeasonData", "net/minecraft/world/World"), false)); - insnList.add(new MethodInsnNode(Opcodes.INVOKEINTERFACE, "toughasnails/api/season/ISeasonData", "getSubSeason", "()Ltoughasnails/api/season/Season$SubSeason;", true)); - insnList.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, "toughasnails/api/season/Season$SubSeason", "getSeason", "()Ltoughasnails/api/season/Season;", false)); - insnList.add(new VarInsnNode(Opcodes.ASTORE, 2)); - - //Invoke our replacement method - insnList.add(new VarInsnNode(Opcodes.ALOAD, 0)); - insnList.add(new VarInsnNode(Opcodes.ALOAD, 1)); - insnList.add(new VarInsnNode(Opcodes.ALOAD, 2)); - insnList.add(new MethodInsnNode(Opcodes.INVOKESTATIC, "toughasnails/season/SeasonASMHelper", "isRainingAtInSeason", ObfHelper.createMethodDescriptor(obfuscatedClass, "Z", "net/minecraft/world/World", "net/minecraft/util/math/BlockPos", "toughasnails/api/season/Season"), false)); - insnList.add(new InsnNode(Opcodes.ICONST_0)); //Necessary for compatibility with RandomThingsCore - insnList.add(new InsnNode(Opcodes.IRETURN)); - - MethodInsnNode invokeMethodNode = ASMHelper.getUniqueMethodInsnNode(methodNode, Opcodes.INVOKEVIRTUAL, ObfHelper.unmapType(obfuscatedClass, "net/minecraft/world/World"), GET_BIOME_GEN_FOR_COORDS_NAMES, ObfHelper.createMethodDescriptor(obfuscatedClass, "net/minecraft/world/biome/Biome", "net/minecraft/util/math/BlockPos")); - AbstractInsnNode insertionPoint = methodNode.instructions.get(methodNode.instructions.indexOf(invokeMethodNode) - 2); - - //Insert our new instructions before the insertion point - methodNode.instructions.insertBefore(insertionPoint, insnList); - - ASMHelper.clearNextInstructions(methodNode, insertionPoint); - - successfulTransformations.add(methodNode.name + " " + methodNode.desc); - } - } - - if (successfulTransformations.size() != 3) throw new RuntimeException("An error occurred transforming World. Applied transformations: " + successfulTransformations.toString()); - - //Encode the altered class back into bytes - ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS); - classNode.accept(writer); - bytes = writer.toByteArray(); - - return bytes; - } +public class WorldTransformer implements IClassTransformer { + private static final String[] VALID_HASHES = new String[] { + "547d356661b3b86facf7043fb930bcfb", + "a812fff5e65c73ca82f3f2c9ddd2fb03" }; + + private static final String[] CAN_SNOW_AT_NAMES = new String[] { + "canSnowAt", "func_175708_f", "f" }; + private static final String[] CAN_BLOCK_FREEZE_NAMES = new String[] { + "canBlockFreeze", "func_175670_e", "e" }; + private static final String[] IS_RAINING_AT_NAMES = new String[] { + "isRainingAt", "func_175727_C", "B" }; + private static final String[] GET_BIOME_GEN_FOR_COORDS_NAMES = new String[] { + "getBiomeGenForCoords", "func_180494_b", "b" }; + + @Override + public byte[] transform(String name, String transformedName, + byte[] basicClass) { + if (transformedName.equals("net.minecraft.world.World")) { + return transformWorld(basicClass, !transformedName.equals(name)); + } + + return basicClass; + } + + private byte[] transformWorld(byte[] bytes, boolean obfuscatedClass) { + // Decode the class from bytes + ClassNode classNode = new ClassNode(); + ClassReader classReader = new ClassReader(bytes); + classReader.accept(classNode, 0); + + // Check this class is unmodified + ASMHelper.verifyClassHash("World", bytes, VALID_HASHES); + System.out.println("Post-verification"); + + List successfulTransformations = Lists.newArrayList(); + + // Iterate over the methods in the class + for (MethodNode methodNode : classNode.methods) { + if (ASMHelper.methodEquals(methodNode, CAN_SNOW_AT_NAMES, + ObfHelper.createMethodDescriptor(obfuscatedClass, "Z", + "net/minecraft/util/math/BlockPos", "Z"))) { + InsnList insnList = new InsnList(); + + // Get the current season + insnList.add(new VarInsnNode(Opcodes.ALOAD, 0)); + insnList.add(new MethodInsnNode(Opcodes.INVOKESTATIC, + "toughasnails/api/season/SeasonHelper", "getSeasonData", + ObfHelper.createMethodDescriptor(obfuscatedClass, + "toughasnails/api/season/ISeasonData", + "net/minecraft/world/World"), + false)); + insnList.add(new MethodInsnNode(Opcodes.INVOKEINTERFACE, + "toughasnails/api/season/ISeasonData", "getSubSeason", + "()Ltoughasnails/api/season/Season$SubSeason;", true)); + insnList.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, + "toughasnails/api/season/Season$SubSeason", "getSeason", + "()Ltoughasnails/api/season/Season;", false)); + insnList.add(new VarInsnNode(Opcodes.ASTORE, 3)); + + // Invoke our replacement method + insnList.add(new VarInsnNode(Opcodes.ALOAD, 0)); + insnList.add(new VarInsnNode(Opcodes.ALOAD, 1)); + insnList.add(new VarInsnNode(Opcodes.ILOAD, 2)); + insnList.add(new VarInsnNode(Opcodes.ALOAD, 3)); + insnList.add(new MethodInsnNode(Opcodes.INVOKESTATIC, + "toughasnails/season/SeasonASMHelper", + "canSnowAtInSeason", + ObfHelper.createMethodDescriptor(obfuscatedClass, "Z", + "net/minecraft/world/World", + "net/minecraft/util/math/BlockPos", "Z", + "toughasnails/api/season/Season"), + false)); + insnList.add(new InsnNode(Opcodes.IRETURN)); + + // Substitute existing instructions with our new ones + methodNode.instructions.clear(); + methodNode.instructions.insert(insnList); + + successfulTransformations + .add(methodNode.name + " " + methodNode.desc); + } else + if (ASMHelper.methodEquals(methodNode, CAN_BLOCK_FREEZE_NAMES, + ObfHelper.createMethodDescriptor(obfuscatedClass, "Z", + "net/minecraft/util/math/BlockPos", "Z"))) { + InsnList insnList = new InsnList(); + + // Get the current season + insnList.add(new VarInsnNode(Opcodes.ALOAD, 0)); + insnList.add(new MethodInsnNode(Opcodes.INVOKESTATIC, + "toughasnails/api/season/SeasonHelper", "getSeasonData", + ObfHelper.createMethodDescriptor(obfuscatedClass, + "toughasnails/api/season/ISeasonData", + "net/minecraft/world/World"), + false)); + insnList.add(new MethodInsnNode(Opcodes.INVOKEINTERFACE, + "toughasnails/api/season/ISeasonData", "getSubSeason", + "()Ltoughasnails/api/season/Season$SubSeason;", true)); + insnList.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, + "toughasnails/api/season/Season$SubSeason", "getSeason", + "()Ltoughasnails/api/season/Season;", false)); + insnList.add(new VarInsnNode(Opcodes.ASTORE, 3)); + + // Invoke our replacement method + insnList.add(new VarInsnNode(Opcodes.ALOAD, 0)); + insnList.add(new VarInsnNode(Opcodes.ALOAD, 1)); + insnList.add(new VarInsnNode(Opcodes.ILOAD, 2)); + insnList.add(new VarInsnNode(Opcodes.ALOAD, 3)); + insnList.add(new MethodInsnNode(Opcodes.INVOKESTATIC, + "toughasnails/season/SeasonASMHelper", + "canBlockFreezeInSeason", + ObfHelper.createMethodDescriptor(obfuscatedClass, "Z", + "net/minecraft/world/World", + "net/minecraft/util/math/BlockPos", "Z", + "toughasnails/api/season/Season"), + false)); + insnList.add(new InsnNode(Opcodes.IRETURN)); + + // Substitute existing instructions with our new ones + methodNode.instructions.clear(); + methodNode.instructions.insert(insnList); + + successfulTransformations + .add(methodNode.name + " " + methodNode.desc); + } else if (ASMHelper.methodEquals(methodNode, IS_RAINING_AT_NAMES, + ObfHelper.createMethodDescriptor(obfuscatedClass, "Z", + "net/minecraft/util/math/BlockPos"))) { + InsnList insnList = new InsnList(); + + // Get the current season + insnList.add(new VarInsnNode(Opcodes.ALOAD, 0)); + insnList.add(new MethodInsnNode(Opcodes.INVOKESTATIC, + "toughasnails/api/season/SeasonHelper", "getSeasonData", + ObfHelper.createMethodDescriptor(obfuscatedClass, + "toughasnails/api/season/ISeasonData", + "net/minecraft/world/World"), + false)); + insnList.add(new MethodInsnNode(Opcodes.INVOKEINTERFACE, + "toughasnails/api/season/ISeasonData", "getSubSeason", + "()Ltoughasnails/api/season/Season$SubSeason;", true)); + insnList.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, + "toughasnails/api/season/Season$SubSeason", "getSeason", + "()Ltoughasnails/api/season/Season;", false)); + insnList.add(new VarInsnNode(Opcodes.ASTORE, 2)); + + // Invoke our replacement method + insnList.add(new VarInsnNode(Opcodes.ALOAD, 0)); + insnList.add(new VarInsnNode(Opcodes.ALOAD, 1)); + insnList.add(new VarInsnNode(Opcodes.ALOAD, 2)); + insnList.add(new MethodInsnNode(Opcodes.INVOKESTATIC, + "toughasnails/season/SeasonASMHelper", + "isRainingAtInSeason", + ObfHelper.createMethodDescriptor(obfuscatedClass, "Z", + "net/minecraft/world/World", + "net/minecraft/util/math/BlockPos", + "toughasnails/api/season/Season"), + false)); + insnList.add(new InsnNode(Opcodes.ICONST_0)); // Necessary for + // compatibility + // with + // RandomThingsCore + insnList.add(new InsnNode(Opcodes.IRETURN)); + + MethodInsnNode invokeMethodNode = ASMHelper + .getUniqueMethodInsnNode(methodNode, + Opcodes.INVOKEVIRTUAL, + ObfHelper.unmapType(obfuscatedClass, + "net/minecraft/world/World"), + GET_BIOME_GEN_FOR_COORDS_NAMES, + ObfHelper.createMethodDescriptor( + obfuscatedClass, + "net/minecraft/world/biome/Biome", + "net/minecraft/util/math/BlockPos")); + AbstractInsnNode insertionPoint = methodNode.instructions.get( + methodNode.instructions.indexOf(invokeMethodNode) - 2); + + // Insert our new instructions before the insertion point + methodNode.instructions.insertBefore(insertionPoint, insnList); + + ASMHelper.clearNextInstructions(methodNode, insertionPoint); + + successfulTransformations + .add(methodNode.name + " " + methodNode.desc); + } + } + + if (successfulTransformations.size() != 3) + throw new RuntimeException( + "An error occurred transforming World. Applied transformations: " + + successfulTransformations.toString()); + + // Encode the altered class back into bytes + ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS); + classNode.accept(writer); + bytes = writer.toByteArray(); + + return bytes; + } } diff --git a/src/main/java/toughasnails/command/TANCommand.java b/src/main/java/toughasnails/command/TANCommand.java index fad75fe6..6b27b13b 100644 --- a/src/main/java/toughasnails/command/TANCommand.java +++ b/src/main/java/toughasnails/command/TANCommand.java @@ -30,12 +30,6 @@ import toughasnails.season.SeasonTime; import toughasnails.temperature.TemperatureDebugger; import toughasnails.temperature.TemperatureHandler; -import toughasnails.temperature.modifier.AltitudeModifier; -import toughasnails.temperature.modifier.BiomeModifier; -import toughasnails.temperature.modifier.ObjectProximityModifier; -import toughasnails.temperature.modifier.SeasonModifier; -import toughasnails.temperature.modifier.TimeModifier; -import toughasnails.temperature.modifier.WeatherModifier; import toughasnails.thirst.ThirstHandler; public class TANCommand extends CommandBase { @@ -152,32 +146,10 @@ private void retrieveTemperatureAt(ICommandSender sender, String[] args) if (world == null) { throw new WrongUsageException("commands.toughasnails.usage"); } - final TemperatureDebugger debugger = new TemperatureDebugger(); - AltitudeModifier altitudeModifier = new AltitudeModifier(debugger); - BiomeModifier biomeModifier = new BiomeModifier(debugger); - ObjectProximityModifier objectProximityModifier = new ObjectProximityModifier( - debugger); - WeatherModifier weatherModifier = new WeatherModifier(debugger); - TimeModifier timeModifier = new TimeModifier(debugger); - SeasonModifier seasonModifier = new SeasonModifier(debugger); - BlockPos position = new BlockPos(x, y, z); - Temperature baseTemperature = new Temperature( - TemperatureHandler.TEMPERATURE_SCALE_MIDPOINT); - Temperature targetTemperature = biomeModifier.modifyTarget(world, - position, baseTemperature); - targetTemperature = altitudeModifier.modifyTarget(world, position, - targetTemperature); - targetTemperature = objectProximityModifier.modifyTarget(world, - position, targetTemperature); - targetTemperature = weatherModifier.modifyTarget(world, position, - targetTemperature); - targetTemperature = timeModifier.modifyTarget(world, position, - targetTemperature); - targetTemperature = seasonModifier.modifyTarget(world, position, - targetTemperature); - - int finalTemperature = targetTemperature.getRawValue(); + + int finalTemperature = TemperatureHandler + .getTargetTemperatureAt(world, position); if (printOutput) { sender.addChatMessage(new TextComponentTranslation( "commands.toughasnails.tempat.success", dimensionID, x, diff --git a/src/main/java/toughasnails/config/CropGrowConfigEntry.java b/src/main/java/toughasnails/config/CropGrowConfigEntry.java new file mode 100644 index 00000000..f3fa9e0c --- /dev/null +++ b/src/main/java/toughasnails/config/CropGrowConfigEntry.java @@ -0,0 +1,38 @@ +package toughasnails.config; + +public class CropGrowConfigEntry { + private int minLiving; + private int minOptimal; + private int maxOptimal; + private int maxLiving; + private float nonOptimalChance; + + public CropGrowConfigEntry(int minLiving, int minOptimal, int maxOptimal, + int maxLiving, float nonOptimalChance) { + this.minLiving = minLiving; + this.minOptimal = minOptimal; + this.maxOptimal = maxOptimal; + this.maxLiving = maxLiving; + this.nonOptimalChance = nonOptimalChance; + } + + public int getMinLiving() { + return this.minLiving; + } + + public int getMinOptimal() { + return this.minOptimal; + } + + public int getMaxOptimal() { + return this.maxOptimal; + } + + public int getMaxLiving() { + return this.maxLiving; + } + + public float getNonOptimalChance() { + return this.nonOptimalChance; + } +} \ No newline at end of file diff --git a/src/main/java/toughasnails/config/GameplayConfigurationHandler.java b/src/main/java/toughasnails/config/GameplayConfigurationHandler.java index 89e9a3ce..6eaf0d9d 100644 --- a/src/main/java/toughasnails/config/GameplayConfigurationHandler.java +++ b/src/main/java/toughasnails/config/GameplayConfigurationHandler.java @@ -8,6 +8,9 @@ package toughasnails.config; import java.io.File; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import net.minecraftforge.common.config.Configuration; import net.minecraftforge.fml.client.event.ConfigChangedEvent; @@ -19,18 +22,99 @@ public class GameplayConfigurationHandler { public static final String SURVIVAL_SETTINGS = "Survival Settings"; public static final String DRINKS = "Drink Configuration"; + public static final String TEMPERATURE_TWEAKS = "Temperature Tweaks"; + public static final String CROP_TWEAKS = "Crop Tweaks"; public static Configuration config; + public static final Map EXTERNAL_DECAYING_CROPS = new HashMap(); + public static final Map EXTERNAL_HIBERNATING_CROPS = new HashMap(); + + private static void parseExternalDecayingCrops() { + List crops = SyncedConfig.getListValue(GameplayOption.CROPS); + for (String cropEntry : crops) { + String[] cropData = cropEntry.split(";"); + if (cropData.length == 6) { + String cropName = cropData[0]; + System.out.println("Parsing crop: " + cropName); + int minLiving = 0; + int minOptimal = 0; + int maxOptimal = 0; + int maxLiving = 0; + float nonOptimalChance = 0; + try { + minLiving = Integer.parseInt(cropData[1]); + minOptimal = Integer.parseInt(cropData[2]); + maxOptimal = Integer.parseInt(cropData[3]); + maxLiving = Integer.parseInt(cropData[4]); + nonOptimalChance = Float.parseFloat(cropData[5]); + CropGrowConfigEntry cropGrowData = new CropGrowConfigEntry( + minLiving, minOptimal, maxOptimal, maxLiving, + nonOptimalChance); + EXTERNAL_DECAYING_CROPS.put(cropName, cropGrowData); + } catch (NumberFormatException e) { + ToughAsNails.logger + .error("Tried to process misconfigured crop! " + + cropData.toString()); + } + } + } + } + + private static void parseExternalHibernatingCrops() { + List hibernating = SyncedConfig + .getListValue(GameplayOption.HIBERNATING); + for (String cropEntry : hibernating) { + String[] cropData = cropEntry.split(";"); + if (cropData.length == 6) { + String cropName = cropData[0]; + System.out.println("Parsing hibernating crop: " + cropName); + int minWaking = 0; + int minOptimal = 0; + int maxOptimal = 0; + int maxWaking = 0; + float nonOptimalChance = 0; + try { + minWaking = Integer.parseInt(cropData[1]); + minOptimal = Integer.parseInt(cropData[2]); + maxOptimal = Integer.parseInt(cropData[3]); + maxWaking = Integer.parseInt(cropData[4]); + nonOptimalChance = Float.parseFloat(cropData[5]); + CropGrowConfigEntry cropGrowData = new CropGrowConfigEntry( + minWaking, minOptimal, maxOptimal, maxWaking, + nonOptimalChance); + EXTERNAL_HIBERNATING_CROPS.put(cropName, cropGrowData); + } catch (NumberFormatException e) { + ToughAsNails.logger + .error("Tried to process misconfigured hibernating crop! " + + cropData.toString()); + } + } + } + } + public static void init(File configFile) { if (config == null) { config = new Configuration(configFile); loadConfiguration(); } + + // Parse the external decaying crops list into memory for greater + // efficiency. + if (EXTERNAL_DECAYING_CROPS.isEmpty()) { + parseExternalDecayingCrops(); + } + + // Parse the external hibernating crops list into memory for greater + // efficiency. + if (EXTERNAL_HIBERNATING_CROPS.isEmpty()) { + parseExternalHibernatingCrops(); + } } private static void loadConfiguration() { try { + // Major features addSyncedBool(GameplayOption.ENABLE_LOWERED_STARTING_HEALTH, true, SURVIVAL_SETTINGS, "Players begin with a lowered maximum health."); @@ -41,17 +125,145 @@ private static void loadConfiguration() { addSyncedBool(GameplayOption.ENABLE_THIRST, true, SURVIVAL_SETTINGS, "Players are affected by thirst"); + // Drink list String[] drinkDefault = { "minecraft:milk_bucket;*;6;0.4;0.0" }; addSyncedList(GameplayOption.DRINKS, drinkDefault, DRINKS, "List of additional drinks with configurable damage (* = any), thirst, " + "hydration, and poison chance values. ;-delimited"); + + // Temperature tweaks + // Thermometer override + addSyncedBool(GameplayOption.OVERRIDE_THERMOMETER_LIMITS, false, + TEMPERATURE_TWEAKS, + "Override the default TAN thermometer to have upper and lower bounds at the specified limits."); + addSyncedInt(GameplayOption.THERMOMETER_LOWER_BOUND, -25, + TEMPERATURE_TWEAKS, "The lower bound of the thermometer."); + addSyncedInt(GameplayOption.THERMOMETER_UPPER_BOUND, 25, + TEMPERATURE_TWEAKS, "The upper bound of the thermometer."); + + // Rain-chill + addSyncedBool(GameplayOption.RAIN_CHILL, true, TEMPERATURE_TWEAKS, + "Should rain reduce the temperature of a block in the world, or only snow?"); + + // Temperature modifiers + addSyncedInt(GameplayOption.BIOME_TEMP_MODIFIER, 10, + TEMPERATURE_TWEAKS, + "Scale how significantly biome influences temperature."); + addSyncedInt(GameplayOption.ALTITUDE_TEMP_MODIFIER, 3, + TEMPERATURE_TWEAKS, + "Scale how significantly altitude influences temperature."); + addSyncedInt(GameplayOption.WET_TEMP_MODIFIER, -7, + TEMPERATURE_TWEAKS, + "Scale how significantly being wet influences temperature."); + addSyncedInt(GameplayOption.SNOW_TEMP_MODIFIER, -10, + TEMPERATURE_TWEAKS, + "Scale how significantly snow influences temperature."); + addSyncedInt(GameplayOption.TIME_TEMP_MODIFIER, 7, + TEMPERATURE_TWEAKS, + "Scale how significantly time of day influences temperature."); + addSyncedFloat(GameplayOption.TIME_EXTREMITY_MODIFIER, 1.25f, + TEMPERATURE_TWEAKS, + "Scale how significantly the extreme times of day change the temperature."); + addSyncedInt(GameplayOption.EARLY_AUTUMN_MODIFIER, 2, + TEMPERATURE_TWEAKS, + "Set how the temperature is modified in the EARLY_AUTUMN season."); + addSyncedInt(GameplayOption.MID_AUTUMN_MODIFIER, 0, + TEMPERATURE_TWEAKS, + "Set how the temperature is modified in the MID_AUTUMN season."); + addSyncedInt(GameplayOption.LATE_AUTUMN_MODIFIER, -2, + TEMPERATURE_TWEAKS, + "Set how the temperature is modified in the LATE_AUTUMN season."); + addSyncedInt(GameplayOption.EARLY_WINTER_MODIFIER, -4, + TEMPERATURE_TWEAKS, + "Set how the temperature is modified in the EARLY_WINTER season."); + addSyncedInt(GameplayOption.MID_WINTER_MODIFIER, -6, + TEMPERATURE_TWEAKS, + "Set how the temperature is modified in the MID_WINTER season."); + addSyncedInt(GameplayOption.LATE_WINTER_MODIFIER, -6, + TEMPERATURE_TWEAKS, + "Set how the temperature is modified in the LATE_WINTER season."); + addSyncedInt(GameplayOption.EARLY_SPRING_MODIFIER, -4, + TEMPERATURE_TWEAKS, + "Set how the temperature is modified in the EARLY_SPRING season."); + addSyncedInt(GameplayOption.MID_SPRING_MODIFIER, -2, + TEMPERATURE_TWEAKS, + "Set how the temperature is modified in the MID_SPRING season."); + addSyncedInt(GameplayOption.LATE_SPRING_MODIFIER, 0, + TEMPERATURE_TWEAKS, + "Set how the temperature is modified in the LATE_SPRING season."); + addSyncedInt(GameplayOption.EARLY_SUMMER_MODIFIER, 0, + TEMPERATURE_TWEAKS, + "Set how the temperature is modified in the EARLY_SUMMER season."); + addSyncedInt(GameplayOption.MID_SUMMER_MODIFIER, 2, + TEMPERATURE_TWEAKS, + "Set how the temperature is modified in the MID_SUMMER season."); + addSyncedInt(GameplayOption.LATE_SUMMER_MODIFIER, 4, + TEMPERATURE_TWEAKS, + "Set how the temperature is modified in the LATE_SUMMER season."); + + // Crop tweaks + addSyncedBool(GameplayOption.TEMPERATURE_WITHERING, false, + CROP_TWEAKS, + "Should crop withering be based on actual plant temperature or just the season?"); + + String[] cropDefault = { "minecraft:wheat;5;10;15;20;0.5" }; + addSyncedList(GameplayOption.CROPS, cropDefault, CROP_TWEAKS, + "List of crops with configurable min living, " + + "min optimal, max optimal, max living temps, " + + "and the chance of skipping a growth tick outside " + + "of the optimal temperature range. ;-delimited"); + + String[] hibernateDefault = { + "harvestcraft:pamAlmond;5;10;15;20;0.5", + "harvestcraft:pamApple;5;10;15;20;0.5", + "harvestcraft:pamApricot;5;10;15;20;0.5", + "harvestcraft:pamAvocado;5;10;15;20;0.5", + "harvestcraft:pamBanana;5;10;15;20;0.5", + "harvestcraft:pamCashew;5;10;15;20;0.5", + "harvestcraft:pamCherry;5;10;15;20;0.5", + "harvestcraft:pamChestnut;5;10;15;20;0.5", + "harvestcraft:pamCinnamon;5;10;15;20;0.5", + "harvestcraft:pamCoconut;5;10;15;20;0.5", + "harvestcraft:pamDate;5;10;15;20;0.5", + "harvestcraft:pamDragonfruit;5;10;15;20;0.5", + "harvestcraft:pamDurian;5;10;15;20;0.5", + "harvestcraft:pamFig;5;10;15;20;0.5", + "harvestcraft:pamGooseberry;5;10;15;20;0.5", + "harvestcraft:pamGrapefruit;5;10;15;20;0.5", + "harvestcraft:pamLemon;5;10;15;20;0.5", + "harvestcraft:pamLime;5;10;15;20;0.5", + "harvestcraft:pamMango;5;10;15;20;0.5", + "harvestcraft:pamMaple;5;10;15;20;0.5", + "harvestcraft:pamNutmeg;5;10;15;20;0.5", + "harvestcraft:pamOlive;5;10;15;20;0.5", + "harvestcraft:pamOrange;5;10;15;20;0.5", + "harvestcraft:pamPapaya;5;10;15;20;0.5", + "harvestcraft:pamPaperbark;5;10;15;20;0.5", + "harvestcraft:pamPeach;5;10;15;20;0.5", + "harvestcraft:pamPear;5;10;15;20;0.5", + "harvestcraft:pamPecan;5;10;15;20;0.5", + "harvestcraft:pamPeppercorn;5;10;15;20;0.5", + "harvestcraft:pamPersimmon;5;10;15;20;0.5", + "harvestcraft:pamPistachio;5;10;15;20;0.5", + "harvestcraft:pamPlum;5;10;15;20;0.5", + "harvestcraft:pamPomegranate;5;10;15;20;0.5", + "harvestcraft:pamStarfruit;5;10;15;20;0.5", + "harvestcraft:pamVanillabean;5;10;15;20;0.5", + "harvestcraft:pamWalnut;5;10;15;20;0.5" }; + addSyncedList(GameplayOption.HIBERNATING, hibernateDefault, + CROP_TWEAKS, + "List of hibernating crops with configurable min waking, " + + "min optimal, max optimal, max waking temps, " + + "and the chance of skipping a growth tick outside " + + "of the optimal temperature range. ;-delimited"); } catch (Exception e) { ToughAsNails.logger.error( "Tough As Nails has encountered a problem loading gameplay.cfg", e); } finally { - if (config.hasChanged()) + if (config.hasChanged()) { config.save(); + } } } @@ -62,6 +274,20 @@ private static void addSyncedBool(GameplayOption option, SyncedConfig.addOption(option, "" + value); } + private static void addSyncedInt(GameplayOption option, int defaultValue, + String category, String comment) { + int value = config.getInt(option.getOptionName(), category, + defaultValue, Integer.MIN_VALUE, Integer.MAX_VALUE, comment); + SyncedConfig.addOption(option, "" + value); + } + + private static void addSyncedFloat(GameplayOption option, + float defaultValue, String category, String comment) { + float value = config.getFloat(option.getOptionName(), category, + defaultValue, Float.MIN_VALUE, Float.MAX_VALUE, comment); + SyncedConfig.addOption(option, "" + value); + } + private static void addSyncedList(GameplayOption option, String[] defaultValue, String category, String comment) { String[] drinkEntries = config.getStringList(option.getOptionName(), diff --git a/src/main/java/toughasnails/config/TANConfig.java b/src/main/java/toughasnails/config/TANConfig.java index d16e0c0a..a1d78694 100644 --- a/src/main/java/toughasnails/config/TANConfig.java +++ b/src/main/java/toughasnails/config/TANConfig.java @@ -29,383 +29,396 @@ import net.minecraft.init.Blocks; import toughasnails.api.TANBlocks; import toughasnails.block.BlockTANCampfire; +import toughasnails.block.BlockTANTemperatureCoil; +import toughasnails.block.BlockTANTemperatureCoil.CoilType; import toughasnails.core.ToughAsNails; import toughasnails.temperature.BlockTemperatureData; import toughasnails.temperature.MaterialTemperatureData; import toughasnails.util.BlockStateUtils; -public class TANConfig -{ - - public static Gson serializer = new GsonBuilder().setPrettyPrinting().create(); - public static JsonParser parser = new JsonParser(); - - public static HashMap> blockTemperatureData; - - public static MaterialTemperatureData materialTemperatureData; - - public static void init(File configDir) - { - - //Block temperature config: - blockTemperatureData = new HashMap>(); - - File blockTemperatureConfigFile = new File(configDir, "block_temperature.json"); - - //No config file, so create default config: - if (!blockTemperatureConfigFile.exists()) - { - try - { - BlockTemperatureData[] defaultBlockTemperatureData = { - new BlockTemperatureData(TANBlocks.campfire.getDefaultState().withProperty(BlockTANCampfire.BURNING, true), new String[]{BlockTANCampfire.BURNING.getName()}, 12.0F), - new BlockTemperatureData(Blocks.LIT_FURNACE.getDefaultState(), new String[0], 12.0F), - new BlockTemperatureData(Blocks.LAVA.getDefaultState(), new String[0], 1.5F), - new BlockTemperatureData(Blocks.FLOWING_LAVA.getDefaultState(), new String[0], 1.5F), - }; - - //Need to do this manually as there is some issue with getting Gson to serialise an IBlockState directly due to duplicated keys - JsonArray tempAry = new JsonArray(); - - for(BlockTemperatureData tempData : defaultBlockTemperatureData) - { - tempAry.add(asJsonObject(tempData)); - } - - writeFile(blockTemperatureConfigFile, tempAry); - } - catch (Exception e) - { - ToughAsNails.logger.error("Error creating default block temperature config file: " + blockTemperatureConfigFile.toString(), e); - } - } - - - try - { - String blockJsonString = FileUtils.readFileToString(blockTemperatureConfigFile); - - JsonElement blockAry = parser.parse(blockJsonString); - - if (blockAry == null) - { - ToughAsNails.logger.error("Error parsing block temperature config from json file: " + blockTemperatureConfigFile.toString() + " temperature information array does not exist."); - } - - if (blockAry.isJsonArray()) - { - for (JsonElement ele : blockAry.getAsJsonArray()) - { - BlockTemperatureData tempData = asBlockTemperatureData(ele, "Error parsing block temperature state configuration " + ele.toString()); - - String blockName = tempData.state.getBlock().getRegistryName().toString(); - - if (!blockTemperatureData.containsKey(blockName)) - { - blockTemperatureData.put(blockName, new ArrayList()); - } - - blockTemperatureData.get(blockName).add(tempData); - } - } - } - catch (Exception e) - { - ToughAsNails.logger.error("Error parsing block temperature config from json: " + blockTemperatureConfigFile.toString(), e); - } - - - //Material temperature config: - materialTemperatureData = new MaterialTemperatureData(); - - File materialTemperatureConfigFile = new File(configDir, "material_temperature.json"); - - try - { - if (!materialTemperatureConfigFile.exists()) - { - writeFile(materialTemperatureConfigFile, materialTemperatureData); - } - } - catch (Exception e) - { - ToughAsNails.logger.error("Error creating default material temperature config file: " + materialTemperatureConfigFile.toString(), e); - } - - try - { - String materialJsonString = FileUtils.readFileToString(materialTemperatureConfigFile); - - Gson gson = new Gson(); - - materialTemperatureData = gson.fromJson(materialJsonString, MaterialTemperatureData.class); - } - catch (Exception e) - { - ToughAsNails.logger.error("Error parsing material temperature config from json: " + materialTemperatureConfigFile.toString(), e); - } - - } - - - protected static boolean writeFile(File outputFile, Object obj) - { - try - { - FileUtils.write(outputFile, serializer.toJson(obj)); - return true; - } - catch (Exception e) - { - ToughAsNails.logger.error("Error writing config file " + outputFile.getAbsolutePath() + ": " + e.getMessage()); - return false; - } - } - - - protected static Map parse(String jsonString) - { - Map members; - - members = new HashMap(); - if (jsonString == null) {return members;} - - JsonElement rootElement = null; - try - { - rootElement = parser.parse(jsonString); - if (rootElement != null) - { - if (rootElement.isJsonObject()) - { - for (Entry entry : rootElement.getAsJsonObject().entrySet()) - { - members.put(entry.getKey(), entry.getValue()); - } - } else { - ToughAsNails.logger.error("Error parsing config: not a JSON object"); - } - } - } - catch (Exception e) - { - ToughAsNails.logger.error("Error parsing config: "+e.getMessage()); - } - - return members; - } - - protected static ReadBlockState asBlockState(JsonElement ele, String extraPrefix) - { - - try { - - JsonObject obj = ele.getAsJsonObject(); - - // attempt to load the specified block - if (!obj.has("block")) - { - ToughAsNails.logger.error(extraPrefix + " Block name missing"); - return null; - } - JsonElement blockName = obj.get("block"); - if (!blockName.isJsonPrimitive()) - { - ToughAsNails.logger.error(extraPrefix + " Invalid block name - must be a string"); - return null; - } - Block block = Block.getBlockFromName(blockName.getAsString()); - if (block == null) - { - ToughAsNails.logger.error(extraPrefix + " Unrecognised block name " + blockName.getAsString()); - return null; - } - - IBlockState state = block.getDefaultState(); - - ArrayList usedProperties = new ArrayList(); - - // attempt to add properties - if (obj.has("properties")) - { - JsonElement properties = obj.get("properties"); - if (!properties.isJsonObject()) - { - ToughAsNails.logger.error(extraPrefix + " Invalid properties list - must be a JSON object"); - return null; - } - - for (Entry entry : properties.getAsJsonObject().entrySet()) - { - String propRawName = entry.getKey(); - - IProperty property = BlockStateUtils.getPropertyByName(state, propRawName); - if (property != null) - { - String propRawValue = entry.getValue().getAsString(); - - if (!propRawValue.equals("*")) - { - Comparable propertyValue = BlockStateUtils.getPropertyValueByName(state, property, propRawValue); - if (propertyValue != null) - { - state = state.withProperty(property, propertyValue); - usedProperties.add(propRawName); - } - else - { - ToughAsNails.logger.error(extraPrefix + " Invalid value " + propRawValue + " for property " + propRawName); - } - } - } - else - { - ToughAsNails.logger.error(extraPrefix + " Invalid property name: " + propRawName); - } - } - } - - return new ReadBlockState(state, usedProperties.toArray(new String[0])); - } - catch (Exception e) - { - ToughAsNails.logger.error(extraPrefix + " Error fetching blockstate: " + e.getMessage()); - return null; - } - - } - - protected static JsonObject asJsonObject(IBlockState state, String[] useProperties) - { - try - { - JsonObject obj = new JsonObject(); - - obj.addProperty("block", state.getBlock().getRegistryName().toString()); - - - JsonObject props = new JsonObject(); - - for (IProperty blockProperty : state.getProperties().keySet()) - { - String propName = blockProperty.getName(); - String propValue = state.getValue(blockProperty).toString(); - - if (useProperties != null) - { - boolean foundProp = false; - for (String useName : useProperties) - { - if (useName.equalsIgnoreCase(propName)) - { - foundProp = true; - break; - } - } - - if (!foundProp) - { - //If a property is unused, set the value to a special wildcard * to indicate that any value matches - propValue = "*"; - } - } - - props.addProperty(propName, propValue); - } - - obj.add("properties", props); - - return obj; - } - catch (Exception e) - { - ToughAsNails.logger.error("Error converting blockstate to Json: " + e.getMessage()); - return null; - } - } - - protected static JsonObject asJsonObject(IBlockState state) - { - try - { - return asJsonObject(state, null); - } - catch (Exception e) - { - ToughAsNails.logger.error("Error converting blockstate to Json: " + e.getMessage()); - return null; - } - } - - protected static JsonObject asJsonObject(BlockTemperatureData blockTemperatureData) - { - JsonObject blockTempJson = new JsonObject(); - - JsonObject stateObject = asJsonObject(blockTemperatureData.state, blockTemperatureData.useProperties); - - blockTempJson.add("state", stateObject); - blockTempJson.addProperty("temperature", blockTemperatureData.blockTemperature); - - return blockTempJson; - } - - protected static BlockTemperatureData asBlockTemperatureData(JsonElement ele, String extraPrefix) - { - try - { - JsonObject obj = ele.getAsJsonObject(); - - // attempt to load the state - if (!obj.has("state")) - { - ToughAsNails.logger.error(extraPrefix + " Block state missing"); - return null; - } - JsonElement blockState = obj.get("state"); - if (!blockState.isJsonObject()) - { - ToughAsNails.logger.error(extraPrefix + " Invalid block state - must be an object"); - return null; - } - - ReadBlockState readState = asBlockState(blockState, extraPrefix); - - IBlockState state = readState.state; - String[] use_properties = readState.usedProperties; - - // attempt to get the temperature value - if(!obj.has("temperature")) - { - ToughAsNails.logger.error(extraPrefix + " block temperature missing"); - return null; - } - JsonElement blockTemperature = obj.get("temperature"); - if(!blockTemperature.isJsonPrimitive()) - { - ToughAsNails.logger.error(extraPrefix + " Invalid block temperature - must be a float"); - } - - float temperature = blockTemperature.getAsFloat(); - - return new BlockTemperatureData(state, use_properties, temperature); - } - catch (Exception e) - { - ToughAsNails.logger.error(extraPrefix + " Error fetching block temperature data: " + e.getMessage()); - return null; - } - - } +public class TANConfig { + + public static Gson serializer = new GsonBuilder().setPrettyPrinting() + .create(); + public static JsonParser parser = new JsonParser(); + + public static HashMap> blockTemperatureData; + + public static MaterialTemperatureData materialTemperatureData; + + public static void init(File configDir) { + + // Block temperature config: + blockTemperatureData = new HashMap>(); + + File blockTemperatureConfigFile = new File(configDir, + "block_temperature.json"); + + // No config file, so create default config: + if (!blockTemperatureConfigFile.exists()) { + try { + BlockTemperatureData[] defaultBlockTemperatureData = { + new BlockTemperatureData( + TANBlocks.campfire.getDefaultState() + .withProperty(BlockTANCampfire.BURNING, + true), + new String[] { + BlockTANCampfire.BURNING.getName() }, + 12.0F), + new BlockTemperatureData( + Blocks.LIT_FURNACE.getDefaultState(), + new String[0], 12.0F), + new BlockTemperatureData(Blocks.LAVA.getDefaultState(), + new String[0], 1.5F), + new BlockTemperatureData( + Blocks.FLOWING_LAVA.getDefaultState(), + new String[0], 1.5F), + new BlockTemperatureData( + TANBlocks.temperature_coil.getDefaultState() + .withProperty( + BlockTANTemperatureCoil.VARIANT, + CoilType.HEATING) + .withProperty( + BlockTANTemperatureCoil.POWERED, + true), + new String[] { "heating_coil" }, 15.0f), + new BlockTemperatureData( + TANBlocks.temperature_coil.getDefaultState() + .withProperty( + BlockTANTemperatureCoil.VARIANT, + CoilType.COOLING) + .withProperty( + BlockTANTemperatureCoil.POWERED, + true), + new String[] { "cooling_coil" }, -15.0f) }; + + // Need to do this manually as there is some issue with getting + // Gson to serialise an IBlockState directly due to duplicated + // keys + JsonArray tempAry = new JsonArray(); + + for (BlockTemperatureData tempData : defaultBlockTemperatureData) { + tempAry.add(asJsonObject(tempData)); + } + + writeFile(blockTemperatureConfigFile, tempAry); + } catch (Exception e) { + ToughAsNails.logger + .error("Error creating default block temperature config file: " + + blockTemperatureConfigFile.toString(), e); + } + } + + try { + String blockJsonString = FileUtils + .readFileToString(blockTemperatureConfigFile); + + JsonElement blockAry = parser.parse(blockJsonString); + + if (blockAry == null) { + ToughAsNails.logger + .error("Error parsing block temperature config from json file: " + + blockTemperatureConfigFile.toString() + + " temperature information array does not exist."); + } + + if (blockAry.isJsonArray()) { + for (JsonElement ele : blockAry.getAsJsonArray()) { + BlockTemperatureData tempData = asBlockTemperatureData(ele, + "Error parsing block temperature state configuration " + + ele.toString()); + + String blockName = tempData.state.getBlock() + .getRegistryName().toString(); + + if (!blockTemperatureData.containsKey(blockName)) { + blockTemperatureData.put(blockName, + new ArrayList()); + } + + blockTemperatureData.get(blockName).add(tempData); + } + } + } catch (Exception e) { + ToughAsNails.logger + .error("Error parsing block temperature config from json: " + + blockTemperatureConfigFile.toString(), e); + } + + // Material temperature config: + materialTemperatureData = new MaterialTemperatureData(); + + File materialTemperatureConfigFile = new File(configDir, + "material_temperature.json"); + + try { + if (!materialTemperatureConfigFile.exists()) { + writeFile(materialTemperatureConfigFile, + materialTemperatureData); + } + } catch (Exception e) { + ToughAsNails.logger + .error("Error creating default material temperature config file: " + + materialTemperatureConfigFile.toString(), e); + } + + try { + String materialJsonString = FileUtils + .readFileToString(materialTemperatureConfigFile); + + Gson gson = new Gson(); + + materialTemperatureData = gson.fromJson(materialJsonString, + MaterialTemperatureData.class); + } catch (Exception e) { + ToughAsNails.logger + .error("Error parsing material temperature config from json: " + + materialTemperatureConfigFile.toString(), e); + } + + } + + protected static boolean writeFile(File outputFile, Object obj) { + try { + FileUtils.write(outputFile, serializer.toJson(obj)); + return true; + } catch (Exception e) { + ToughAsNails.logger.error("Error writing config file " + + outputFile.getAbsolutePath() + ": " + e.getMessage()); + return false; + } + } + + protected static Map parse(String jsonString) { + Map members; + + members = new HashMap(); + if (jsonString == null) { + return members; + } + + JsonElement rootElement = null; + try { + rootElement = parser.parse(jsonString); + if (rootElement != null) { + if (rootElement.isJsonObject()) { + for (Entry entry : rootElement + .getAsJsonObject().entrySet()) { + members.put(entry.getKey(), entry.getValue()); + } + } else { + ToughAsNails.logger + .error("Error parsing config: not a JSON object"); + } + } + } catch (Exception e) { + ToughAsNails.logger + .error("Error parsing config: " + e.getMessage()); + } + + return members; + } + + protected static ReadBlockState asBlockState(JsonElement ele, + String extraPrefix) { + + try { + + JsonObject obj = ele.getAsJsonObject(); + + // attempt to load the specified block + if (!obj.has("block")) { + ToughAsNails.logger.error(extraPrefix + " Block name missing"); + return null; + } + JsonElement blockName = obj.get("block"); + if (!blockName.isJsonPrimitive()) { + ToughAsNails.logger.error( + extraPrefix + " Invalid block name - must be a string"); + return null; + } + Block block = Block.getBlockFromName(blockName.getAsString()); + if (block == null) { + ToughAsNails.logger + .error(extraPrefix + " Unrecognised block name " + + blockName.getAsString()); + return null; + } + + IBlockState state = block.getDefaultState(); + + ArrayList usedProperties = new ArrayList(); + + // attempt to add properties + if (obj.has("properties")) { + JsonElement properties = obj.get("properties"); + if (!properties.isJsonObject()) { + ToughAsNails.logger.error(extraPrefix + + " Invalid properties list - must be a JSON object"); + return null; + } + + for (Entry entry : properties + .getAsJsonObject().entrySet()) { + String propRawName = entry.getKey(); + + IProperty property = BlockStateUtils + .getPropertyByName(state, propRawName); + if (property != null) { + String propRawValue = entry.getValue().getAsString(); + + if (!propRawValue.equals("*")) { + Comparable propertyValue = BlockStateUtils + .getPropertyValueByName(state, property, + propRawValue); + if (propertyValue != null) { + state = state.withProperty(property, + propertyValue); + usedProperties.add(propRawName); + } else { + ToughAsNails.logger.error(extraPrefix + + " Invalid value " + propRawValue + + " for property " + propRawName); + } + } + } else { + ToughAsNails.logger.error(extraPrefix + + " Invalid property name: " + propRawName); + } + } + } + + return new ReadBlockState(state, + usedProperties.toArray(new String[0])); + } catch (Exception e) { + ToughAsNails.logger.error(extraPrefix + + " Error fetching blockstate: " + e.getMessage()); + return null; + } + + } + + protected static JsonObject asJsonObject(IBlockState state, + String[] useProperties) { + try { + JsonObject obj = new JsonObject(); + + obj.addProperty("block", + state.getBlock().getRegistryName().toString()); + + JsonObject props = new JsonObject(); + + for (IProperty blockProperty : state.getProperties().keySet()) { + String propName = blockProperty.getName(); + String propValue = state.getValue(blockProperty).toString(); + + if (useProperties != null) { + boolean foundProp = false; + for (String useName : useProperties) { + if (useName.equalsIgnoreCase(propName)) { + foundProp = true; + break; + } + } + + if (!foundProp) { + // If a property is unused, set the value to a special + // wildcard * to indicate that any value matches + propValue = "*"; + } + } + + props.addProperty(propName, propValue); + } + + obj.add("properties", props); + + return obj; + } catch (Exception e) { + ToughAsNails.logger.error( + "Error converting blockstate to Json: " + e.getMessage()); + return null; + } + } + + protected static JsonObject asJsonObject(IBlockState state) { + try { + return asJsonObject(state, null); + } catch (Exception e) { + ToughAsNails.logger.error( + "Error converting blockstate to Json: " + e.getMessage()); + return null; + } + } + + protected static JsonObject asJsonObject( + BlockTemperatureData blockTemperatureData) { + JsonObject blockTempJson = new JsonObject(); + + JsonObject stateObject = asJsonObject(blockTemperatureData.state, + blockTemperatureData.useProperties); + + blockTempJson.add("state", stateObject); + blockTempJson.addProperty("temperature", + blockTemperatureData.blockTemperature); + + return blockTempJson; + } + + protected static BlockTemperatureData asBlockTemperatureData( + JsonElement ele, String extraPrefix) { + try { + JsonObject obj = ele.getAsJsonObject(); + + // attempt to load the state + if (!obj.has("state")) { + ToughAsNails.logger.error(extraPrefix + " Block state missing"); + return null; + } + JsonElement blockState = obj.get("state"); + if (!blockState.isJsonObject()) { + ToughAsNails.logger.error(extraPrefix + + " Invalid block state - must be an object"); + return null; + } + + ReadBlockState readState = asBlockState(blockState, extraPrefix); + + IBlockState state = readState.state; + String[] use_properties = readState.usedProperties; + + // attempt to get the temperature value + if (!obj.has("temperature")) { + ToughAsNails.logger + .error(extraPrefix + " block temperature missing"); + return null; + } + JsonElement blockTemperature = obj.get("temperature"); + if (!blockTemperature.isJsonPrimitive()) { + ToughAsNails.logger.error(extraPrefix + + " Invalid block temperature - must be a float"); + } + + float temperature = blockTemperature.getAsFloat(); + + return new BlockTemperatureData(state, use_properties, temperature); + } catch (Exception e) { + ToughAsNails.logger.error( + extraPrefix + " Error fetching block temperature data: " + + e.getMessage()); + return null; + } + + } } final class ReadBlockState { - IBlockState state; - String[] usedProperties; - - public ReadBlockState(IBlockState state, String[] usedProperties) - { - this.state = state; - this.usedProperties = usedProperties; - } + IBlockState state; + String[] usedProperties; + + public ReadBlockState(IBlockState state, String[] usedProperties) { + this.state = state; + this.usedProperties = usedProperties; + } } diff --git a/src/main/java/toughasnails/handler/BlockHarvestEventHandler.java b/src/main/java/toughasnails/handler/BlockHarvestEventHandler.java index 7d5d7be5..bdc1832e 100644 --- a/src/main/java/toughasnails/handler/BlockHarvestEventHandler.java +++ b/src/main/java/toughasnails/handler/BlockHarvestEventHandler.java @@ -9,21 +9,19 @@ import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; import toughasnails.api.item.TANItems; -public class BlockHarvestEventHandler -{ +public class BlockHarvestEventHandler { @SubscribeEvent - public void onBlockBreak(HarvestDropsEvent event) - { - IBlockState state = event.getState(); - int fortune = event.getFortuneLevel(); - + public void onBlockBreak(HarvestDropsEvent event) { + IBlockState state = event.getState(); + // int fortune = event.getFortuneLevel(); + if (event.getHarvester() == null || event.isSilkTouching()) - return; - - if (state.getBlock() == Blocks.ICE && event.getHarvester() != null) - { - event.getDrops().clear(); - event.getDrops().add(new ItemStack(TANItems.ice_cube, new Random().nextInt(2))); - } - } + return; + + if (state.getBlock() == Blocks.ICE && event.getHarvester() != null) { + event.getDrops().clear(); + event.getDrops().add( + new ItemStack(TANItems.ice_cube, new Random().nextInt(2))); + } + } } diff --git a/src/main/java/toughasnails/handler/season/SeasonHandler.java b/src/main/java/toughasnails/handler/season/SeasonHandler.java index d545972d..71749855 100644 --- a/src/main/java/toughasnails/handler/season/SeasonHandler.java +++ b/src/main/java/toughasnails/handler/season/SeasonHandler.java @@ -7,122 +7,261 @@ ******************************************************************************/ package toughasnails.handler.season; +import net.minecraft.block.Block; import net.minecraft.client.Minecraft; import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; import net.minecraft.world.storage.MapStorage; +import net.minecraftforge.event.world.BlockEvent; +import net.minecraftforge.fml.common.eventhandler.Event; +import net.minecraftforge.fml.common.eventhandler.Event.Result; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; import net.minecraftforge.fml.common.gameevent.PlayerEvent.PlayerLoggedInEvent; import net.minecraftforge.fml.common.gameevent.TickEvent; +import toughasnails.api.config.GameplayOption; import toughasnails.api.config.SyncedConfig; +import toughasnails.api.season.IDecayableCrop; +import toughasnails.api.season.IHibernatingCrop; import toughasnails.api.season.ISeasonData; +import toughasnails.api.season.Season; import toughasnails.api.season.Season.SubSeason; -import toughasnails.api.config.GameplayOption; +import toughasnails.api.season.SeasonHelper; +import toughasnails.api.temperature.Temperature; +import toughasnails.api.temperature.TemperatureHelper; +import toughasnails.config.CropGrowConfigEntry; +import toughasnails.config.GameplayConfigurationHandler; import toughasnails.handler.PacketHandler; import toughasnails.network.message.MessageSyncSeasonCycle; import toughasnails.season.SeasonSavedData; import toughasnails.season.SeasonTime; +import toughasnails.temperature.TemperatureHandler; + +public class SeasonHandler { + + @SubscribeEvent + public void onWorldTick(TickEvent.WorldTickEvent event) { + World world = event.world; + + if (event.phase == TickEvent.Phase.END && !world.isRemote + && world.provider.getDimension() == 0 && SyncedConfig + .getBooleanValue(GameplayOption.ENABLE_SEASONS)) { + SeasonSavedData savedData = getSeasonSavedData(world); + + if (savedData.seasonCycleTicks++ > SeasonTime.TOTAL_CYCLE_TICKS) { + savedData.seasonCycleTicks = 0; + } + + if (savedData.seasonCycleTicks % 20 == 0) { + sendSeasonUpdate(world); + } + + savedData.markDirty(); + } + } + + @SubscribeEvent + public void onPlayerLogin(PlayerLoggedInEvent event) { + EntityPlayer player = event.player; + World world = player.worldObj; + + sendSeasonUpdate(world); + } + + private SubSeason lastSeason = null; + public static int clientSeasonCycleTicks = 0; + + @SubscribeEvent + public void onClientTick(TickEvent.ClientTickEvent event) { + // Only do this when in the world + if (Minecraft.getMinecraft().thePlayer == null) + return; + + int dimension = Minecraft.getMinecraft().thePlayer.dimension; + + if (event.phase == TickEvent.Phase.END && dimension == 0 && SyncedConfig + .getBooleanValue(GameplayOption.ENABLE_SEASONS)) { + // Keep ticking as we're synchronized with the server only every + // second + if (clientSeasonCycleTicks++ > SeasonTime.TOTAL_CYCLE_TICKS) { + clientSeasonCycleTicks = 0; + } + + SeasonTime calendar = new SeasonTime(clientSeasonCycleTicks); + + if (calendar.getSubSeason() != lastSeason) { + Minecraft.getMinecraft().renderGlobal.loadRenderers(); + lastSeason = calendar.getSubSeason(); + } + } + } + + public static void sendSeasonUpdate(World world) { + if (!world.isRemote && SyncedConfig + .getBooleanValue(GameplayOption.ENABLE_SEASONS)) { + SeasonSavedData savedData = getSeasonSavedData(world); + PacketHandler.instance.sendToAll( + new MessageSyncSeasonCycle(savedData.seasonCycleTicks)); + } + } + + public static SeasonSavedData getSeasonSavedData(World world) { + MapStorage mapStorage = world.getPerWorldStorage(); + SeasonSavedData savedData = (SeasonSavedData) mapStorage.getOrLoadData( + SeasonSavedData.class, SeasonSavedData.DATA_IDENTIFIER); + + // If the saved data file hasn't been created before, create it + if (savedData == null) { + savedData = new SeasonSavedData(SeasonSavedData.DATA_IDENTIFIER); + mapStorage.setData(SeasonSavedData.DATA_IDENTIFIER, savedData); + savedData.markDirty(); // Mark for saving + } + + return savedData; + } + + /* + * Check for hibernating crops attempting to grow in the cold or heat, and + * block them. + */ + @SubscribeEvent + public void onCropGrowPre(BlockEvent.CropGrowEvent.Pre event) { + Block block = event.getState().getBlock(); + String blockName = block.getRegistryName().toString(); + BlockPos pos = event.getPos(); + World world = event.getWorld(); + boolean temperatureWithering = SyncedConfig + .getBooleanValue(GameplayOption.TEMPERATURE_WITHERING); + if (temperatureWithering) { + int minHibernate = 0; + int minOptimal = 0; + int maxOptimal = 0; + int maxHibernate = 0; + float nonOptimalChance = 0; + // Assign crop hibernation details from config file. + if (GameplayConfigurationHandler.EXTERNAL_HIBERNATING_CROPS + .containsKey(blockName)) { + CropGrowConfigEntry cropData = GameplayConfigurationHandler.EXTERNAL_HIBERNATING_CROPS + .get(blockName); + minHibernate = cropData.getMinLiving(); + minOptimal = cropData.getMinOptimal(); + maxOptimal = cropData.getMaxOptimal(); + maxHibernate = cropData.getMaxLiving(); + nonOptimalChance = cropData.getNonOptimalChance(); + System.out.println(blockName + " using spec " + minHibernate + + ", " + minOptimal + ", " + maxOptimal + ", " + + maxHibernate + ", " + nonOptimalChance); + + // Otherwise, assign defaults. + } else if (block instanceof IHibernatingCrop + && ((IHibernatingCrop) block).shouldHibernate()) { + minHibernate = 5; + minOptimal = 10; + maxOptimal = 15; + maxHibernate = 20; + nonOptimalChance = 0.5f; + System.out.println(blockName + " using default."); + } else { + System.out.println(blockName + " is not a hibernater."); + return; + } + + // Alive but not optimal, slow growth + int targetTemperature = TemperatureHandler + .getTargetTemperatureAt(world, pos); + if ((targetTemperature > maxOptimal + && targetTemperature <= maxHibernate) + || (targetTemperature < minOptimal + && targetTemperature >= minHibernate) + && (Math.random() < nonOptimalChance)) { + System.out.println("non-optimal blocking"); + event.setResult(Result.DENY); + } else if (targetTemperature < minHibernate + || targetTemperature > maxHibernate) { + System.out.println("temp exceeded blocking"); + event.setResult(Result.DENY); + } + } else { + Season season = SeasonHelper.getSeasonData(world).getSubSeason() + .getSeason(); + if (season == Season.WINTER + && (block instanceof IHibernatingCrop + && ((IHibernatingCrop) block).shouldHibernate()) + && !TemperatureHelper.isPosClimatisedForTemp(world, pos, + new Temperature(1)) + && SyncedConfig + .getBooleanValue(GameplayOption.ENABLE_SEASONS)) { + event.setResult(Event.Result.DENY); + } + } + } + + /* + * Detect crop grow events and check for the optimal growth condition. + */ + @SubscribeEvent + public void onCropGrowEvent(BlockEvent.CropGrowEvent event) { + if (!event.getWorld().isRemote) { + // This type of growth only applies when temperature decay is + // enabled. + Block block = event.getState().getBlock(); + World world = event.getWorld(); + BlockPos pos = event.getPos(); + String blockName = block.getRegistryName().toString(); + boolean temperatureWithering = SyncedConfig + .getBooleanValue(GameplayOption.TEMPERATURE_WITHERING); + if (temperatureWithering) { + int minLiving = 0; + int minOptimal = 0; + int maxOptimal = 0; + int maxLiving = 0; + float nonOptimalChance = 0; + // Assign crop life details from config file. + if (GameplayConfigurationHandler.EXTERNAL_DECAYING_CROPS + .containsKey(blockName)) { + CropGrowConfigEntry cropData = GameplayConfigurationHandler.EXTERNAL_DECAYING_CROPS + .get(blockName); + minLiving = cropData.getMinLiving(); + minOptimal = cropData.getMinOptimal(); + maxOptimal = cropData.getMaxOptimal(); + maxLiving = cropData.getMaxLiving(); + nonOptimalChance = cropData.getNonOptimalChance(); + + // Otherwise, assign defaults. + } else if (block instanceof IDecayableCrop + && ((IDecayableCrop) block).shouldDecay()) { + minLiving = 5; + minOptimal = 10; + maxOptimal = 15; + maxLiving = 20; + nonOptimalChance = 0.5f; + } else { + return; + } + + // Alive but not optimal, slow growth + int targetTemperature = TemperatureHandler + .getTargetTemperatureAt(world, pos); + if ((targetTemperature > maxOptimal + && targetTemperature <= maxLiving) + || (targetTemperature < minOptimal + && targetTemperature >= minLiving) + && (Math.random() < nonOptimalChance)) { + event.setResult(Result.DENY); + } + } + } + } + + // + // Used to implement getSeasonData in the API + // + public static ISeasonData getServerSeasonData(World world) { + SeasonSavedData savedData = getSeasonSavedData(world); + return new SeasonTime(savedData.seasonCycleTicks); + } -public class SeasonHandler -{ - @SubscribeEvent - public void onWorldTick(TickEvent.WorldTickEvent event) - { - World world = event.world; - - if (event.phase == TickEvent.Phase.END && !world.isRemote && world.provider.getDimension() == 0 && SyncedConfig.getBooleanValue(GameplayOption.ENABLE_SEASONS)) - { - SeasonSavedData savedData = getSeasonSavedData(world); - - if (savedData.seasonCycleTicks++ > SeasonTime.TOTAL_CYCLE_TICKS) - { - savedData.seasonCycleTicks = 0; - } - - if (savedData.seasonCycleTicks % 20 == 0) - { - sendSeasonUpdate(world); - } - - savedData.markDirty(); - } - } - - @SubscribeEvent - public void onPlayerLogin(PlayerLoggedInEvent event) - { - EntityPlayer player = event.player; - World world = player.worldObj; - - sendSeasonUpdate(world); - } - - private SubSeason lastSeason = null; - public static int clientSeasonCycleTicks = 0; - - @SubscribeEvent - public void onClientTick(TickEvent.ClientTickEvent event) - { - //Only do this when in the world - if (Minecraft.getMinecraft().thePlayer == null) return; - - int dimension = Minecraft.getMinecraft().thePlayer.dimension; - - if (event.phase == TickEvent.Phase.END && dimension == 0 && SyncedConfig.getBooleanValue(GameplayOption.ENABLE_SEASONS)) - { - //Keep ticking as we're synchronized with the server only every second - if (clientSeasonCycleTicks++ > SeasonTime.TOTAL_CYCLE_TICKS) - { - clientSeasonCycleTicks = 0; - } - - SeasonTime calendar = new SeasonTime(clientSeasonCycleTicks); - - if (calendar.getSubSeason() != lastSeason) - { - Minecraft.getMinecraft().renderGlobal.loadRenderers(); - lastSeason = calendar.getSubSeason(); - } - } - } - - public static void sendSeasonUpdate(World world) - { - if (!world.isRemote && SyncedConfig.getBooleanValue(GameplayOption.ENABLE_SEASONS)) - { - SeasonSavedData savedData = getSeasonSavedData(world); - PacketHandler.instance.sendToAll(new MessageSyncSeasonCycle(savedData.seasonCycleTicks)); - } - } - - public static SeasonSavedData getSeasonSavedData(World world) - { - MapStorage mapStorage = world.getPerWorldStorage(); - SeasonSavedData savedData = (SeasonSavedData)mapStorage.getOrLoadData(SeasonSavedData.class, SeasonSavedData.DATA_IDENTIFIER); - - //If the saved data file hasn't been created before, create it - if (savedData == null) - { - savedData = new SeasonSavedData(SeasonSavedData.DATA_IDENTIFIER); - mapStorage.setData(SeasonSavedData.DATA_IDENTIFIER, savedData); - savedData.markDirty(); //Mark for saving - } - - return savedData; - } - - // - // Used to implement getSeasonData in the API - // - - public static ISeasonData getServerSeasonData(World world) - { - SeasonSavedData savedData = getSeasonSavedData(world); - return new SeasonTime(savedData.seasonCycleTicks); - } - - public static ISeasonData getClientSeasonData() - { - return new SeasonTime(clientSeasonCycleTicks); - } + public static ISeasonData getClientSeasonData() { + return new SeasonTime(clientSeasonCycleTicks); + } } diff --git a/src/main/java/toughasnails/handler/thirst/VanillaDrinkHandler.java b/src/main/java/toughasnails/handler/thirst/VanillaDrinkHandler.java index 936db28d..88780791 100644 --- a/src/main/java/toughasnails/handler/thirst/VanillaDrinkHandler.java +++ b/src/main/java/toughasnails/handler/thirst/VanillaDrinkHandler.java @@ -22,6 +22,7 @@ import toughasnails.api.config.GameplayOption; import toughasnails.api.config.SyncedConfig; import toughasnails.api.thirst.ThirstHelper; +import toughasnails.core.ToughAsNails; import toughasnails.thirst.ThirstHandler; public class VanillaDrinkHandler { @@ -90,8 +91,8 @@ public void onItemUseFinish(LivingEntityUseItemEvent.Finish event) { TANPotions.thirst, 600)); } } catch (NumberFormatException e) { - System.out - .println("Tried to drink misconfigured " + ToughAsNails.logger + .error("Tried to drink misconfigured " + itemName); } break; diff --git a/src/main/java/toughasnails/item/ItemThermometer.java b/src/main/java/toughasnails/item/ItemThermometer.java index 7c33a5b1..de475473 100644 --- a/src/main/java/toughasnails/item/ItemThermometer.java +++ b/src/main/java/toughasnails/item/ItemThermometer.java @@ -7,29 +7,31 @@ ******************************************************************************/ package toughasnails.item; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.IItemPropertyGetter; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; +import net.minecraft.util.ActionResult; +import net.minecraft.util.EnumActionResult; +import net.minecraft.util.EnumHand; import net.minecraft.util.ResourceLocation; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.MathHelper; +import net.minecraft.util.text.TextComponentTranslation; import net.minecraft.world.World; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; -import toughasnails.api.temperature.Temperature; +import toughasnails.api.config.GameplayOption; +import toughasnails.api.config.SyncedConfig; import toughasnails.api.temperature.TemperatureHelper; import toughasnails.api.temperature.TemperatureScale; -import toughasnails.temperature.TemperatureDebugger; import toughasnails.temperature.TemperatureHandler; -import toughasnails.temperature.modifier.AltitudeModifier; -import toughasnails.temperature.modifier.BiomeModifier; -import toughasnails.temperature.modifier.ObjectProximityModifier; -import toughasnails.temperature.modifier.SeasonModifier; -import toughasnails.temperature.modifier.TimeModifier; -import toughasnails.temperature.modifier.WeatherModifier; public class ItemThermometer extends Item { public ItemThermometer() { @@ -46,55 +48,42 @@ public ItemThermometer() { @SideOnly(Side.CLIENT) public float apply(ItemStack stack, World world, EntityLivingBase entity) { + boolean overrideThermometerLimits = SyncedConfig + .getBooleanValue( + GameplayOption.OVERRIDE_THERMOMETER_LIMITS); + int lowerBound = SyncedConfig.getIntegerValue( + GameplayOption.THERMOMETER_LOWER_BOUND); + int upperBound = SyncedConfig.getIntegerValue( + GameplayOption.THERMOMETER_UPPER_BOUND); + if (entity == null || !(entity instanceof EntityPlayer)) { Entity frame = stack.getItemFrame(); if (frame != null) { BlockPos framePosition = frame.getPosition(); World frameWorld = frame.getEntityWorld(); - final TemperatureDebugger debugger = new TemperatureDebugger(); - AltitudeModifier altitudeModifier = new AltitudeModifier( - debugger); - BiomeModifier biomeModifier = new BiomeModifier( - debugger); - ObjectProximityModifier objectProximityModifier = new ObjectProximityModifier( - debugger); - WeatherModifier weatherModifier = new WeatherModifier( - debugger); - TimeModifier timeModifier = new TimeModifier( - debugger); - SeasonModifier seasonModifier = new SeasonModifier( - debugger); + int finalTemperature = TemperatureHandler + .getTargetTemperatureAt(frameWorld, + framePosition); - Temperature baseTemperature = new Temperature( - TemperatureHandler.TEMPERATURE_SCALE_MIDPOINT); - Temperature targetTemperature = biomeModifier - .modifyTarget(frameWorld, framePosition, - baseTemperature); - targetTemperature = altitudeModifier - .modifyTarget(frameWorld, framePosition, - targetTemperature); - targetTemperature = objectProximityModifier - .modifyTarget(frameWorld, framePosition, - targetTemperature); - targetTemperature = weatherModifier - .modifyTarget(frameWorld, framePosition, - targetTemperature); - targetTemperature = timeModifier.modifyTarget( - frameWorld, framePosition, - targetTemperature); - targetTemperature = seasonModifier.modifyTarget( - frameWorld, framePosition, - targetTemperature); - - int finalTemperature = targetTemperature - .getRawValue(); - return (float) MathHelper - .clamp_double(finalTemperature, 0, - TemperatureScale - .getScaleTotal()) - / (float) TemperatureScale - .getScaleTotal(); + if (overrideThermometerLimits) { + float clampedTemp = (float) MathHelper + .clamp_double(finalTemperature, + lowerBound, upperBound); + float shiftedTemp = (clampedTemp + - lowerBound); + float needlePosition = shiftedTemp + / ((float) (upperBound + - lowerBound)); + return needlePosition; + } else { + return (float) MathHelper + .clamp_double(finalTemperature, 0, + TemperatureScale + .getScaleTotal()) + / (float) TemperatureScale + .getScaleTotal(); + } } else { return 0.0f; } @@ -107,11 +96,43 @@ public float apply(ItemStack stack, World world, TemperatureHandler tempHandler = (TemperatureHandler) TemperatureHelper .getTemperatureData(player); - return (float) MathHelper.clamp_double( - tempHandler.debugger.targetTemperature, 0, - TemperatureScale.getScaleTotal()) - / (float) TemperatureScale.getScaleTotal(); + int finalTemperature = tempHandler.debugger.targetTemperature; + if (overrideThermometerLimits) { + float clampedTemp = (float) MathHelper.clamp_double( + finalTemperature, lowerBound, upperBound); + float shiftedTemp = (clampedTemp - lowerBound); + return shiftedTemp + / ((float) (upperBound - lowerBound)); + } else { + return (float) MathHelper.clamp_double( + finalTemperature, 0, + TemperatureScale.getScaleTotal()) + / (float) TemperatureScale.getScaleTotal(); + } } }); } + + Map messageDebounce = new HashMap(); + + @Override + public ActionResult onItemRightClick(ItemStack itemStackIn, + World world, EntityPlayer player, EnumHand hand) { + if (!world.isRemote) { + // Get the temperature of the world at the player's location. + if (!messageDebounce.containsKey(player.getUniqueID()) + || (System.currentTimeMillis() - messageDebounce + .get(player.getUniqueID()) > 2000)) { + BlockPos playerPosition = player.getPosition(); + int finalTemperature = TemperatureHandler + .getTargetTemperatureAt(world, playerPosition); + + player.addChatMessage(new TextComponentTranslation( + "item.thermometer.read", finalTemperature)); + messageDebounce.put(player.getUniqueID(), + System.currentTimeMillis()); + } + } + return new ActionResult(EnumActionResult.PASS, itemStackIn); + } } diff --git a/src/main/java/toughasnails/season/SeasonASMHelper.java b/src/main/java/toughasnails/season/SeasonASMHelper.java index fcfda493..6816535b 100644 --- a/src/main/java/toughasnails/season/SeasonASMHelper.java +++ b/src/main/java/toughasnails/season/SeasonASMHelper.java @@ -20,134 +20,174 @@ import toughasnails.api.config.GameplayOption; import toughasnails.api.config.SyncedConfig; import toughasnails.api.season.IDecayableCrop; -import toughasnails.api.season.IHibernatingCrop; import toughasnails.api.season.Season; import toughasnails.api.season.SeasonHelper; import toughasnails.api.temperature.Temperature; import toughasnails.api.temperature.TemperatureHelper; +import toughasnails.config.CropGrowConfigEntry; +import toughasnails.config.GameplayConfigurationHandler; import toughasnails.handler.season.SeasonHandler; +import toughasnails.temperature.TemperatureHandler; -public class SeasonASMHelper -{ - /////////////////// - // World methods // - /////////////////// - - public static boolean canSnowAtInSeason(World world, BlockPos pos, boolean checkLight, Season season) - { - Biome biome = world.getBiomeGenForCoords(pos); - float temperature = biome.getFloatTemperature(pos); - - //If we're in winter, the temperature can be anything equal to or below 0.7 - if (!SeasonHelper.canSnowAtTempInSeason(season, temperature)) - { - return false; - } - else if (biome == Biomes.RIVER || biome == Biomes.OCEAN || biome == Biomes.DEEP_OCEAN) - { - return false; - } - else if (checkLight) - { - if (pos.getY() >= 0 && pos.getY() < 256 && world.getLightFor(EnumSkyBlock.BLOCK, pos) < 10) - { - IBlockState state = world.getBlockState(pos); - - if (state.getBlock().isAir(state, world, pos) && Blocks.SNOW_LAYER.canPlaceBlockAt(world, pos)) - { - return true; - } - } - - return false; - } - - return true; - } - - public static boolean canBlockFreezeInSeason(World world, BlockPos pos, boolean noWaterAdj, Season season) - { - Biome Biome = world.getBiomeGenForCoords(pos); - float temperature = Biome.getFloatTemperature(pos); - - //If we're in winter, the temperature can be anything equal to or below 0.7 - if (!SeasonHelper.canSnowAtTempInSeason(season, temperature)) - { - return false; - } - else if (Biome == Biomes.RIVER || Biome == Biomes.OCEAN || Biome == Biomes.DEEP_OCEAN) - { - return false; - } - else - { - if (pos.getY() >= 0 && pos.getY() < 256 && world.getLightFor(EnumSkyBlock.BLOCK, pos) < 10) - { - IBlockState iblockstate = world.getBlockState(pos); - Block block = iblockstate.getBlock(); - - if ((block == Blocks.WATER || block == Blocks.FLOWING_WATER) && ((Integer)iblockstate.getValue(BlockLiquid.LEVEL)).intValue() == 0) - { - if (!noWaterAdj) - { - return true; - } - - boolean flag = world.isWater(pos.west()) && world.isWater(pos.east()) && world.isWater(pos.north()) && world.isWater(pos.south()); - - if (!flag) - { - return true; - } - } - } - - return false; - } - } - - public static boolean isRainingAtInSeason(World world, BlockPos pos, Season season) - { - Biome biome = world.getBiomeGenForCoords(pos); - return biome.getEnableSnow() && season != Season.WINTER ? false : (world.canSnowAt(pos, false) ? false : biome.canRain()); - } - - /////////////////// - // Biome methods // - /////////////////// - - public static float getFloatTemperature(Biome biome, BlockPos pos) - { - Season season = new SeasonTime(SeasonHandler.clientSeasonCycleTicks).getSubSeason().getSeason(); - - if (biome.getTemperature() <= 0.7F && season == Season.WINTER && SyncedConfig.getBooleanValue(GameplayOption.ENABLE_SEASONS)) - { - return 0.0F; - } - else - { - return biome.getFloatTemperature(pos); - } - } - - //////////////////////// - // BlockCrops methods // - //////////////////////// - - public static void onUpdateTick(Block block, World world, BlockPos pos) - { - Season season = SeasonHelper.getSeasonData(world).getSubSeason().getSeason(); - - if (season == Season.WINTER && block instanceof IDecayableCrop && !TemperatureHelper.isPosClimatisedForTemp(world, pos, new Temperature(1)) && SyncedConfig.getBooleanValue(GameplayOption.ENABLE_SEASONS)) - { - world.setBlockState(pos, TANBlocks.dead_crops.getDefaultState()); - } - } - - public static boolean shouldHibernate(Block block, World world, BlockPos pos) - { - Season season = SeasonHelper.getSeasonData(world).getSubSeason().getSeason(); - - return (season == Season.WINTER && block instanceof IHibernatingCrop && !TemperatureHelper.isPosClimatisedForTemp(world, pos, new Temperature(1)) && SyncedConfig.getBooleanValue(GameplayOption.ENABLE_SEASONS)); - } +public class SeasonASMHelper { + /////////////////// + // World methods // + /////////////////// + + public static boolean canSnowAtInSeason(World world, BlockPos pos, + boolean checkLight, Season season) { + Biome biome = world.getBiomeGenForCoords(pos); + float temperature = biome.getFloatTemperature(pos); + + // If we're in winter, the temperature can be anything equal to or below + // 0.7 + if (!SeasonHelper.canSnowAtTempInSeason(season, temperature)) { + return false; + } else if (biome == Biomes.RIVER || biome == Biomes.OCEAN + || biome == Biomes.DEEP_OCEAN) { + return false; + } else if (checkLight) { + if (pos.getY() >= 0 && pos.getY() < 256 + && world.getLightFor(EnumSkyBlock.BLOCK, pos) < 10) { + IBlockState state = world.getBlockState(pos); + + if (state.getBlock().isAir(state, world, pos) + && Blocks.SNOW_LAYER.canPlaceBlockAt(world, pos)) { + return true; + } + } + + return false; + } + + return true; + } + + public static boolean canBlockFreezeInSeason(World world, BlockPos pos, + boolean noWaterAdj, Season season) { + Biome Biome = world.getBiomeGenForCoords(pos); + float temperature = Biome.getFloatTemperature(pos); + + // If we're in winter, the temperature can be anything equal to or below + // 0.7 + if (!SeasonHelper.canSnowAtTempInSeason(season, temperature)) { + return false; + } else if (Biome == Biomes.RIVER || Biome == Biomes.OCEAN + || Biome == Biomes.DEEP_OCEAN) { + return false; + } else { + if (pos.getY() >= 0 && pos.getY() < 256 + && world.getLightFor(EnumSkyBlock.BLOCK, pos) < 10) { + IBlockState iblockstate = world.getBlockState(pos); + Block block = iblockstate.getBlock(); + + if ((block == Blocks.WATER || block == Blocks.FLOWING_WATER) + && ((Integer) iblockstate.getValue(BlockLiquid.LEVEL)) + .intValue() == 0) { + if (!noWaterAdj) { + return true; + } + + boolean flag = world.isWater(pos.west()) + && world.isWater(pos.east()) + && world.isWater(pos.north()) + && world.isWater(pos.south()); + + if (!flag) { + return true; + } + } + } + + return false; + } + } + + public static boolean isRainingAtInSeason(World world, BlockPos pos, + Season season) { + Biome biome = world.getBiomeGenForCoords(pos); + return biome.getEnableSnow() && season != Season.WINTER ? false + : (world.canSnowAt(pos, false) ? false : biome.canRain()); + } + + /////////////////// + // Biome methods // + /////////////////// + + public static float getFloatTemperature(Biome biome, BlockPos pos) { + Season season = new SeasonTime(SeasonHandler.clientSeasonCycleTicks) + .getSubSeason().getSeason(); + + if (biome.getTemperature() <= 0.7F && season == Season.WINTER + && SyncedConfig + .getBooleanValue(GameplayOption.ENABLE_SEASONS)) { + return 0.0F; + } else { + return biome.getFloatTemperature(pos); + } + } + + //////////////////////// + // BlockCrops methods // + //////////////////////// + + public static void onUpdateTick(Block block, World world, BlockPos pos) { + + // Should withering be based on the season, or on temperature? + String blockName = block.getRegistryName().toString(); + boolean temperatureWithering = SyncedConfig + .getBooleanValue(GameplayOption.TEMPERATURE_WITHERING); + if (!temperatureWithering) { + Season season = SeasonHelper.getSeasonData(world).getSubSeason() + .getSeason(); + + if (season == Season.WINTER + && !TemperatureHelper.isPosClimatisedForTemp(world, pos, + new Temperature(1)) + && SyncedConfig + .getBooleanValue(GameplayOption.ENABLE_SEASONS)) { + + // Kill those crops which implement the decaying API or + // externally-specified. + if (block instanceof IDecayableCrop + && ((IDecayableCrop) block).shouldDecay()) { + world.setBlockState(pos, + TANBlocks.dead_crops.getDefaultState()); + } else if (GameplayConfigurationHandler.EXTERNAL_DECAYING_CROPS + .containsKey(blockName)) { + world.setBlockState(pos, + TANBlocks.dead_crops.getDefaultState()); + } + } + } else { + int minLiving = 0; + int maxLiving = 0; + + // Assign crop life details from config file. + if (GameplayConfigurationHandler.EXTERNAL_DECAYING_CROPS + .containsKey(blockName)) { + CropGrowConfigEntry cropData = GameplayConfigurationHandler.EXTERNAL_DECAYING_CROPS + .get(blockName); + minLiving = cropData.getMinLiving(); + maxLiving = cropData.getMaxLiving(); + + // Otherwise, assign defaults. + } else if (block instanceof IDecayableCrop + && ((IDecayableCrop) block).shouldDecay()) { + minLiving = 5; + maxLiving = 20; + } else { + return; + } + + // Kill the crop if it exceeds temperature bounds. + int targetTemperature = TemperatureHandler + .getTargetTemperatureAt(world, pos); + if (targetTemperature > maxLiving + || targetTemperature < minLiving) { + world.setBlockState(pos, + TANBlocks.dead_crops.getDefaultState()); + } + } + } } diff --git a/src/main/java/toughasnails/temperature/TemperatureHandler.java b/src/main/java/toughasnails/temperature/TemperatureHandler.java index e000c504..da6cdb38 100644 --- a/src/main/java/toughasnails/temperature/TemperatureHandler.java +++ b/src/main/java/toughasnails/temperature/TemperatureHandler.java @@ -9,19 +9,20 @@ import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.potion.PotionEffect; +import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.MathHelper; import net.minecraft.world.World; import net.minecraftforge.fml.common.gameevent.TickEvent.Phase; import net.minecraftforge.fml.common.network.simpleimpl.IMessage; -import toughasnails.api.config.SyncedConfig; import toughasnails.api.TANCapabilities; import toughasnails.api.TANPotions; +import toughasnails.api.config.GameplayOption; +import toughasnails.api.config.SyncedConfig; import toughasnails.api.stat.StatHandlerBase; import toughasnails.api.stat.capability.ITemperature; import toughasnails.api.temperature.Temperature; import toughasnails.api.temperature.TemperatureScale; import toughasnails.api.temperature.TemperatureScale.TemperatureRange; -import toughasnails.api.config.GameplayOption; import toughasnails.network.message.MessageUpdateStat; import toughasnails.temperature.TemperatureDebugger.Modifier; import toughasnails.temperature.modifier.AltitudeModifier; @@ -35,255 +36,308 @@ import toughasnails.temperature.modifier.TimeModifier; import toughasnails.temperature.modifier.WeatherModifier; -public class TemperatureHandler extends StatHandlerBase implements ITemperature -{ - public static final int TEMPERATURE_SCALE_MIDPOINT = TemperatureScale.getScaleTotal() / 2; - public static final int BASE_TEMPERATURE_CHANGE_TICKS = 1200; - - private int temperatureLevel; - private int prevTemperatureLevel; - private int temperatureTimer; - - private TemperatureModifier altitudeModifier; - private TemperatureModifier armorModifier; - private TemperatureModifier biomeModifier; - private TemperatureModifier playerStateModifier; - private TemperatureModifier objectProximityModifier; - private TemperatureModifier weatherModifier; - private TemperatureModifier timeModifier; - private TemperatureModifier seasonModifier; - - private Map externalModifiers; - - public final TemperatureDebugger debugger = new TemperatureDebugger(); - - public TemperatureHandler() - { - this.temperatureLevel = TemperatureScale.getScaleTotal() / 2; - this.prevTemperatureLevel = this.temperatureLevel; - - this.altitudeModifier = new AltitudeModifier(debugger); - this.armorModifier = new ArmorModifier(debugger); - this.biomeModifier = new BiomeModifier(debugger); - this.playerStateModifier = new PlayerStateModifier(debugger); - this.objectProximityModifier = new ObjectProximityModifier(debugger); - this.weatherModifier = new WeatherModifier(debugger); - this.timeModifier = new TimeModifier(debugger); - this.seasonModifier = new SeasonModifier(debugger); - - this.externalModifiers = Maps.newHashMap(); - } - - @Override - public void update(EntityPlayer player, World world, Phase phase) - { - if (phase == Phase.END && !world.isRemote) - { - int newTempChangeTicks = BASE_TEMPERATURE_CHANGE_TICKS; - - TemperatureTrend trend; - - if (debugger.targetTemperature == this.temperatureLevel) trend = TemperatureTrend.STILL; - else trend = debugger.targetTemperature > this.temperatureLevel ? TemperatureTrend.INCREASING : TemperatureTrend.DECREASING; - - newTempChangeTicks = altitudeModifier.modifyChangeRate(world, player, newTempChangeTicks, trend); - newTempChangeTicks = armorModifier.modifyChangeRate(world, player, newTempChangeTicks, trend); - newTempChangeTicks = biomeModifier.modifyChangeRate(world, player, newTempChangeTicks, trend); - newTempChangeTicks = playerStateModifier.modifyChangeRate(world, player, newTempChangeTicks, trend); - newTempChangeTicks = objectProximityModifier.modifyChangeRate(world, player, newTempChangeTicks, trend); - newTempChangeTicks = weatherModifier.modifyChangeRate(world, player, newTempChangeTicks, trend); - newTempChangeTicks = timeModifier.modifyChangeRate(world, player, newTempChangeTicks, trend); - - java.util.Iterator iterator = this.externalModifiers.values().iterator(); - - debugger.start(Modifier.CLIMATISATION_RATE, newTempChangeTicks); - while (iterator.hasNext()) - { - TemperatureModifier.ExternalModifier modifier = iterator.next(); - - if (this.temperatureTimer > modifier.getEndTime()) - { - iterator.remove(); - } - else - { - if (SyncedConfig.getBooleanValue(GameplayOption.ENABLE_TEMPERATURE)) - { - newTempChangeTicks += modifier.getRate(); - } - } - } - debugger.end(newTempChangeTicks); - - newTempChangeTicks = Math.max(20, newTempChangeTicks); - - boolean incrementTemperature = ++temperatureTimer >= newTempChangeTicks; - boolean updateClient = ++debugger.debugTimer % 5 == 0; - - debugger.temperatureTimer = temperatureTimer; - debugger.changeTicks = newTempChangeTicks; - - if (incrementTemperature && SyncedConfig.getBooleanValue(GameplayOption.ENABLE_TEMPERATURE)) - { - for (ExternalModifier modifier : this.externalModifiers.values()) - { - modifier.setEndTime(modifier.getEndTime() - this.temperatureTimer); - } - } - - if ((incrementTemperature || updateClient) && SyncedConfig.getBooleanValue(GameplayOption.ENABLE_TEMPERATURE)) - { - debugger.start(Modifier.EQUILIBRIUM_TARGET, 0); - debugger.end(TemperatureScale.getScaleTotal() / 2); - - Temperature targetTemperature = biomeModifier.modifyTarget(world, player, new Temperature(TEMPERATURE_SCALE_MIDPOINT)); - targetTemperature = altitudeModifier.modifyTarget(world, player, targetTemperature); - targetTemperature = armorModifier.modifyTarget(world, player, targetTemperature); - targetTemperature = playerStateModifier.modifyTarget(world, player, targetTemperature); - targetTemperature = objectProximityModifier.modifyTarget(world, player, targetTemperature); - targetTemperature = weatherModifier.modifyTarget(world, player, targetTemperature); - targetTemperature = timeModifier.modifyTarget(world, player, targetTemperature); - targetTemperature = seasonModifier.modifyTarget(world, player, targetTemperature); - - debugger.start(Modifier.CLIMATISATION_TARGET, targetTemperature.getRawValue()); - for (TemperatureModifier.ExternalModifier modifier : this.externalModifiers.values()) - { - targetTemperature = new Temperature(targetTemperature.getRawValue() + modifier.getAmount()); - } - debugger.end(targetTemperature.getRawValue()); - - debugger.targetTemperature = targetTemperature.getRawValue(); - - targetTemperature = new Temperature(MathHelper.clamp_int(targetTemperature.getRawValue(), 0, TemperatureScale.getScaleTotal())); - - if (incrementTemperature) - { - this.addTemperature(new Temperature((int)Math.signum(targetTemperature.getRawValue() - this.temperatureLevel))); - this.temperatureTimer = 0; - } - } - - addPotionEffects(player); - - if (updateClient) - { - //This works because update is only called if !world.isRemote - debugger.finalize((EntityPlayerMP)player); - } - } - } - - private void addPotionEffects(EntityPlayer player) - { - TemperatureRange range = TemperatureScale.getTemperatureRange(this.temperatureLevel); - float multiplier = 1.0F; - - //The point from 0 to 1 at which potion effects begin in an extremity range - float extremityDelta = (3.0F / 6.0F); - - //Start the hypo/hyperthermia slightly after the real ranges start - int hypoRangeSize = (int)(TemperatureRange.ICY.getRangeSize() * extremityDelta); - int hypoRangeStart = hypoRangeSize - 1; - int hyperRangeSize = (int)(TemperatureRange.HOT.getRangeSize() * extremityDelta); - int hyperRangeStart = (TemperatureScale.getScaleTotal() + 1) - hyperRangeSize; - - //Don't apply any negative effects whilst in creative mode - if (!player.capabilities.isCreativeMode && (SyncedConfig.getBooleanValue(GameplayOption.ENABLE_TEMPERATURE))) - { - if (this.temperatureLevel <= hypoRangeStart && (!player.isPotionActive(TANPotions.cold_resistance)) && (temperatureLevel < prevTemperatureLevel || !player.isPotionActive(TANPotions.hypothermia))) - { - multiplier = 1.0F - ((float)(this.temperatureLevel + 1) / (float)hypoRangeSize); - player.removePotionEffect(TANPotions.hypothermia); - player.addPotionEffect(new PotionEffect(TANPotions.hypothermia, (int)(1800 * multiplier) + 600, (int)(3 * multiplier + extremityDelta))); - } - else if (this.temperatureLevel >= hyperRangeStart && (!player.isPotionActive(TANPotions.heat_resistance)) && (temperatureLevel > prevTemperatureLevel || !player.isPotionActive(TANPotions.hyperthermia))) - { - multiplier = (float)(this.temperatureLevel - hyperRangeStart) / hyperRangeSize; - player.removePotionEffect(TANPotions.hyperthermia); - player.addPotionEffect(new PotionEffect(TANPotions.hyperthermia, (int)(1800 * multiplier) + 600, (int)(3 * multiplier))); - } - } - } - - @Override - public boolean hasChanged() - { - return this.prevTemperatureLevel != this.temperatureLevel; - } - - @Override - public void onSendClientUpdate() - { - this.prevTemperatureLevel = this.temperatureLevel; - } - - @Override - public IMessage createUpdateMessage() - { - NBTTagCompound data = (NBTTagCompound)TANCapabilities.TEMPERATURE.getStorage().writeNBT(TANCapabilities.TEMPERATURE, this, null); - return new MessageUpdateStat(TANCapabilities.TEMPERATURE, data); - } - - @Override - public void setChangeTime(int ticks) - { - this.temperatureTimer = ticks; - } - - @Override - public int getChangeTime() - { - return this.temperatureTimer; - } - - @Override - public void setTemperature(Temperature temperature) - { - this.temperatureLevel = temperature.getRawValue(); - } - - @Override - public void addTemperature(Temperature difference) - { - this.temperatureLevel = Math.max(Math.min(TemperatureScale.getScaleTotal(), this.temperatureLevel + difference.getRawValue()), 0); - } - - @Override - public void applyModifier(String name, int amount, int rate, int duration) - { - if (this.externalModifiers.containsKey(name)) - { - ExternalModifier modifier = this.externalModifiers.get(name); - modifier.setEndTime(this.temperatureTimer + duration); - } - else - { - TemperatureModifier.ExternalModifier modifier = new TemperatureModifier.ExternalModifier(name, amount, rate, this.temperatureTimer + duration); - this.externalModifiers.put(name, modifier); - } - } - - @Override - public boolean hasModifier(String name) - { - return this.externalModifiers.containsKey(name); - } - - @Override - public ImmutableMap getExternalModifiers() - { - return ImmutableMap.copyOf(this.externalModifiers); - } - - @Override - public void setExternalModifiers(Map externalModifiers) - { - this.externalModifiers = externalModifiers; - } - - @Override - public Temperature getTemperature() - { - return new Temperature(this.temperatureLevel); - } +public class TemperatureHandler extends StatHandlerBase + implements ITemperature { + public static final int TEMPERATURE_SCALE_MIDPOINT = TemperatureScale + .getScaleTotal() / 2; + public static final int BASE_TEMPERATURE_CHANGE_TICKS = 1200; + + private int temperatureLevel; + private int prevTemperatureLevel; + private int temperatureTimer; + + private TemperatureModifier altitudeModifier; + private TemperatureModifier armorModifier; + private TemperatureModifier biomeModifier; + private TemperatureModifier playerStateModifier; + private TemperatureModifier objectProximityModifier; + private TemperatureModifier weatherModifier; + private TemperatureModifier timeModifier; + private TemperatureModifier seasonModifier; + + private Map externalModifiers; + + public final TemperatureDebugger debugger = new TemperatureDebugger(); + + public TemperatureHandler() { + this.temperatureLevel = TemperatureScale.getScaleTotal() / 2; + this.prevTemperatureLevel = this.temperatureLevel; + + this.altitudeModifier = new AltitudeModifier(debugger); + this.armorModifier = new ArmorModifier(debugger); + this.biomeModifier = new BiomeModifier(debugger); + this.playerStateModifier = new PlayerStateModifier(debugger); + this.objectProximityModifier = new ObjectProximityModifier(debugger); + this.weatherModifier = new WeatherModifier(debugger); + this.timeModifier = new TimeModifier(debugger); + this.seasonModifier = new SeasonModifier(debugger); + + this.externalModifiers = Maps.newHashMap(); + } + + @Override + public void update(EntityPlayer player, World world, Phase phase) { + if (phase == Phase.END && !world.isRemote) { + int newTempChangeTicks = BASE_TEMPERATURE_CHANGE_TICKS; + + TemperatureTrend trend; + + if (debugger.targetTemperature == this.temperatureLevel) + trend = TemperatureTrend.STILL; + else + trend = debugger.targetTemperature > this.temperatureLevel + ? TemperatureTrend.INCREASING + : TemperatureTrend.DECREASING; + + newTempChangeTicks = altitudeModifier.modifyChangeRate(world, + player, newTempChangeTicks, trend); + newTempChangeTicks = armorModifier.modifyChangeRate(world, player, + newTempChangeTicks, trend); + newTempChangeTicks = biomeModifier.modifyChangeRate(world, player, + newTempChangeTicks, trend); + newTempChangeTicks = playerStateModifier.modifyChangeRate(world, + player, newTempChangeTicks, trend); + newTempChangeTicks = objectProximityModifier.modifyChangeRate(world, + player, newTempChangeTicks, trend); + newTempChangeTicks = weatherModifier.modifyChangeRate(world, player, + newTempChangeTicks, trend); + newTempChangeTicks = timeModifier.modifyChangeRate(world, player, + newTempChangeTicks, trend); + + java.util.Iterator iterator = this.externalModifiers + .values().iterator(); + + debugger.start(Modifier.CLIMATISATION_RATE, newTempChangeTicks); + while (iterator.hasNext()) { + TemperatureModifier.ExternalModifier modifier = iterator.next(); + + if (this.temperatureTimer > modifier.getEndTime()) { + iterator.remove(); + } else { + if (SyncedConfig.getBooleanValue( + GameplayOption.ENABLE_TEMPERATURE)) { + newTempChangeTicks += modifier.getRate(); + } + } + } + debugger.end(newTempChangeTicks); + + newTempChangeTicks = Math.max(20, newTempChangeTicks); + + boolean incrementTemperature = ++temperatureTimer >= newTempChangeTicks; + boolean updateClient = ++debugger.debugTimer % 5 == 0; + + debugger.temperatureTimer = temperatureTimer; + debugger.changeTicks = newTempChangeTicks; + + if (incrementTemperature && SyncedConfig + .getBooleanValue(GameplayOption.ENABLE_TEMPERATURE)) { + for (ExternalModifier modifier : this.externalModifiers + .values()) { + modifier.setEndTime( + modifier.getEndTime() - this.temperatureTimer); + } + } + + if ((incrementTemperature || updateClient) && SyncedConfig + .getBooleanValue(GameplayOption.ENABLE_TEMPERATURE)) { + debugger.start(Modifier.EQUILIBRIUM_TARGET, 0); + debugger.end(TemperatureScale.getScaleTotal() / 2); + + Temperature targetTemperature = biomeModifier.modifyTarget( + world, player, + new Temperature(TEMPERATURE_SCALE_MIDPOINT)); + targetTemperature = altitudeModifier.modifyTarget(world, player, + targetTemperature); + targetTemperature = armorModifier.modifyTarget(world, player, + targetTemperature); + targetTemperature = playerStateModifier.modifyTarget(world, + player, targetTemperature); + targetTemperature = objectProximityModifier.modifyTarget(world, + player, targetTemperature); + targetTemperature = weatherModifier.modifyTarget(world, player, + targetTemperature); + targetTemperature = timeModifier.modifyTarget(world, player, + targetTemperature); + targetTemperature = seasonModifier.modifyTarget(world, player, + targetTemperature); + + debugger.start(Modifier.CLIMATISATION_TARGET, + targetTemperature.getRawValue()); + for (TemperatureModifier.ExternalModifier modifier : this.externalModifiers + .values()) { + targetTemperature = new Temperature( + targetTemperature.getRawValue() + + modifier.getAmount()); + } + debugger.end(targetTemperature.getRawValue()); + + debugger.targetTemperature = targetTemperature.getRawValue(); + + targetTemperature = new Temperature( + MathHelper.clamp_int(targetTemperature.getRawValue(), 0, + TemperatureScale.getScaleTotal())); + + if (incrementTemperature) { + this.addTemperature(new Temperature( + (int) Math.signum(targetTemperature.getRawValue() + - this.temperatureLevel))); + this.temperatureTimer = 0; + } + } + + addPotionEffects(player); + + if (updateClient) { + // This works because update is only called if !world.isRemote + debugger.finalize((EntityPlayerMP) player); + } + } + } + + private void addPotionEffects(EntityPlayer player) { + // TemperatureRange range = TemperatureScale + // .getTemperatureRange(this.temperatureLevel); + float multiplier = 1.0F; + + // The point from 0 to 1 at which potion effects begin in an extremity + // range + float extremityDelta = (3.0F / 6.0F); + + // Start the hypo/hyperthermia slightly after the real ranges start + int hypoRangeSize = (int) (TemperatureRange.ICY.getRangeSize() + * extremityDelta); + int hypoRangeStart = hypoRangeSize - 1; + int hyperRangeSize = (int) (TemperatureRange.HOT.getRangeSize() + * extremityDelta); + int hyperRangeStart = (TemperatureScale.getScaleTotal() + 1) + - hyperRangeSize; + + // Don't apply any negative effects whilst in creative mode + if (!player.capabilities.isCreativeMode && (SyncedConfig + .getBooleanValue(GameplayOption.ENABLE_TEMPERATURE))) { + if (this.temperatureLevel <= hypoRangeStart + && (!player.isPotionActive(TANPotions.cold_resistance)) + && (temperatureLevel < prevTemperatureLevel || !player + .isPotionActive(TANPotions.hypothermia))) { + multiplier = 1.0F - ((float) (this.temperatureLevel + 1) + / (float) hypoRangeSize); + player.removePotionEffect(TANPotions.hypothermia); + player.addPotionEffect(new PotionEffect(TANPotions.hypothermia, + (int) (1800 * multiplier) + 600, + (int) (3 * multiplier + extremityDelta))); + } else if (this.temperatureLevel >= hyperRangeStart + && (!player.isPotionActive(TANPotions.heat_resistance)) + && (temperatureLevel > prevTemperatureLevel || !player + .isPotionActive(TANPotions.hyperthermia))) { + multiplier = (float) (this.temperatureLevel - hyperRangeStart) + / hyperRangeSize; + player.removePotionEffect(TANPotions.hyperthermia); + player.addPotionEffect(new PotionEffect(TANPotions.hyperthermia, + (int) (1800 * multiplier) + 600, + (int) (3 * multiplier))); + } + } + } + + @Override + public boolean hasChanged() { + return this.prevTemperatureLevel != this.temperatureLevel; + } + + @Override + public void onSendClientUpdate() { + this.prevTemperatureLevel = this.temperatureLevel; + } + + @Override + public IMessage createUpdateMessage() { + NBTTagCompound data = (NBTTagCompound) TANCapabilities.TEMPERATURE + .getStorage().writeNBT(TANCapabilities.TEMPERATURE, this, null); + return new MessageUpdateStat(TANCapabilities.TEMPERATURE, data); + } + + @Override + public void setChangeTime(int ticks) { + this.temperatureTimer = ticks; + } + + @Override + public int getChangeTime() { + return this.temperatureTimer; + } + + @Override + public void setTemperature(Temperature temperature) { + this.temperatureLevel = temperature.getRawValue(); + } + + @Override + public void addTemperature(Temperature difference) { + this.temperatureLevel = Math + .max(Math.min(TemperatureScale.getScaleTotal(), + this.temperatureLevel + difference.getRawValue()), 0); + } + + @Override + public void applyModifier(String name, int amount, int rate, int duration) { + if (this.externalModifiers.containsKey(name)) { + ExternalModifier modifier = this.externalModifiers.get(name); + modifier.setEndTime(this.temperatureTimer + duration); + } else { + TemperatureModifier.ExternalModifier modifier = new TemperatureModifier.ExternalModifier( + name, amount, rate, this.temperatureTimer + duration); + this.externalModifiers.put(name, modifier); + } + } + + @Override + public boolean hasModifier(String name) { + return this.externalModifiers.containsKey(name); + } + + @Override + public ImmutableMap getExternalModifiers() { + return ImmutableMap.copyOf(this.externalModifiers); + } + + @Override + public void setExternalModifiers( + Map externalModifiers) { + this.externalModifiers = externalModifiers; + } + + @Override + public Temperature getTemperature() { + return new Temperature(this.temperatureLevel); + } + + public static int getTargetTemperatureAt(World world, BlockPos position) { + final TemperatureDebugger debugger = new TemperatureDebugger(); + AltitudeModifier altitudeModifier = new AltitudeModifier(debugger); + BiomeModifier biomeModifier = new BiomeModifier(debugger); + ObjectProximityModifier objectProximityModifier = new ObjectProximityModifier( + debugger); + WeatherModifier weatherModifier = new WeatherModifier(debugger); + TimeModifier timeModifier = new TimeModifier(debugger); + SeasonModifier seasonModifier = new SeasonModifier(debugger); + + Temperature baseTemperature = new Temperature( + TemperatureHandler.TEMPERATURE_SCALE_MIDPOINT); + Temperature targetTemperature = biomeModifier.modifyTarget(world, + position, baseTemperature); + targetTemperature = altitudeModifier.modifyTarget(world, position, + targetTemperature); + targetTemperature = objectProximityModifier.modifyTarget(world, + position, targetTemperature); + targetTemperature = weatherModifier.modifyTarget(world, position, + targetTemperature); + targetTemperature = timeModifier.modifyTarget(world, position, + targetTemperature); + targetTemperature = seasonModifier.modifyTarget(world, position, + targetTemperature); + + int finalTemperature = targetTemperature.getRawValue(); + return finalTemperature; + } } diff --git a/src/main/java/toughasnails/temperature/modifier/AltitudeModifier.java b/src/main/java/toughasnails/temperature/modifier/AltitudeModifier.java index ca29b351..6cfd9ea5 100644 --- a/src/main/java/toughasnails/temperature/modifier/AltitudeModifier.java +++ b/src/main/java/toughasnails/temperature/modifier/AltitudeModifier.java @@ -4,16 +4,20 @@ import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.MathHelper; import net.minecraft.world.World; +import toughasnails.api.config.GameplayOption; +import toughasnails.api.config.SyncedConfig; import toughasnails.api.temperature.Temperature; import toughasnails.temperature.TemperatureDebugger; import toughasnails.temperature.TemperatureDebugger.Modifier; import toughasnails.temperature.TemperatureTrend; public class AltitudeModifier extends TemperatureModifier { - public static final int ALTITUDE_TARGET_MODIFIER = 3; + public final int ALTITUDE_TARGET_MODIFIER; public AltitudeModifier(TemperatureDebugger debugger) { super(debugger); + this.ALTITUDE_TARGET_MODIFIER = SyncedConfig + .getIntegerValue(GameplayOption.ALTITUDE_TEMP_MODIFIER); } @Override diff --git a/src/main/java/toughasnails/temperature/modifier/BiomeModifier.java b/src/main/java/toughasnails/temperature/modifier/BiomeModifier.java index 5bdde04c..ff152280 100644 --- a/src/main/java/toughasnails/temperature/modifier/BiomeModifier.java +++ b/src/main/java/toughasnails/temperature/modifier/BiomeModifier.java @@ -4,6 +4,8 @@ import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; import net.minecraft.world.biome.Biome; +import toughasnails.api.config.GameplayOption; +import toughasnails.api.config.SyncedConfig; import toughasnails.api.temperature.Temperature; import toughasnails.temperature.TemperatureDebugger; import toughasnails.temperature.TemperatureDebugger.Modifier; @@ -11,10 +13,12 @@ import toughasnails.util.BiomeUtils; public class BiomeModifier extends TemperatureModifier { - public static final int MAX_TEMP_OFFSET = 10; + public final int MAX_TEMP_OFFSET; public BiomeModifier(TemperatureDebugger debugger) { super(debugger); + this.MAX_TEMP_OFFSET = SyncedConfig + .getIntegerValue(GameplayOption.BIOME_TEMP_MODIFIER); } @Override diff --git a/src/main/java/toughasnails/temperature/modifier/ObjectProximityModifier.java b/src/main/java/toughasnails/temperature/modifier/ObjectProximityModifier.java index 68b3da96..d8de978d 100644 --- a/src/main/java/toughasnails/temperature/modifier/ObjectProximityModifier.java +++ b/src/main/java/toughasnails/temperature/modifier/ObjectProximityModifier.java @@ -81,6 +81,10 @@ public Temperature modifyTarget(World world, EntityPlayer player, public static float getBlockTemperature(IBlockState state) { Material material = state.getMaterial(); + // NPE guard + if (state.getBlock().getRegistryName() == null) { + return 0.0F; + } String blockName = state.getBlock().getRegistryName().toString(); // Blocks diff --git a/src/main/java/toughasnails/temperature/modifier/SeasonModifier.java b/src/main/java/toughasnails/temperature/modifier/SeasonModifier.java index f1423b04..5d998ea8 100644 --- a/src/main/java/toughasnails/temperature/modifier/SeasonModifier.java +++ b/src/main/java/toughasnails/temperature/modifier/SeasonModifier.java @@ -20,8 +20,49 @@ import toughasnails.temperature.TemperatureTrend; public class SeasonModifier extends TemperatureModifier { + + public final int EARLY_AUTUMN_MODIFIER; + public final int MID_AUTUMN_MODIFIER; + public final int LATE_AUTUMN_MODIFIER; + public final int EARLY_WINTER_MODIFIER; + public final int MID_WINTER_MODIFIER; + public final int LATE_WINTER_MODIFIER; + public final int EARLY_SPRING_MODIFIER; + public final int MID_SPRING_MODIFIER; + public final int LATE_SPRING_MODIFIER; + public final int EARLY_SUMMER_MODIFIER; + public final int MID_SUMMER_MODIFIER; + public final int LATE_SUMMER_MODIFIER; + public SeasonModifier(TemperatureDebugger debugger) { super(debugger); + this.EARLY_AUTUMN_MODIFIER = SyncedConfig + .getIntegerValue(GameplayOption.EARLY_AUTUMN_MODIFIER); + this.MID_AUTUMN_MODIFIER = SyncedConfig + .getIntegerValue(GameplayOption.MID_AUTUMN_MODIFIER); + this.LATE_AUTUMN_MODIFIER = SyncedConfig + .getIntegerValue(GameplayOption.LATE_AUTUMN_MODIFIER); + + this.EARLY_WINTER_MODIFIER = SyncedConfig + .getIntegerValue(GameplayOption.EARLY_WINTER_MODIFIER); + this.MID_WINTER_MODIFIER = SyncedConfig + .getIntegerValue(GameplayOption.MID_WINTER_MODIFIER); + this.LATE_WINTER_MODIFIER = SyncedConfig + .getIntegerValue(GameplayOption.LATE_WINTER_MODIFIER); + + this.EARLY_SPRING_MODIFIER = SyncedConfig + .getIntegerValue(GameplayOption.EARLY_SPRING_MODIFIER); + this.MID_SPRING_MODIFIER = SyncedConfig + .getIntegerValue(GameplayOption.MID_SPRING_MODIFIER); + this.LATE_SPRING_MODIFIER = SyncedConfig + .getIntegerValue(GameplayOption.LATE_SPRING_MODIFIER); + + this.EARLY_SUMMER_MODIFIER = SyncedConfig + .getIntegerValue(GameplayOption.EARLY_SUMMER_MODIFIER); + this.MID_SUMMER_MODIFIER = SyncedConfig + .getIntegerValue(GameplayOption.MID_SUMMER_MODIFIER); + this.LATE_SUMMER_MODIFIER = SyncedConfig + .getIntegerValue(GameplayOption.LATE_SUMMER_MODIFIER); } @Override @@ -44,30 +85,42 @@ public Temperature modifyTarget(World world, EntityPlayer player, if (world.provider.isSurfaceWorld()) { switch (season) { + case EARLY_AUTUMN: + temperatureLevel += this.EARLY_AUTUMN_MODIFIER; + break; + case MID_AUTUMN: + temperatureLevel += this.MID_AUTUMN_MODIFIER; + break; + case LATE_AUTUMN: + temperatureLevel += this.LATE_AUTUMN_MODIFIER; + break; + case EARLY_WINTER: + temperatureLevel += this.EARLY_WINTER_MODIFIER; + break; case MID_WINTER: + temperatureLevel += this.MID_WINTER_MODIFIER; + break; case LATE_WINTER: - temperatureLevel -= 6; + temperatureLevel += this.LATE_WINTER_MODIFIER; break; - case EARLY_SPRING: - case EARLY_WINTER: - temperatureLevel -= 4; + temperatureLevel += this.EARLY_SPRING_MODIFIER; break; - case MID_SPRING: - case LATE_AUTUMN: - temperatureLevel -= 2; + temperatureLevel += this.MID_SPRING_MODIFIER; + break; + case LATE_SPRING: + temperatureLevel += this.LATE_SPRING_MODIFIER; + break; + case EARLY_SUMMER: + temperatureLevel += this.EARLY_SUMMER_MODIFIER; break; - case MID_SUMMER: - case EARLY_AUTUMN: - temperatureLevel += 2; + temperatureLevel += this.MID_SUMMER_MODIFIER; break; - case LATE_SUMMER: - temperatureLevel += 4; + temperatureLevel += this.LATE_SUMMER_MODIFIER; break; - default: break; } @@ -88,30 +141,42 @@ public Temperature modifyTarget(World world, BlockPos position, if (world.provider.isSurfaceWorld()) { switch (season) { + case EARLY_AUTUMN: + temperatureLevel += this.EARLY_AUTUMN_MODIFIER; + break; + case MID_AUTUMN: + temperatureLevel += this.MID_AUTUMN_MODIFIER; + break; + case LATE_AUTUMN: + temperatureLevel += this.LATE_AUTUMN_MODIFIER; + break; + case EARLY_WINTER: + temperatureLevel += this.EARLY_WINTER_MODIFIER; + break; case MID_WINTER: + temperatureLevel += this.MID_WINTER_MODIFIER; + break; case LATE_WINTER: - temperatureLevel -= 6; + temperatureLevel += this.LATE_WINTER_MODIFIER; break; - case EARLY_SPRING: - case EARLY_WINTER: - temperatureLevel -= 4; + temperatureLevel += this.EARLY_SPRING_MODIFIER; break; - case MID_SPRING: - case LATE_AUTUMN: - temperatureLevel -= 2; + temperatureLevel += this.MID_SPRING_MODIFIER; + break; + case LATE_SPRING: + temperatureLevel += this.LATE_SPRING_MODIFIER; + break; + case EARLY_SUMMER: + temperatureLevel += this.EARLY_SUMMER_MODIFIER; break; - case MID_SUMMER: - case EARLY_AUTUMN: - temperatureLevel += 2; + temperatureLevel += this.MID_SUMMER_MODIFIER; break; - case LATE_SUMMER: - temperatureLevel += 4; + temperatureLevel += this.LATE_SUMMER_MODIFIER; break; - default: break; } diff --git a/src/main/java/toughasnails/temperature/modifier/TimeModifier.java b/src/main/java/toughasnails/temperature/modifier/TimeModifier.java index 48fbddb5..97e9d0b5 100644 --- a/src/main/java/toughasnails/temperature/modifier/TimeModifier.java +++ b/src/main/java/toughasnails/temperature/modifier/TimeModifier.java @@ -4,6 +4,8 @@ import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; import net.minecraft.world.biome.Biome; +import toughasnails.api.config.GameplayOption; +import toughasnails.api.config.SyncedConfig; import toughasnails.api.temperature.Temperature; import toughasnails.temperature.TemperatureDebugger; import toughasnails.temperature.TemperatureDebugger.Modifier; @@ -11,16 +13,20 @@ import toughasnails.util.BiomeUtils; public class TimeModifier extends TemperatureModifier { - public static final int TIME_TARGET_MODIFIER = 7; + public final int TIME_TARGET_MODIFIER; /** * Multiplies how much should the temperature be increased/decreased by the * closer the biome temp is to a extreme hot or cold */ - public static final float EXTREMITY_MULTIPLIER = 1.25F; + public final float EXTREMITY_MULTIPLIER; public TimeModifier(TemperatureDebugger debugger) { super(debugger); + this.TIME_TARGET_MODIFIER = SyncedConfig + .getIntegerValue(GameplayOption.TIME_TEMP_MODIFIER); + this.EXTREMITY_MULTIPLIER = SyncedConfig + .getFloatValue(GameplayOption.TIME_EXTREMITY_MODIFIER); } @Override diff --git a/src/main/java/toughasnails/temperature/modifier/WeatherModifier.java b/src/main/java/toughasnails/temperature/modifier/WeatherModifier.java index ee56f45b..33f9caa7 100644 --- a/src/main/java/toughasnails/temperature/modifier/WeatherModifier.java +++ b/src/main/java/toughasnails/temperature/modifier/WeatherModifier.java @@ -4,6 +4,8 @@ import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; import net.minecraft.world.biome.Biome; +import toughasnails.api.config.GameplayOption; +import toughasnails.api.config.SyncedConfig; import toughasnails.api.temperature.Temperature; import toughasnails.temperature.TemperatureDebugger; import toughasnails.temperature.TemperatureDebugger.Modifier; @@ -11,11 +13,15 @@ public class WeatherModifier extends TemperatureModifier { public static final int WET_RATE_MODIFIER = -750; - public static final int WET_TARGET_MODIFIER = -7; - public static final int SNOW_TARGET_MODIFIER = -10; + public final int WET_TARGET_MODIFIER; + public final int SNOW_TARGET_MODIFIER; public WeatherModifier(TemperatureDebugger debugger) { super(debugger); + this.WET_TARGET_MODIFIER = SyncedConfig + .getIntegerValue(GameplayOption.WET_TEMP_MODIFIER); + this.SNOW_TARGET_MODIFIER = SyncedConfig + .getIntegerValue(GameplayOption.SNOW_TEMP_MODIFIER); } @Override @@ -60,12 +66,14 @@ public Temperature modifyTarget(World world, BlockPos position, Temperature temperature) { int temperatureLevel = temperature.getRawValue(); int newTemperatureLevel = temperatureLevel; + boolean rainChill = SyncedConfig + .getBooleanValue(GameplayOption.RAIN_CHILL); if (world.isRaining() && world.canSeeSky(position)) { Biome biome = world.getBiomeGenForCoords(position); if (biome.getEnableSnow()) { newTemperatureLevel += SNOW_TARGET_MODIFIER; - } else if (biome.canRain()) { + } else if (biome.canRain() && rainChill) { newTemperatureLevel += WET_TARGET_MODIFIER; } } diff --git a/src/main/resources/assets/toughasnails/lang/en_US.lang b/src/main/resources/assets/toughasnails/lang/en_US.lang index 58680151..04ec3456 100644 --- a/src/main/resources/assets/toughasnails/lang/en_US.lang +++ b/src/main/resources/assets/toughasnails/lang/en_US.lang @@ -80,6 +80,7 @@ item.season_clock.name=Season Clock item.spawn_egg_freeze.name=Spawn Freeze item.tan_icon.name=TAN Icon item.thermometer.name=Thermometer +item.thermometer.read=The temperature here is %d degrees. item.wool_helmet.name=Wool Hood item.wool_chestplate.name=Wool Jacket item.wool_leggings.name=Wool Pants