From b32f4c2da27cb0d154302d5b843b6bc81a46ad4c Mon Sep 17 00:00:00 2001 From: Tim Clancy Date: Fri, 14 Jul 2017 01:24:16 -0400 Subject: [PATCH] Large fix commit. Updated thermometer to show temperature based on world location. Thermometer now works in item frame. Added configuration for temperature modifiers of various types. Added configurable thermometer bounds. Added toggle for whether or not thermometer is affected by rain chill. Added asanetargoss's 1.11 PR for withering plant stems. Added configuration options for hibernating crops (such as trees) and configuration for crops which wither without implementing the API. Added command for retrieving temperature at world location. --- build.properties | 4 +- .../api/config/GameplayOption.java | 35 +- .../toughasnails/api/config/SyncedConfig.java | 8 + .../api/season/IHibernatingCrop.java | 10 +- src/main/java/toughasnails/asm/ASMHelper.java | 189 ++--- src/main/java/toughasnails/asm/ObfHelper.java | 57 +- .../toughasnails/asm/TANLoadingPlugin.java | 56 +- .../transformer/AbstractCropTransformer.java | 98 --- .../transformer/BlockCropsTransformer.java | 24 - .../asm/transformer/BlockStemTransformer.java | 18 - .../asm/transformer/CropDecayTransformer.java | 145 ++++ .../transformer/InterfaceCheckVisitor.java | 26 + .../transformer/MysticalCropTransformer.java | 43 - .../asm/transformer/PamCropTransformer.java | 44 - .../asm/transformer/WorldTransformer.java | 322 +++++--- .../java/toughasnails/command/TANCommand.java | 34 +- .../config/CropGrowConfigEntry.java | 38 + .../config/GameplayConfigurationHandler.java | 228 +++++- .../java/toughasnails/config/TANConfig.java | 753 +++++++++--------- .../handler/BlockHarvestEventHandler.java | 28 +- .../handler/season/SeasonHandler.java | 345 +++++--- .../handler/thirst/VanillaDrinkHandler.java | 5 +- .../toughasnails/item/ItemThermometer.java | 129 +-- .../toughasnails/season/SeasonASMHelper.java | 288 ++++--- .../temperature/TemperatureHandler.java | 560 +++++++------ .../modifier/AltitudeModifier.java | 6 +- .../temperature/modifier/BiomeModifier.java | 6 +- .../modifier/ObjectProximityModifier.java | 4 + .../temperature/modifier/SeasonModifier.java | 117 ++- .../temperature/modifier/TimeModifier.java | 10 +- .../temperature/modifier/WeatherModifier.java | 14 +- .../assets/toughasnails/lang/en_US.lang | 1 + 32 files changed, 2143 insertions(+), 1502 deletions(-) delete mode 100644 src/main/java/toughasnails/asm/transformer/AbstractCropTransformer.java delete mode 100644 src/main/java/toughasnails/asm/transformer/BlockCropsTransformer.java delete mode 100644 src/main/java/toughasnails/asm/transformer/BlockStemTransformer.java create mode 100644 src/main/java/toughasnails/asm/transformer/CropDecayTransformer.java create mode 100644 src/main/java/toughasnails/asm/transformer/InterfaceCheckVisitor.java delete mode 100644 src/main/java/toughasnails/asm/transformer/MysticalCropTransformer.java delete mode 100644 src/main/java/toughasnails/asm/transformer/PamCropTransformer.java create mode 100644 src/main/java/toughasnails/config/CropGrowConfigEntry.java 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