diff --git a/TEMPLATE_LICENSE.txt b/LICENSE.txt similarity index 100% rename from TEMPLATE_LICENSE.txt rename to LICENSE.txt diff --git a/README.md b/README.md index e5e39f1..e0dadb5 100644 --- a/README.md +++ b/README.md @@ -1,28 +1,12 @@ +# METABOLISM -Installation information -======= +[**Metabolism**](https://en.wikipedia.org/wiki/Metabolism) (/məˈtæbəlɪzəm/, from Greek: μεταβολή *metabolē*, "change") +is the set of life-sustaining chemical reactions in organisms. -This template repository can be directly cloned to get you started with a new -mod. Simply create a new repository cloned from this one, by following the -instructions at [github](https://docs.github.com/en/repositories/creating-and-managing-repositories/creating-a-repository-from-a-template). + -Once you have your clone, simply open the repository in the IDE of your choice. The usual recommendation for an IDE is either IntelliJ IDEA or Eclipse. +A mod that adds a new temperature system, plus an overhaul of hunger & food! -> **Note**: For IDEs other than Intellij IDEA, you must run the `ideBeforeRun` task first from the terminal (such as `./gradlew ideBeforeRun`) for the run configs to work. -If at any point you are missing libraries in your IDE, or you've run into problems you can -run `gradlew --refresh-dependencies` to refresh the local cache. `gradlew clean` to reset everything -{this does not affect your code} and then start the process again. -Mapping Names: -============ -By default, the MDK is configured to use the official mapping names from Mojang for methods and fields -in the Minecraft codebase. These names are covered by a specific license. All modders should be aware of this -license, if you do not agree with it you can change your mapping names to other crowdsourced names in your -build.gradle. For the latest license text, refer to the mapping file itself, or the reference copy here: -https://github.com/NeoForged/NeoForm/blob/main/Mojang.md -Additional Resources: -========== -Community Documentation: https://docs.neoforged.net/ -NeoForged Discord: https://discord.neoforged.net/ diff --git a/build.gradle b/build.gradle index ffc7a0f..50548e4 100644 --- a/build.gradle +++ b/build.gradle @@ -1,120 +1,102 @@ plugins { - id 'java-library' id 'eclipse' id 'idea' id 'maven-publish' - id 'net.neoforged.gradle.userdev' version '7.0.57' + id 'net.minecraftforge.gradle' version '[6.0,6.2)' + id 'org.parchmentmc.librarian.forgegradle' version '1.+' + id("org.spongepowered.mixin") version "0.7-SNAPSHOT" } version = mod_version group = mod_group_id -repositories { - mavenLocal() -} - base { archivesName = mod_id } -// Mojang ships Java 17 to end users in 1.18+, so your mod should target Java 17. java.toolchain.languageVersion = JavaLanguageVersion.of(17) -//minecraft.accessTransformers.file rootProject.file('src/main/resources/META-INF/accesstransformer.cfg') -//minecraft.accessTransformers.entry public net.minecraft.client.Minecraft textureManager # textureManager - -// Default run configurations. -// These can be tweaked, removed, or duplicated as needed. -runs { - // applies to all the run configs below - configureEach { - // Recommended logging data for a userdev environment - // The markers can be added/remove as needed separated by commas. - // "SCAN": For mods scan. - // "REGISTRIES": For firing of registry events. - // "REGISTRYDUMP": For getting the contents of all registries. - systemProperty 'forge.logging.markers', 'REGISTRIES' - - // Recommended logging level for the console - // You can set various levels here. - // Please read: https://stackoverflow.com/questions/2031163/when-to-use-the-different-log-levels - systemProperty 'forge.logging.console.level', 'debug' - - modSource project.sourceSets.main - } +println "Java: ${System.getProperty 'java.version'}, JVM: ${System.getProperty 'java.vm.version'} (${System.getProperty 'java.vendor'}), Arch: ${System.getProperty 'os.arch'}" +minecraft { - client { - // Comma-separated list of namespaces to load gametests from. Empty = all namespaces. - systemProperty 'forge.enabledGameTestNamespaces', project.mod_id - } + mappings channel: mapping_channel, version: mapping_version - server { - systemProperty 'forge.enabledGameTestNamespaces', project.mod_id - programArgument '--nogui' - } + copyIdeResources = true - // This run config launches GameTestServer and runs all registered gametests, then exits. - // By default, the server will crash when no gametests are provided. - // The gametest system is also enabled by default for other run configs under the /test command. - gameTestServer { - systemProperty 'forge.enabledGameTestNamespaces', project.mod_id - } - data { - // example of overriding the workingDirectory set in configureEach above, uncomment if you want to use it - // workingDirectory project.file('run-data') + runs { + configureEach { + workingDirectory project.file('run') + property 'forge.logging.markers', 'REGISTRIES' + property 'forge.logging.console.level', 'debug' + properties 'mixin.env.disableRefMap': 'true' + arg "-mixin.config="+"${mod_id}"+".mixins.json" + + mods { + "${mod_id}" { + source sourceSets.main + } + } + } + + client { + property 'forge.enabledGameTestNamespaces', mod_id + } + + server { + property 'forge.enabledGameTestNamespaces', mod_id + args '--nogui' + } + + gameTestServer { + property 'forge.enabledGameTestNamespaces', mod_id + } - // Specify the modid for data generation, where to output the resulting resource, and where to look for existing resources. - programArguments.addAll '--mod', project.mod_id, '--all', '--output', file('src/generated/resources/').getAbsolutePath(), '--existing', file('src/main/resources/').getAbsolutePath() + data { + workingDirectory project.file('run-data') + args '--mod', mod_id, '--all', '--output', file('src/generated/resources/'), '--existing', file('src/main/resources/') + } } } -// Include resources generated by data generators. sourceSets.main.resources { srcDir 'src/generated/resources' } +repositories { + // Put repositories for dependencies here + // ForgeGradle automatically adds the Forge maven and Maven Central for you + + // If you have mod jar dependencies in ./libs, you can declare them as a repository like so. + // See https://docs.gradle.org/current/userguide/declaring_repositories.html#sub:flat_dir_resolver + // flatDir { + // dir 'libs' + // } + maven { + name = 'Sponge / Mixin' + url = 'https://repo.spongepowered.org/repository/maven-public/' + } +} dependencies { - // Specify the version of Minecraft to use. - // Depending on the plugin applied there are several options. We will assume you applied the userdev plugin as shown above. - // The group for userdev is net.neoforged, the module name is neoforge, and the version is the same as the neoforge version. - // You can however also use the vanilla plugin (net.neoforged.gradle.vanilla) to use a version of Minecraft without the neoforge loader. - // And its provides the option to then use net.minecraft as the group, and one of; client, server or joined as the module name, plus the game version as version. - // For all intends and purposes: You can treat this dependency as if it is a normal library you would use. - implementation "net.neoforged:neoforge:${neo_version}" - - // Example mod dependency with JEI - // The JEI API is declared for compile time use, while the full JEI artifact is used at runtime - // compileOnly "mezz.jei:jei-${mc_version}-common-api:${jei_version}" - // compileOnly "mezz.jei:jei-${mc_version}-forge-api:${jei_version}" - // runtimeOnly "mezz.jei:jei-${mc_version}-forge:${jei_version}" - - // Example mod dependency using a mod jar from ./libs with a flat dir repository - // This maps to ./libs/coolmod-${mc_version}-${coolmod_version}.jar - // The group id is ignored when searching -- in this case, it is "blank" - // implementation "blank:coolmod-${mc_version}:${coolmod_version}" - - // Example mod dependency using a file as dependency - // implementation files("libs/coolmod-${mc_version}-${coolmod_version}.jar") - - // Example project dependency using a sister or child project: - // implementation project(":myproject") - - // For more info: - // http://www.gradle.org/docs/current/userguide/artifact_dependencies_tutorial.html - // http://www.gradle.org/docs/current/userguide/dependency_management.html + minecraft "net.minecraftforge:forge:${minecraft_version}-${forge_version}" + annotationProcessor 'org.spongepowered:mixin:0.8.5:processor' + } -// This block of code expands all declared replace properties in the specified resource targets. -// A missing property will result in an error. Properties are expanded using ${} Groovy notation. -// When "copyIdeResources" is enabled, this will also run before the game launches in IDE environments. -// See https://docs.gradle.org/current/dsl/org.gradle.language.jvm.tasks.ProcessResources.html -tasks.withType(ProcessResources).configureEach { +mixin{ + add sourceSets.main, "${mod_id}.refmap.json" + config 'metabolism.mixins.json' + + debug.verbose = true + debug.export = true +} + +tasks.named('processResources', ProcessResources).configure { var replaceProperties = [ - minecraft_version : minecraft_version, minecraft_version_range: minecraft_version_range, - neo_version : neo_version, neo_version_range: neo_version_range, + minecraft_version: minecraft_version, minecraft_version_range: minecraft_version_range, + forge_version: forge_version, forge_version_range: forge_version_range, loader_version_range: loader_version_range, - mod_id : mod_id, mod_name: mod_name, mod_license: mod_license, mod_version: mod_version, - mod_authors : mod_authors, mod_description: mod_description, pack_format_number: pack_format_number, + mod_id: mod_id, mod_name: mod_name, mod_license: mod_license, mod_version: mod_version, + mod_authors: mod_authors, mod_description: mod_description, ] inputs.properties replaceProperties @@ -123,16 +105,37 @@ tasks.withType(ProcessResources).configureEach { } } +tasks.named('jar', Jar).configure { + manifest { + attributes([ + 'Specification-Title' : mod_id, + 'Specification-Vendor' : mod_authors, + 'Specification-Version' : '1', // We are version 1 of ourselves + 'Implementation-Title' : project.name, + 'Implementation-Version' : project.jar.archiveVersion, + 'Implementation-Vendor' : mod_authors, + 'Implementation-Timestamp': new Date().format("yyyy-MM-dd'T'HH:mm:ssZ") + ]) + } + + finalizedBy 'reobfJar' +} + +// However if you are in a multi-project build, dev time needs unobfed jar files, so you can delay the obfuscation until publishing by doing: +// tasks.named('publish').configure { +// dependsOn 'reobfJar' +// } + // Example configuration to allow publishing using the maven-publish plugin publishing { publications { register('mavenJava', MavenPublication) { - from components.java + artifact jar } } repositories { maven { - url "file://${project.projectDir}/repo" + url "file://${project.projectDir}/mcmodsrepo" } } } diff --git a/gradle.properties b/gradle.properties index 01edc03..8cf677e 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,41 +1,21 @@ -# Sets default memory used for gradle commands. Can be overridden by user or command line properties. -#org.gradle.jvmargs= +org.gradle.jvmargs=-Xmx3G org.gradle.daemon=false -org.gradle.debug=false -## Environment Properties -# The Minecraft version must agree with the Neo version to get a valid artifact -minecraft_version=1.20.2 -# The Minecraft version range can use any release version of Minecraft as bounds. -# Snapshots, pre-releases, and release candidates are not guaranteed to sort properly -# as they do not follow standard versioning conventions. -minecraft_version_range=[1.20.2,1.21) -# The Neo version must agree with the Minecraft version to get a valid artifact -neo_version=20.2.86 -# The Neo version range can use any version of Neo as bounds or match the loader version range -neo_version_range=[20.2,) -# The loader version range can only use the major version of Neo/FML as bounds -loader_version_range=[1,) +minecraft_version=1.20.1 +minecraft_version_range=[1.20.1,1.21) +forge_version=47.2.0 +forge_version_range=[47,) +loader_version_range=[47,) -## Mod Properties +mapping_channel=parchment +mapping_version=2023.09.03-1.20.1 -# The unique mod identifier for the mod. Must be lowercase in English locale. Must fit the regex [a-z][a-z0-9_]{1,63} -# Must match the String constant located in the main mod class annotated with @Mod. -mod_id=examplemod -# The human-readable display name for the mod. -mod_name=Example Mod -# The license of the mod. Review your options at https://choosealicense.com/. All Rights Reserved is the default. -mod_license=All Rights Reserved -# The mod version. See https://semver.org/ -mod_version=1.0.0 -# The group ID for the mod. It is only important when publishing as an artifact to a Maven repository. -# This should match the base package used for the mod sources. -# See https://maven.apache.org/guides/mini/guide-naming-conventions.html -mod_group_id=com.example.examplemod -# The authors of the mod. This is a simple text string that is used for display purposes in the mod list. -mod_authors=YourNameHere, OtherNameHere -# The description of the mod. This is a simple multiline text string that is used for display purposes in the mod list. -mod_description=Example mod description.\nNewline characters can be used and will be replaced properly. -# Pack version - this changes each minecraft release, in general. -pack_format_number=18 +## Mod Properties +mod_id=metabolism +mod_name=Metabolism +mod_license=MIT +mod_version=1.20.1-0.0.0-ALPHA +mod_group_id=lilypuree.metabolism +mod_authors=Lilypuree +mod_description=a temperature & hunger overhaul mod \ No newline at end of file diff --git a/logo.png b/logo.png new file mode 100644 index 0000000..d8979a4 Binary files /dev/null and b/logo.png differ diff --git a/settings.gradle b/settings.gradle index b359a59..e029595 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,11 +1,18 @@ pluginManagement { repositories { - mavenLocal() gradlePluginPortal() - maven { url = 'https://maven.neoforged.net/releases' } + maven { + name = 'MinecraftForge' + url = 'https://maven.minecraftforge.net/' + } + maven { url = 'https://maven.parchmentmc.org' } // Add this line + maven { + name = 'Sponge Snapshots' + url = 'https://repo.spongepowered.org/repository/maven-public/' + } } } plugins { id 'org.gradle.toolchains.foojay-resolver-convention' version '0.5.0' -} +} \ No newline at end of file diff --git a/src/main/java/com/example/examplemod/Config.java b/src/main/java/com/example/examplemod/Config.java deleted file mode 100644 index 38dcbcd..0000000 --- a/src/main/java/com/example/examplemod/Config.java +++ /dev/null @@ -1,63 +0,0 @@ -package com.example.examplemod; - -import net.minecraft.core.registries.BuiltInRegistries; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.item.Item; -import net.neoforged.bus.api.SubscribeEvent; -import net.neoforged.fml.common.Mod; -import net.neoforged.fml.event.config.ModConfigEvent; -import net.neoforged.neoforge.common.ModConfigSpec; - -import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; - -// An example config class. This is not required, but it's a good idea to have one to keep your config organized. -// Demonstrates how to use Forge's config APIs -@Mod.EventBusSubscriber(modid = ExampleMod.MODID, bus = Mod.EventBusSubscriber.Bus.MOD) -public class Config -{ - private static final ModConfigSpec.Builder BUILDER = new ModConfigSpec.Builder(); - - private static final ModConfigSpec.BooleanValue LOG_DIRT_BLOCK = BUILDER - .comment("Whether to log the dirt block on common setup") - .define("logDirtBlock", true); - - private static final ModConfigSpec.IntValue MAGIC_NUMBER = BUILDER - .comment("A magic number") - .defineInRange("magicNumber", 42, 0, Integer.MAX_VALUE); - - public static final ModConfigSpec.ConfigValue MAGIC_NUMBER_INTRODUCTION = BUILDER - .comment("What you want the introduction message to be for the magic number") - .define("magicNumberIntroduction", "The magic number is... "); - - // a list of strings that are treated as resource locations for items - private static final ModConfigSpec.ConfigValue> ITEM_STRINGS = BUILDER - .comment("A list of items to log on common setup.") - .defineListAllowEmpty("items", List.of("minecraft:iron_ingot"), Config::validateItemName); - - static final ModConfigSpec SPEC = BUILDER.build(); - - public static boolean logDirtBlock; - public static int magicNumber; - public static String magicNumberIntroduction; - public static Set items; - - private static boolean validateItemName(final Object obj) - { - return obj instanceof String itemName && BuiltInRegistries.ITEM.containsKey(new ResourceLocation(itemName)); - } - - @SubscribeEvent - static void onLoad(final ModConfigEvent event) - { - logDirtBlock = LOG_DIRT_BLOCK.get(); - magicNumber = MAGIC_NUMBER.get(); - magicNumberIntroduction = MAGIC_NUMBER_INTRODUCTION.get(); - - // convert the list of strings into a set of items - items = ITEM_STRINGS.get().stream() - .map(itemName -> BuiltInRegistries.ITEM.get(new ResourceLocation(itemName))) - .collect(Collectors.toSet()); - } -} diff --git a/src/main/java/com/example/examplemod/ExampleMod.java b/src/main/java/com/example/examplemod/ExampleMod.java deleted file mode 100644 index 4f50275..0000000 --- a/src/main/java/com/example/examplemod/ExampleMod.java +++ /dev/null @@ -1,129 +0,0 @@ -package com.example.examplemod; - -import com.mojang.logging.LogUtils; -import net.minecraft.client.Minecraft; -import net.minecraft.core.registries.BuiltInRegistries; -import net.minecraft.core.registries.Registries; -import net.minecraft.world.food.FoodProperties; -import net.minecraft.world.item.BlockItem; -import net.minecraft.world.item.CreativeModeTab; -import net.minecraft.world.item.CreativeModeTabs; -import net.minecraft.world.item.Item; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.Blocks; -import net.minecraft.world.level.block.state.BlockBehaviour; -import net.minecraft.world.level.material.MapColor; -import net.neoforged.api.distmarker.Dist; -import net.neoforged.bus.api.IEventBus; -import net.neoforged.bus.api.SubscribeEvent; -import net.neoforged.fml.ModLoadingContext; -import net.neoforged.fml.common.Mod; -import net.neoforged.fml.config.ModConfig; -import net.neoforged.fml.event.lifecycle.FMLClientSetupEvent; -import net.neoforged.fml.event.lifecycle.FMLCommonSetupEvent; -import net.neoforged.neoforge.common.NeoForge; -import net.neoforged.neoforge.event.BuildCreativeModeTabContentsEvent; -import net.neoforged.neoforge.event.server.ServerStartingEvent; -import net.neoforged.neoforge.registries.DeferredBlock; -import net.neoforged.neoforge.registries.DeferredHolder; -import net.neoforged.neoforge.registries.DeferredItem; -import net.neoforged.neoforge.registries.DeferredRegister; -import org.slf4j.Logger; - -// The value here should match an entry in the META-INF/mods.toml file -@Mod(ExampleMod.MODID) -public class ExampleMod -{ - // Define mod id in a common place for everything to reference - public static final String MODID = "examplemod"; - // Directly reference a slf4j logger - private static final Logger LOGGER = LogUtils.getLogger(); - // Create a Deferred Register to hold Blocks which will all be registered under the "examplemod" namespace - public static final DeferredRegister.Blocks BLOCKS = DeferredRegister.createBlocks(MODID); - // Create a Deferred Register to hold Items which will all be registered under the "examplemod" namespace - public static final DeferredRegister.Items ITEMS = DeferredRegister.createItems(MODID); - // Create a Deferred Register to hold CreativeModeTabs which will all be registered under the "examplemod" namespace - public static final DeferredRegister CREATIVE_MODE_TABS = DeferredRegister.create(Registries.CREATIVE_MODE_TAB, MODID); - - // Creates a new Block with the id "examplemod:example_block", combining the namespace and path - public static final DeferredBlock EXAMPLE_BLOCK = BLOCKS.registerSimpleBlock("example_block", BlockBehaviour.Properties.of().mapColor(MapColor.STONE)); - // Creates a new BlockItem with the id "examplemod:example_block", combining the namespace and path - public static final DeferredItem EXAMPLE_BLOCK_ITEM = ITEMS.registerSimpleBlockItem("example_block", EXAMPLE_BLOCK); - - // Creates a new food item with the id "examplemod:example_id", nutrition 1 and saturation 2 - public static final DeferredItem EXAMPLE_ITEM = ITEMS.registerSimpleItem("example_item", new Item.Properties().food(new FoodProperties.Builder() - .alwaysEat().nutrition(1).saturationMod(2f).build())); - - // Creates a creative tab with the id "examplemod:example_tab" for the example item, that is placed after the combat tab - public static final DeferredHolder EXAMPLE_TAB = CREATIVE_MODE_TABS.register("example_tab", () -> CreativeModeTab.builder() - .withTabsBefore(CreativeModeTabs.COMBAT) - .icon(() -> EXAMPLE_ITEM.get().getDefaultInstance()) - .displayItems((parameters, output) -> { - output.accept(EXAMPLE_ITEM.get()); // Add the example item to the tab. For your own tabs, this method is preferred over the event - }).build()); - - // The constructor for the mod class is the first code that is run when your mod is loaded. - // FML will recognize some parameter types like IEventBus or ModContainer and pass them in automatically. - public ExampleMod(IEventBus modEventBus) - { - // Register the commonSetup method for modloading - modEventBus.addListener(this::commonSetup); - - // Register the Deferred Register to the mod event bus so blocks get registered - BLOCKS.register(modEventBus); - // Register the Deferred Register to the mod event bus so items get registered - ITEMS.register(modEventBus); - // Register the Deferred Register to the mod event bus so tabs get registered - CREATIVE_MODE_TABS.register(modEventBus); - - // Register ourselves for server and other game events we are interested in - NeoForge.EVENT_BUS.register(this); - - // Register the item to a creative tab - modEventBus.addListener(this::addCreative); - - // Register our mod's ModConfigSpec so that FML can create and load the config file for us - ModLoadingContext.get().registerConfig(ModConfig.Type.COMMON, Config.SPEC); - } - - private void commonSetup(final FMLCommonSetupEvent event) - { - // Some common setup code - LOGGER.info("HELLO FROM COMMON SETUP"); - - if (Config.logDirtBlock) - LOGGER.info("DIRT BLOCK >> {}", BuiltInRegistries.BLOCK.getKey(Blocks.DIRT)); - - LOGGER.info(Config.magicNumberIntroduction + Config.magicNumber); - - Config.items.forEach((item) -> LOGGER.info("ITEM >> {}", item.toString())); - } - - // Add the example block item to the building blocks tab - private void addCreative(BuildCreativeModeTabContentsEvent event) - { - if (event.getTabKey() == CreativeModeTabs.BUILDING_BLOCKS) - event.accept(EXAMPLE_BLOCK_ITEM); - } - - // You can use SubscribeEvent and let the Event Bus discover methods to call - @SubscribeEvent - public void onServerStarting(ServerStartingEvent event) - { - // Do something when the server starts - LOGGER.info("HELLO from server starting"); - } - - // You can use EventBusSubscriber to automatically register all static methods in the class annotated with @SubscribeEvent - @Mod.EventBusSubscriber(modid = MODID, bus = Mod.EventBusSubscriber.Bus.MOD, value = Dist.CLIENT) - public static class ClientModEvents - { - @SubscribeEvent - public static void onClientSetup(FMLClientSetupEvent event) - { - // Some client setup code - LOGGER.info("HELLO FROM CLIENT SETUP"); - LOGGER.info("MINECRAFT NAME >> {}", Minecraft.getInstance().getUser().getName()); - } - } -} diff --git a/src/main/java/lilypuree/metabolism/MetabolismMod.java b/src/main/java/lilypuree/metabolism/MetabolismMod.java new file mode 100644 index 0000000..7dbae8d --- /dev/null +++ b/src/main/java/lilypuree/metabolism/MetabolismMod.java @@ -0,0 +1,67 @@ +package lilypuree.metabolism; + +import com.mojang.logging.LogUtils; +import lilypuree.metabolism.data.Environment; +import lilypuree.metabolism.config.Config; +import lilypuree.metabolism.data.Metabolites; +import lilypuree.metabolism.network.MetabolitesPacket; +import lilypuree.metabolism.network.Network; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.level.ServerPlayer; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.event.AddReloadListenerEvent; +import net.minecraftforge.event.OnDatapackSyncEvent; +import net.minecraftforge.eventbus.api.IEventBus; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.config.ModConfig; +import net.minecraftforge.fml.event.config.ModConfigEvent; +import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; +import net.minecraftforge.network.NetworkDirection; +import org.slf4j.Logger; + +@Mod(MetabolismMod.MOD_ID) +public class MetabolismMod { + public static final String MOD_ID = "metabolism"; + public static final Logger LOGGER = LogUtils.getLogger(); + + + public MetabolismMod() { + IEventBus modbus = FMLJavaModLoadingContext.get().getModEventBus(); + IEventBus forgebus = MinecraftForge.EVENT_BUS; + Registration.LOOT_CONDITIONS.register(modbus); + + Config.register(); + Network.init(); + + modbus.addListener(this::reloadConfig); + forgebus.addListener(this::addListener); + forgebus.addListener(this::syncMetabolites); + } + + private void reloadConfig(ModConfigEvent.Reloading event) { + if (event.getConfig().getType() == ModConfig.Type.CLIENT) + Config.CLIENT.reload(); + else if (event.getConfig().getType() == ModConfig.Type.COMMON) + Config.SERVER.reload(); + } + + private void addListener(AddReloadListenerEvent event) { + event.addListener(new Environment()); + event.addListener(new Metabolites()); + } + + private void syncMetabolites(OnDatapackSyncEvent event) { + if (event.getPlayer() != null) { + Network.channel.sendTo(new MetabolitesPacket(Metabolites.getMetaboliteMap()), event.getPlayer().connection.connection, NetworkDirection.PLAY_TO_CLIENT); + } else { + for (ServerPlayer player : event.getPlayerList().getPlayers()) { + Network.channel.sendTo(new MetabolitesPacket(Metabolites.getMetaboliteMap()), player.connection.connection, NetworkDirection.PLAY_TO_CLIENT); + } + } + } + + public static ResourceLocation getID(String path) { + return new ResourceLocation(MOD_ID, path); + } + +} diff --git a/src/main/java/lilypuree/metabolism/MetabolismTags.java b/src/main/java/lilypuree/metabolism/MetabolismTags.java new file mode 100644 index 0000000..2d51e31 --- /dev/null +++ b/src/main/java/lilypuree/metabolism/MetabolismTags.java @@ -0,0 +1,15 @@ +package lilypuree.metabolism; + +import net.minecraft.core.registries.Registries; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.tags.TagKey; +import net.minecraft.world.level.biome.Biome; + +public class MetabolismTags { + private static TagKey biomeTag(String path) { + return TagKey.create(Registries.BIOME, new ResourceLocation(MetabolismMod.MOD_ID, path)); + } + + public static final TagKey HOT_BIOMES = biomeTag("hot_biomes"); + public static final TagKey COLD_BIOMES = biomeTag("cold_biomes"); +} diff --git a/src/main/java/lilypuree/metabolism/Registration.java b/src/main/java/lilypuree/metabolism/Registration.java new file mode 100644 index 0000000..2b1d9ff --- /dev/null +++ b/src/main/java/lilypuree/metabolism/Registration.java @@ -0,0 +1,20 @@ +package lilypuree.metabolism; + +import lilypuree.metabolism.util.AdvancedWeatherCheck; +import net.minecraft.core.registries.Registries; +import net.minecraft.world.level.storage.loot.Serializer; +import net.minecraft.world.level.storage.loot.predicates.LootItemCondition; +import net.minecraft.world.level.storage.loot.predicates.LootItemConditionType; +import net.minecraftforge.registries.DeferredRegister; +import net.minecraftforge.registries.RegistryObject; + +public class Registration { + + public static final DeferredRegister LOOT_CONDITIONS = DeferredRegister.create(Registries.LOOT_CONDITION_TYPE, MetabolismMod.MOD_ID); + + public static final RegistryObject ADVANCED_WEATHER_CHECK = registerLootCondition("advanced_weather_check", new AdvancedWeatherCheck.Serializer()); + + private static RegistryObject registerLootCondition(String name, Serializer serializer) { + return LOOT_CONDITIONS.register(name, () -> new LootItemConditionType(serializer)); + } +} diff --git a/src/main/java/lilypuree/metabolism/client/ClientHandler.java b/src/main/java/lilypuree/metabolism/client/ClientHandler.java new file mode 100644 index 0000000..ef9d190 --- /dev/null +++ b/src/main/java/lilypuree/metabolism/client/ClientHandler.java @@ -0,0 +1,28 @@ +package lilypuree.metabolism.client; + + +import lilypuree.metabolism.metabolism.Metabolism; +import lilypuree.metabolism.metabolism.FoodDataDuck; +import lilypuree.metabolism.network.ClientSyncMessage; +import net.minecraft.client.Minecraft; +import net.minecraftforge.network.NetworkEvent; + +import java.util.function.Supplier; + +/** + * Handles information synced from the server. Only information actually needed by the client should + * be synced. This information will likely not be updated every tick. + */ +public class ClientHandler { + private ClientHandler() { + } + + public static void handleSyncMessage(ClientSyncMessage msg, Supplier ctx) { + Metabolism metabolism = getClientMetabolism(Minecraft.getInstance()); + metabolism.syncOnClient(msg); + } + + public static Metabolism getClientMetabolism(Minecraft mc) { + return ((FoodDataDuck) mc.player.getFoodData()).getMetabolism(); + } +} diff --git a/src/main/java/lilypuree/metabolism/client/ClientInit.java b/src/main/java/lilypuree/metabolism/client/ClientInit.java new file mode 100644 index 0000000..0be34a3 --- /dev/null +++ b/src/main/java/lilypuree/metabolism/client/ClientInit.java @@ -0,0 +1,27 @@ +package lilypuree.metabolism.client; + +import lilypuree.metabolism.MetabolismMod; +import lilypuree.metabolism.client.gui.DebugOverlay; +import lilypuree.metabolism.client.gui.EnergyDisplayHandler; +import lilypuree.metabolism.client.gui.WarmthDisplayHandler; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.client.event.RegisterParticleProvidersEvent; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.common.Mod; + +@Mod.EventBusSubscriber(modid = MetabolismMod.MOD_ID, value = Dist.CLIENT, bus = Mod.EventBusSubscriber.Bus.MOD) +public class ClientInit { + //Scaling Health says this works... + static { + MinecraftForge.EVENT_BUS.register(EnergyDisplayHandler.INSTANCE); + MinecraftForge.EVENT_BUS.register(WarmthDisplayHandler.INSTANCE); + + DebugOverlay.init(); + } + + @SubscribeEvent + public static void registerParticleFactories(RegisterParticleProvidersEvent event) { + + } +} diff --git a/src/main/java/lilypuree/metabolism/client/ClientMetabolites.java b/src/main/java/lilypuree/metabolism/client/ClientMetabolites.java new file mode 100644 index 0000000..2adfd4d --- /dev/null +++ b/src/main/java/lilypuree/metabolism/client/ClientMetabolites.java @@ -0,0 +1,21 @@ +package lilypuree.metabolism.client; + +import lilypuree.metabolism.MetabolismMod; +import lilypuree.metabolism.data.Metabolite; +import lilypuree.metabolism.data.Metabolites; +import net.minecraft.world.item.Item; + +import java.util.Map; + +public class ClientMetabolites { + private static Map metabolites; + + public static void setClientMetabolites(Map data) { + metabolites = data; + MetabolismMod.LOGGER.debug("Loaded MetabolismData on the client."); + } + + public static Map getClientMetabolites() { + return metabolites; + } +} diff --git a/src/main/java/lilypuree/metabolism/client/gui/DebugOverlay.java b/src/main/java/lilypuree/metabolism/client/gui/DebugOverlay.java new file mode 100644 index 0000000..95368c1 --- /dev/null +++ b/src/main/java/lilypuree/metabolism/client/gui/DebugOverlay.java @@ -0,0 +1,48 @@ +package lilypuree.metabolism.client.gui; + +import com.google.common.collect.ImmutableList; +import lilypuree.metabolism.client.ClientHandler; +import lilypuree.metabolism.config.Config; +import lilypuree.metabolism.metabolism.Metabolism; +import lilypuree.metabolism.util.Anchor; +import net.minecraft.client.Minecraft; +import net.minecraft.client.player.LocalPlayer; + +import java.util.List; + +public class DebugOverlay extends DebugRenderOverlay { + private static final String FLOAT_FORMAT = "%.5f"; + public static DebugOverlay INSTANCE = new DebugOverlay(); + + public static void init() { + } + + @Override + public List getDebugText() { + LocalPlayer player = Minecraft.getInstance().player; + if (player == null) return ImmutableList.of(); + Metabolism metabolism = ClientHandler.getClientMetabolism(Minecraft.getInstance()); + return ImmutableList.of( + "- Warmth=" + String.format(FLOAT_FORMAT, metabolism.getWarmth()), + "- Heat=" + String.format(FLOAT_FORMAT, metabolism.getHeat()), + "- Food=" + String.format(FLOAT_FORMAT, metabolism.getFood()), + "- Energy=" + String.format(FLOAT_FORMAT, metabolism.getEnergy()) + ); + } + + + @Override + public boolean isHidden() { + return !Config.CLIENT.debugShowOverlay(); + } + + @Override + public Anchor getAnchorPoint() { + return Config.CLIENT.debugOverlayAnchor(); + } + + @Override + public float getTextScale() { + return Config.CLIENT.debugOverlayTextScale(); + } +} diff --git a/src/main/java/lilypuree/metabolism/client/gui/DebugRenderOverlay.java b/src/main/java/lilypuree/metabolism/client/gui/DebugRenderOverlay.java new file mode 100644 index 0000000..1460392 --- /dev/null +++ b/src/main/java/lilypuree/metabolism/client/gui/DebugRenderOverlay.java @@ -0,0 +1,117 @@ +// +// Credits: SilentLib by SilentChaos512 +// + +package lilypuree.metabolism.client.gui; + +import com.mojang.blaze3d.platform.Window; +import com.mojang.blaze3d.vertex.PoseStack; +import lilypuree.metabolism.util.Anchor; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.Font; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraftforge.client.event.RenderGuiOverlayEvent; +import net.minecraftforge.client.gui.overlay.VanillaGuiOverlay; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.event.TickEvent; + +import javax.annotation.Nonnegative; +import java.util.ArrayList; +import java.util.List; + +public abstract class DebugRenderOverlay { + protected static final String SPLITTER = "="; + private static final int DEFAULT_UPDATE_FREQUENCY = 10; + private static final int DEFAULT_SPLIT_WIDTH = 100; + private static final int LINE_HEIGHT = 10; + private List debugText; + private int textWidth; + private int textHeight; + + private int ticksPassed; + + protected DebugRenderOverlay() { + this.debugText = new ArrayList<>(); + this.ticksPassed = 0; + MinecraftForge.EVENT_BUS.addListener(this::renderTick); + MinecraftForge.EVENT_BUS.addListener(this::clientTick); + } + + public abstract List getDebugText(); + + public abstract float getTextScale(); + + public Anchor getAnchorPoint() { + return Anchor.TOP_LEFT; + } + + public int getMarginSize() { + return 3; + } + + @Nonnegative + public int getUpdateFrequency() { + return DEFAULT_UPDATE_FREQUENCY; + } + + @Nonnegative + public int getSplitWidth() { + return DEFAULT_SPLIT_WIDTH; + } + + public abstract boolean isHidden(); + + + protected void drawLine(GuiGraphics graphics, Font font, String line, int x, int y, int color) { + String[] array = line.split(SPLITTER); + if (array.length == 2) { + graphics.drawString(font, array[0].trim(), x, y, color); + graphics.drawString(font, array[1].trim(), x + this.getSplitWidth(), y, color); + } else { + graphics.drawString(font, line, x, y, color); + } + } + + public void renderTick(RenderGuiOverlayEvent.Post event) { + Minecraft mc = Minecraft.getInstance(); + if (!this.isHidden() && !this.debugText.isEmpty() && !mc.isPaused() && !mc.options.renderDebug && event.getOverlay() == VanillaGuiOverlay.CHAT_PANEL.type()) { + float scale = this.getTextScale(); + if (scale > 0.0F) { + Font font = mc.font; + PoseStack matrix = event.getGuiGraphics().pose(); + matrix.pushPose(); + matrix.scale(scale, scale, 1.0F); + Window mainWindow = mc.getWindow(); + int x = (int) ((float) this.getAnchorPoint().getX(mainWindow.getGuiScaledWidth(), this.textWidth, this.getMarginSize()) / this.getTextScale()); + int y = (int) ((float) this.getAnchorPoint().getY(mainWindow.getGuiScaledHeight(), this.textHeight, this.getMarginSize()) / this.getTextScale()); + + for (String line : debugText) { + this.drawLine(event.getGuiGraphics(), font, line, x, y, 16777215); + y += LINE_HEIGHT; + } + matrix.popPose(); + } + } + } + + public void clientTick(TickEvent.ClientTickEvent event) { + if (event.phase == TickEvent.Phase.START) + return; + if (this.getUpdateFrequency() == 0 || ++this.ticksPassed % this.getUpdateFrequency() == 0) { + this.debugText = this.getDebugText(); + Font font = Minecraft.getInstance().font; + this.textWidth = 0; + this.textHeight = LINE_HEIGHT * this.debugText.size(); + debugText.forEach(line -> { + String[] array = line.split(SPLITTER); + if (array.length == 2) { + int width = this.getSplitWidth() + font.width(array[1]); + this.textWidth = Math.max(this.textWidth, width); + } else { + this.textWidth = Math.max(this.textWidth, font.width(line)); + } + }); + } + } + +} diff --git a/src/main/java/lilypuree/metabolism/client/gui/EnergyDisplayHandler.java b/src/main/java/lilypuree/metabolism/client/gui/EnergyDisplayHandler.java new file mode 100644 index 0000000..711d89b --- /dev/null +++ b/src/main/java/lilypuree/metabolism/client/gui/EnergyDisplayHandler.java @@ -0,0 +1,81 @@ +package lilypuree.metabolism.client.gui; + +import com.mojang.blaze3d.vertex.PoseStack; +import lilypuree.metabolism.MetabolismMod; +import lilypuree.metabolism.client.ClientHandler; +import lilypuree.metabolism.config.Config; +import lilypuree.metabolism.metabolism.Metabolism; +import lilypuree.metabolism.util.Anchor; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.util.Mth; +import net.minecraft.world.entity.player.Player; +import net.minecraftforge.client.event.RenderGuiOverlayEvent; +import net.minecraftforge.client.gui.overlay.VanillaGuiOverlay; +import net.minecraftforge.eventbus.api.SubscribeEvent; + +public class EnergyDisplayHandler extends Screen { + public static final EnergyDisplayHandler INSTANCE = new EnergyDisplayHandler(Component.empty()); + private static final ResourceLocation TEXTURE = new ResourceLocation(MetabolismMod.MOD_ID, "textures/gui/hud.png"); + + private EnergyDisplayHandler(Component title) { + super(title); + } + + @SubscribeEvent + public void onRenderOverlay(RenderGuiOverlayEvent.Post event) { + if (event.getOverlay() != VanillaGuiOverlay.DEBUG_TEXT.type() || !Config.CLIENT.energyBarShow()) return; + + Minecraft mc = Minecraft.getInstance(); + Player player = mc.player; + if (player == null) return; + + Metabolism metabolism = ClientHandler.getClientMetabolism(mc); + render(event.getGuiGraphics(), mc, Mth.floor(metabolism.getFood()), Mth.floor(metabolism.getEnergy())); + } + + public void render(GuiGraphics graphics, Minecraft mc, int foodLevel, int energyLevel) { + Anchor anchor = Config.CLIENT.energyBarAnchor(); + int posX = anchor.getX(mc.getWindow().getGuiScaledWidth(), 21, 5) + Config.CLIENT.energyBarOffsetX(); + int posY = anchor.getY(mc.getWindow().getGuiScaledHeight(), 50, 5) + Config.CLIENT.energyBarOffsetY(); + + graphics.blit(TEXTURE, posX, posY, 235, 0, 21, 50); + renderBar(graphics, posX + 2, posY, 216, foodLevel); + renderBar(graphics, posX + 12, posY, 226, energyLevel); + } + + private void renderBar(GuiGraphics graphics, int posX, int posY, int uOffset, int level) { + posY += 34; + int I = level % 10; + int D = (level / 10) % 10; + int C = level / 100; + for (int i = 1; i < 10; i++) { + int flag = i <= I ? 1 : 0; + flag += i <= D ? 2 : 0; + flag += i <= C ? 4 : 0; + if (flag == 0) break; + + int vOffset = (flag - 1) * 4; + graphics.blit(TEXTURE, posX, posY, uOffset, vOffset, 7, 3); + posY -= 4; + } + } + + private void renderStat(GuiGraphics graphics, Minecraft mc, int posX, int posY, float textScale, int stat) { + if (textScale > 0) { + PoseStack stack = graphics.pose(); + stack.pushPose(); + stack.scale(textScale, textScale, 1); + graphics.drawString( + mc.font, String.format("x %d", stat), + posX / textScale + 12, + posY / textScale + 6, + 0x00ff00, + true); + stack.popPose(); + } + } +} diff --git a/src/main/java/lilypuree/metabolism/client/gui/WarmthDisplayHandler.java b/src/main/java/lilypuree/metabolism/client/gui/WarmthDisplayHandler.java new file mode 100644 index 0000000..70aef97 --- /dev/null +++ b/src/main/java/lilypuree/metabolism/client/gui/WarmthDisplayHandler.java @@ -0,0 +1,84 @@ +package lilypuree.metabolism.client.gui; + +import com.mojang.blaze3d.systems.RenderSystem; +import lilypuree.metabolism.MetabolismMod; +import lilypuree.metabolism.client.ClientHandler; +import lilypuree.metabolism.metabolism.Metabolism; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.util.Mth; +import net.minecraftforge.client.event.RenderGuiOverlayEvent; +import net.minecraftforge.client.gui.overlay.ForgeGui; +import net.minecraftforge.client.gui.overlay.VanillaGuiOverlay; +import net.minecraftforge.eventbus.api.SubscribeEvent; + +public class WarmthDisplayHandler extends Screen { + public static final WarmthDisplayHandler INSTANCE = new WarmthDisplayHandler(Component.empty()); + + private static final ResourceLocation TEXTURE = new ResourceLocation(MetabolismMod.MOD_ID, "textures/gui/hud.png"); + + + private WarmthDisplayHandler(Component pTitle) { + super(pTitle); + } + + @SubscribeEvent(receiveCanceled = true) + public void onFoodDraw(RenderGuiOverlayEvent.Pre event) { + if (event.getOverlay() != VanillaGuiOverlay.FOOD_LEVEL.type() || + Minecraft.getInstance().options.hideGui || + !getGui().shouldDrawSurvivalElements() + ) return; + + Minecraft mc = Minecraft.getInstance(); + + event.setCanceled(true); + mc.getProfiler().push("metabolismRenderWarmth"); + renderWarmth(event.getGuiGraphics(), mc); + + mc.getProfiler().pop(); + } + + private void renderWarmth(GuiGraphics graphics, Minecraft mc) { + RenderSystem.enableBlend(); + int left = mc.getWindow().getGuiScaledWidth() / 2 + 91; + int top = mc.getWindow().getGuiScaledHeight() - getGui().rightHeight; + int tickCount = getGui().getGuiTicks(); + getGui().rightHeight += 10; + + Metabolism metabolism = ClientHandler.getClientMetabolism(mc); + int maxOrbs = Mth.floor(metabolism.getMaxWarmth() / 2); + float heat = metabolism.getHeat(); + int heatOrColdSpriteV = heat > 0 ? 9 : 18; + int warmth = Mth.floor(metabolism.getWarmth()); + int heatThreshold = Mth.floor(metabolism.getMaxWarmth() - Math.abs(heat)); + for (int orb = 0; orb < maxOrbs; ++orb) { + int idx = orb * 2 + 1; + int x = left - orb * 8 - 9; + int y = top; + + graphics.blit(TEXTURE, x, top, 18, 0, 9, 9); + + + if (idx < warmth) + graphics.blit(TEXTURE, x, y, 0, 0, 9, 9); + else if (idx == warmth) + graphics.blit(TEXTURE, x, y, 9, 0, 9, 9); + + + if (idx == heatThreshold) + graphics.blit(TEXTURE, x, y, 9, heatOrColdSpriteV, 9, 9); + else if (idx > heatThreshold) + graphics.blit(TEXTURE, x, y, 0, heatOrColdSpriteV, 9, 9); + } + RenderSystem.disableBlend(); + } + + + public static ForgeGui getGui() { + return (ForgeGui) Minecraft.getInstance().gui; + } + +} diff --git a/src/main/java/lilypuree/metabolism/config/Config.java b/src/main/java/lilypuree/metabolism/config/Config.java new file mode 100644 index 0000000..808a8ab --- /dev/null +++ b/src/main/java/lilypuree/metabolism/config/Config.java @@ -0,0 +1,124 @@ +package lilypuree.metabolism.config; + +import lilypuree.metabolism.MetabolismMod; +import lilypuree.metabolism.util.Anchor; +import net.minecraftforge.common.ForgeConfigSpec; +import net.minecraftforge.common.ForgeConfigSpec.*; +import net.minecraftforge.fml.ModLoadingContext; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.config.ModConfig; +import org.apache.commons.lang3.tuple.Pair; + +// An example config class. This is not required, but it's a good idea to have one to keep your config organized. +// Demonstrates how to use Forge's config APIs +@Mod.EventBusSubscriber(modid = MetabolismMod.MOD_ID, bus = Mod.EventBusSubscriber.Bus.MOD) +public class Config { + + public static ForgeConfigSpec CLIENT_SPEC; + public static ForgeConfigSpec SERVER_SPEC; + public static MetabolismClientConfig CLIENT; + public static MetabolismServerConfig SERVER; + + static { + Pair pCli = new Builder().configure(ClientConfig::new); + Pair pCom = new Builder().configure(ServerConfig::new); + CLIENT_SPEC = pCli.getRight(); + CLIENT = pCli.getLeft(); + + SERVER_SPEC = pCom.getRight(); + SERVER = pCom.getLeft(); + } + + public static void register() { + ModLoadingContext.get().registerConfig(ModConfig.Type.CLIENT, CLIENT_SPEC); + ModLoadingContext.get().registerConfig(ModConfig.Type.SERVER, SERVER_SPEC); + } + + private static class ServerConfig implements MetabolismServerConfig { + public final BooleanValue preciseFeedback; + + + public ServerConfig(Builder builder) { + preciseFeedback = builder.comment("enable more precise heat feedback").define("metabolism.heat.preciseFeedback", false); + } + + @Override + public boolean preciseFeedback() { + return preciseFeedback.get(); + } + + @Override + public void reload() { + + } + } + + private static class ClientConfig implements MetabolismClientConfig { + public final BooleanValue debugShowOverlay; + public final EnumValue debugOverlayAnchor; + public final DoubleValue debugOverlayTextScale; + + public final BooleanValue energyBarShow; + public final EnumValue energyBarAnchor; + public final IntValue energyBarOffsetX; + public final IntValue energyBarOffsetY; + public final DoubleValue energyBarTextScale; + + public ClientConfig(Builder builder) { + debugShowOverlay = builder.comment("Enable debug overlay").define("debug.overlay.show", false); + debugOverlayAnchor = builder.comment("Position of the debug overlay").defineEnum("debug.overlay.anchor", Anchor.TOP_RIGHT); + debugOverlayTextScale = builder.comment("Overlay text size. 1 = standard-sized text").defineInRange("debug.overlay.textscale", 0.75, 0.01, Double.MAX_VALUE); + + energyBarShow = builder.comment("Make the energy bar visible").define("energy.bar.show", true); + energyBarAnchor = builder.comment("Position of the energy bar").defineEnum("energy.bar.anchor", Anchor.BOTTOM_LEFT); + energyBarOffsetX = builder.comment("Fine-tune the energy bar position").defineInRange("energy.bar.offsetX", 0, Integer.MIN_VALUE, Integer.MAX_VALUE); + energyBarOffsetY = builder.comment("Fine-tune the energy bar position").defineInRange("energy.bar.offsetY", 0, Integer.MIN_VALUE, Integer.MAX_VALUE); + energyBarTextScale = builder.comment("Display scale of the energy bar text").defineInRange("energy.bar.text.scale", 0.6, 0, Double.MAX_VALUE); + } + + @Override + public boolean debugShowOverlay() { + return debugShowOverlay.get(); + } + + @Override + public Anchor debugOverlayAnchor() { + return debugOverlayAnchor.get(); + } + + @Override + public float debugOverlayTextScale() { + return debugOverlayTextScale.get().floatValue(); + } + + @Override + public boolean energyBarShow() { + return energyBarShow.get(); + } + + @Override + public Anchor energyBarAnchor() { + return energyBarAnchor.get(); + } + + @Override + public int energyBarOffsetX() { + return energyBarOffsetX.get(); + } + + @Override + public int energyBarOffsetY() { + return energyBarOffsetY.get(); + } + + @Override + public float energyBarTextScale() { + return energyBarTextScale.get().floatValue(); + } + + @Override + public void reload() { + + } + } +} diff --git a/src/main/java/lilypuree/metabolism/config/MetabolismClientConfig.java b/src/main/java/lilypuree/metabolism/config/MetabolismClientConfig.java new file mode 100644 index 0000000..0d0e7e7 --- /dev/null +++ b/src/main/java/lilypuree/metabolism/config/MetabolismClientConfig.java @@ -0,0 +1,23 @@ +package lilypuree.metabolism.config; + +import lilypuree.metabolism.util.Anchor; + +public interface MetabolismClientConfig { + boolean debugShowOverlay(); + + Anchor debugOverlayAnchor(); + + float debugOverlayTextScale(); + + boolean energyBarShow(); + + Anchor energyBarAnchor(); + + int energyBarOffsetX(); + + int energyBarOffsetY(); + + float energyBarTextScale(); + + void reload(); +} diff --git a/src/main/java/lilypuree/metabolism/config/MetabolismServerConfig.java b/src/main/java/lilypuree/metabolism/config/MetabolismServerConfig.java new file mode 100644 index 0000000..731e4ca --- /dev/null +++ b/src/main/java/lilypuree/metabolism/config/MetabolismServerConfig.java @@ -0,0 +1,10 @@ +package lilypuree.metabolism.config; + +public interface MetabolismServerConfig { + + boolean preciseFeedback(); + + + + void reload(); +} diff --git a/src/main/java/lilypuree/metabolism/data/Environment.java b/src/main/java/lilypuree/metabolism/data/Environment.java new file mode 100644 index 0000000..a938ec0 --- /dev/null +++ b/src/main/java/lilypuree/metabolism/data/Environment.java @@ -0,0 +1,109 @@ +package lilypuree.metabolism.data; + +import com.google.common.collect.ImmutableSet; +import com.google.gson.Gson; +import com.google.gson.JsonElement; +import com.google.gson.JsonParseException; +import lilypuree.metabolism.MetabolismMod; +import net.minecraft.core.BlockPos; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.packs.resources.ResourceManager; +import net.minecraft.server.packs.resources.SimpleJsonResourceReloadListener; +import net.minecraft.util.Mth; +import net.minecraft.util.profiling.ProfilerFiller; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.level.storage.loot.LootContext; +import net.minecraft.world.level.storage.loot.LootDataType; +import net.minecraft.world.level.storage.loot.LootParams; +import net.minecraft.world.level.storage.loot.parameters.LootContextParamSets; +import net.minecraft.world.level.storage.loot.parameters.LootContextParams; +import net.minecraft.world.phys.AABB; +import net.minecraft.world.phys.Vec3; +import org.apache.commons.lang3.mutable.MutableFloat; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.util.Map; + +public class Environment extends SimpleJsonResourceReloadListener { + + private static Environment currentInstance = null; + private static Environment reloadingInstance = null; + private static final Gson GSON = LootDataType.PREDICATE.parser(); + public static final Logger LOGGER = LogManager.getLogger(); + public static final String FOLDER = "environment_effects"; + private ImmutableSet localEffects; + private ImmutableSet rangedEffects; + private float maxRange = 0.0F; + + public Environment() { + super(GSON, FOLDER); + if (currentInstance == null) + currentInstance = this; + else + reloadingInstance = this; + } + + public static Environment get() { + if (currentInstance == null) + throw new RuntimeException("Tried to access Environment too early!"); + return currentInstance; + } + + @Override + protected void apply(Map map, ResourceManager resourceManager, ProfilerFiller profiler) { + ImmutableSet.Builder locals = ImmutableSet.builder(); + ImmutableSet.Builder ranged = ImmutableSet.builder(); + map.entrySet().stream() + .filter(entry -> entry.getKey().getNamespace().equals(MetabolismMod.MOD_ID)) + .forEach(entry -> { + try { + EnvironmentEffect effect = EnvironmentEffect.deserialize(entry.getKey(), entry.getValue(), resourceManager); + if (effect.isRanged()) { + ranged.add(effect); + if (effect.range > this.maxRange) { + this.maxRange = effect.range; + } + } else + locals.add(effect); + } catch (JsonParseException exception) { + LOGGER.error("Cannot parse environment effect " + entry.getKey(), exception); + } + }); + this.localEffects = locals.build(); + this.rangedEffects = ranged.build(); + + LOGGER.debug("Finished parsing environment effects"); + if (this == reloadingInstance) { + currentInstance = this; + reloadingInstance = null; + } + } + + public EnvironmentEffect.Combined getCurrentEffect(ServerLevel level, Player player) { + LootParams.Builder builder = (new LootParams.Builder(level)).withParameter(LootContextParams.THIS_ENTITY, player).withParameter(LootContextParams.ORIGIN, player.position()); + LootParams params = builder.create(LootContextParamSets.SELECTOR); + LootContext lootContext = new LootContext.Builder(params).create(null); + + EnvironmentEffect.Combined combined = new EnvironmentEffect.Combined(level.isNight()); + localEffects.stream() + .filter(effect -> effect.canApply(lootContext)) + .forEach(combined::addEffect); + + applyRangedEffects(level, player, combined); + return combined; + } + + + private void applyRangedEffects(ServerLevel level, Player player, EnvironmentEffect.Combined combined) { + AABB range = AABB.ofSize(player.position(), maxRange * 2, maxRange * 2, maxRange * 2); + BlockPos.betweenClosedStream(range).forEach(pos -> { + Vec3 position = pos.getCenter(); + float distToPlayer = Mth.sqrt((float) player.position().distanceToSqr(position)); + rangedEffects.stream() + .filter(effect -> effect.canApplyRanged(level, position, distToPlayer)) + .forEach(combined::addEffect); + }); + } +} diff --git a/src/main/java/lilypuree/metabolism/data/EnvironmentEffect.java b/src/main/java/lilypuree/metabolism/data/EnvironmentEffect.java new file mode 100644 index 0000000..9a221c7 --- /dev/null +++ b/src/main/java/lilypuree/metabolism/data/EnvironmentEffect.java @@ -0,0 +1,147 @@ +package lilypuree.metabolism.data; + +import com.google.gson.*; +import lilypuree.metabolism.metabolism.Metabolism; +import lilypuree.metabolism.mixin.LocationCheckAccessor; +import net.minecraft.advancements.critereon.LocationPredicate; +import net.minecraft.core.Position; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.packs.resources.ResourceManager; +import net.minecraft.util.GsonHelper; +import net.minecraft.util.Mth; +import net.minecraft.world.level.storage.loot.LootContext; +import net.minecraft.world.level.storage.loot.LootDataType; +import net.minecraft.world.level.storage.loot.predicates.LocationCheck; +import net.minecraft.world.level.storage.loot.predicates.LootItemCondition; + +public class EnvironmentEffect { + private final ResourceLocation name; + protected final LootItemCondition condition; + protected final float warmthEffect; + protected final float heatTarget; + protected final float nightMultiplier; + protected final float range; + protected final boolean isAdditive; + protected final boolean isResistance; + + + public EnvironmentEffect(ResourceLocation name, LootItemCondition condition, float warmthEffect, float heatTarget, float nightMultiplier, float range, boolean isAdditive, boolean isResistance) { + this.name = name; + this.condition = condition; + this.warmthEffect = warmthEffect; + this.heatTarget = heatTarget; + this.nightMultiplier = nightMultiplier; + this.range = range; + this.isAdditive = isAdditive; + this.isResistance = isResistance; + } + + public float getWarmthEffect(boolean isNight) { + return isNight ? warmthEffect * nightMultiplier : warmthEffect; + } + + public float getHeatTarget(boolean isNight) { + return isNight ? heatTarget * nightMultiplier : heatTarget; + } + + public boolean isRanged() { + return range > 0; + } + + public boolean canApply(LootContext context) { + return this.condition.test(context); + } + + public boolean canApplyRanged(ServerLevel level, Position pos, float distToPlayer) { + if (condition instanceof LocationCheck location && range >= distToPlayer) { + LocationPredicate predicate = ((LocationCheckAccessor) location).getPredicate(); + return predicate.matches(level, pos.x(), pos.y(), pos.z()); + } else return false; + } + + + public static class Combined { + private boolean isNight; + private float additiveWarmthEffect = 0; + private float warmthEffect = 0; + private float additiveHeatTarget = 0; + private float heatTarget = 0; + private float coldTarget = 0; + private float heatResistance = 0; + private float coldResistance = 0; + private float additiveHeatResistance = 0; + private float additiveColdResistance = 0; + + public Combined(boolean isNight) { + this.isNight = isNight; + } + + public void addEffect(EnvironmentEffect effect) { + float effectHeatTarget = effect.getHeatTarget(isNight); + + + if (effect.isAdditive) { + additiveWarmthEffect += effect.getWarmthEffect(isNight); + + if (effect.isResistance) { + if (effectHeatTarget > 0) + additiveColdResistance += effectHeatTarget; + else if (effectHeatTarget < 0) + additiveHeatResistance += effectHeatTarget; + } else + additiveHeatTarget += effectHeatTarget; + } else { + warmthEffect = Math.max(warmthEffect, effect.getWarmthEffect(isNight)); + + if (effect.isResistance) { + if (effectHeatTarget > 0) + coldResistance = Math.max(coldResistance, effectHeatTarget); + else if (effectHeatTarget < 0) + heatResistance = Math.min(heatResistance, effectHeatTarget); + } else { + if (effectHeatTarget > 0) + heatTarget = Math.max(heatTarget, effectHeatTarget); + else if (effectHeatTarget < 0) + coldTarget = Math.min(coldTarget, effectHeatTarget); + } + } + } + + public float getCombinedHeatTarget() { + float combinedHeatTarget = heatTarget + coldTarget + additiveHeatTarget; + if (combinedHeatTarget == 0) + return 0; + else if (combinedHeatTarget > 0) { //HOT + return Math.max(combinedHeatTarget + additiveHeatResistance, 0); + } else { //COLD + return Math.min(combinedHeatTarget + additiveColdResistance, 0); + } + } + + public float getCombinedWarmthEffect() { + return warmthEffect + additiveWarmthEffect; + } + } + + public static EnvironmentEffect deserialize(ResourceLocation location, JsonElement json, ResourceManager resourceManager) throws JsonParseException { + JsonObject jsonObject = GsonHelper.convertToJsonObject(json, "environment effect"); + float warmthEffect = GsonHelper.getAsFloat(jsonObject, "warmth_effect", 0.0F); + float heatTarget = GsonHelper.getAsFloat(jsonObject, "heat_target", 0.0F); + float nightMultiplier = GsonHelper.getAsFloat(jsonObject, "night_multiplier", 1.0F); + float range = GsonHelper.getAsFloat(jsonObject, "range", 0.0F); + boolean isAdditive = GsonHelper.getAsBoolean(jsonObject, "is_additive", false); + boolean isResistance = GsonHelper.getAsBoolean(jsonObject, "is_resistance", false); + LootItemCondition condition; + if (!jsonObject.has("conditions")) { + throw new JsonSyntaxException("No conditions defined for environment effect"); + } else { + JsonElement element = GsonHelper.getNonNull(jsonObject, "conditions"); + condition = LootDataType.PREDICATE.deserialize(location, element, resourceManager) + .orElseThrow(() -> new JsonParseException("Environment effect condition malformed")); + if (range > 0 && !(condition instanceof LocationCheck)) + throw new JsonSyntaxException("Ranged Environment effect needs a location predicate"); + } + return new EnvironmentEffect(location, condition, warmthEffect, heatTarget, nightMultiplier, range, isAdditive, isResistance); + } +} diff --git a/src/main/java/lilypuree/metabolism/data/Metabolite.java b/src/main/java/lilypuree/metabolism/data/Metabolite.java new file mode 100644 index 0000000..06633c7 --- /dev/null +++ b/src/main/java/lilypuree/metabolism/data/Metabolite.java @@ -0,0 +1,15 @@ +package lilypuree.metabolism.data; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; + +public record Metabolite(float warmth, float energy, float food) { + public static final Codec CODEC = RecordCodecBuilder.create(inst -> + inst.group( + Codec.FLOAT.fieldOf("warmth").forGetter(Metabolite::warmth), + Codec.FLOAT.fieldOf("energy").forGetter(Metabolite::energy), + Codec.FLOAT.fieldOf("food").forGetter(Metabolite::food) + ).apply(inst, Metabolite::new)); + + public static final Metabolite DEFAULT = new Metabolite(0.0F, 0.0F, 0.0F); +} diff --git a/src/main/java/lilypuree/metabolism/data/Metabolites.java b/src/main/java/lilypuree/metabolism/data/Metabolites.java new file mode 100644 index 0000000..d4391fd --- /dev/null +++ b/src/main/java/lilypuree/metabolism/data/Metabolites.java @@ -0,0 +1,75 @@ +package lilypuree.metabolism.data; + +import com.google.common.collect.ImmutableMap; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonElement; +import com.mojang.serialization.JsonOps; +import lilypuree.metabolism.client.ClientMetabolites; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.packs.resources.ResourceManager; +import net.minecraft.server.packs.resources.SimpleJsonResourceReloadListener; +import net.minecraft.util.profiling.ProfilerFiller; +import net.minecraft.world.item.Item; +import net.minecraftforge.fml.DistExecutor; +import net.minecraftforge.registries.ForgeRegistries; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.util.Map; +import java.util.function.Consumer; + +public class Metabolites extends SimpleJsonResourceReloadListener { + private static Metabolites currentInstance = null; + private static Metabolites reloadingInstance = null; + private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create(); + public static final Logger LOGGER = LogManager.getLogger("Metabolites"); + public static final String FOLDER = "metabolites"; + + private ImmutableMap metaboliteMap; + + public Metabolites() { + super(GSON, FOLDER); + if (currentInstance == null) + currentInstance = this; + else + reloadingInstance = this; + } + public static Map getMetabolites() { + return DistExecutor.unsafeRunForDist( + () -> ClientMetabolites::getClientMetabolites, + () -> Metabolites::getMetaboliteMap + ); + } + + public static Map getMetaboliteMap() { + if (currentInstance == null) + throw new RuntimeException("Tried to access Metabolites too early!"); + return currentInstance.metaboliteMap; + } + @Override + protected void apply(Map map, ResourceManager resourceManager, ProfilerFiller profiler) { + ImmutableMap.Builder builder = ImmutableMap.builder(); + map.forEach((location, value) -> { + Item item = ForgeRegistries.ITEMS.getValue(location); + if (item != null) { + Metabolite metabolite = Metabolite.CODEC.parse(JsonOps.INSTANCE, value) + .getOrThrow(false, prefix("Metabolite for " + location + ": ")); + builder.put(item, metabolite); + } else + LOGGER.warn("defined metabolite for nonexistent item " + location); + }); + this.metaboliteMap = builder.build(); + + LOGGER.debug("Finished parsing metabolites"); + if (this == reloadingInstance) { + currentInstance = this; + reloadingInstance = null; + } + } + + private static Consumer prefix(String pre) { + return s -> LOGGER.error(pre + s); + } +} diff --git a/src/main/java/lilypuree/metabolism/metabolism/FoodDataDuck.java b/src/main/java/lilypuree/metabolism/metabolism/FoodDataDuck.java new file mode 100644 index 0000000..5305d3d --- /dev/null +++ b/src/main/java/lilypuree/metabolism/metabolism/FoodDataDuck.java @@ -0,0 +1,8 @@ +package lilypuree.metabolism.metabolism; + + +import lilypuree.metabolism.metabolism.Metabolism; + +public interface FoodDataDuck { + Metabolism getMetabolism(); +} diff --git a/src/main/java/lilypuree/metabolism/metabolism/Metabolism.java b/src/main/java/lilypuree/metabolism/metabolism/Metabolism.java new file mode 100644 index 0000000..31f68e5 --- /dev/null +++ b/src/main/java/lilypuree/metabolism/metabolism/Metabolism.java @@ -0,0 +1,240 @@ +package lilypuree.metabolism.metabolism; + +import lilypuree.metabolism.config.Config; +import lilypuree.metabolism.data.Environment; +import lilypuree.metabolism.data.EnvironmentEffect; +import lilypuree.metabolism.data.Metabolite; +import lilypuree.metabolism.network.ClientSyncMessage; +import lilypuree.metabolism.network.Network; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.util.Mth; +import net.minecraft.world.Difficulty; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.level.GameRules; +import net.minecraftforge.network.NetworkDirection; + +public class Metabolism { + private static final double LN_10 = Math.log(10); + + //VARIABLES + private float maxWarmth; + private float maxFood; + private float warmth; + private float heat; + private float energy; + private float food; + + /** + * the amount of time it takes to reach approximately 90% of the given heat target, in ticks + * changing this will vary how fast effects will cause heat changes to the player + */ + private int adaptationTicks; + private float heatCoefficent; + + //SYNCING + private float lastSentWarmth; + private float lastSentHeat; + private float lastSentEnergy; + private float lastSentFood; + + //TICKING + private int warmthTick = 0; + private int damageTick = 0; + + public Metabolism() { + this.maxWarmth = MetabolismConstants.MAX_WARMTH; + this.maxFood = MetabolismConstants.MAX_CAPACITY; + this.warmth = maxWarmth; + this.heat = 0.0F; + this.food = MetabolismConstants.START_FOOD; + this.energy = MetabolismConstants.START_ENERGY; + this.setAdaptationTicks(MetabolismConstants.ADAPTATION_TICKS); + } + + public void tick(Player player) { + if (player.level().isClientSide) return; + warmthTick++; + damageTick++; + + if (warmthTick >= MetabolismConstants.WARMTH_TICK_COUNT) { + EnvironmentEffect.Combined effect = Environment.get().getCurrentEffect((ServerLevel) player.level(), player); + applyHeatTarget(effect.getCombinedHeatTarget()); + warm(effect.getCombinedWarmthEffect()); + + boolean regen = player.level().getGameRules().getBoolean(GameRules.RULE_NATURAL_REGENERATION); + if (regen && player.isHurt() && warmth > 0) { + player.heal(1.0F); + warmth = Math.max(0.0F, warmth - 1.0F); + } + warmthTick = 0; + } + + if (damageTick >= MetabolismConstants.DAMAGE_TICK_COUNT) { + if (canBeHurt(player)) + causeDamage(player); + damageTick = 0; + } + + boolean changed = warmth != lastSentWarmth || heat != lastSentHeat || food != lastSentFood || energy != lastSentEnergy; + if (changed && player instanceof ServerPlayer sPlayer) { + ClientSyncMessage msg = new ClientSyncMessage(heat, warmth, food, energy); + Network.channel.sendTo(msg, sPlayer.connection.connection, NetworkDirection.PLAY_TO_CLIENT); + lastSentWarmth = warmth; + lastSentHeat = heat; + lastSentFood = food; + lastSentEnergy = energy; + } + } + + private void causeDamage(Player player) { + if (heat > 0) { + if (energy > 0) + energy = Math.max(0.0F, energy - calculateDrain()); + else + player.hurt(player.damageSources().starve(), 1.0F); + + } else if (heat < 0) { + if (food > 0) + food = Math.max(0.0F, food - calculateDrain()); + else + player.hurt(player.damageSources().starve(), 1.0F); + } + + if (heat == maxWarmth) { + player.hurt(player.damageSources().inFire(), 1.0F); + } else if (heat == -maxWarmth) { + player.hurt(player.damageSources().freeze(), 1.0F); + } + } + + private void applyHeatTarget(float heatTarget) { + if (Math.abs(heat - heatTarget) < 1E-3) { + heat = heatTarget; + } else if (heat > 0) { + //HOT + heat = Mth.clamp(heat + heatChange(heatTarget, energy > 0), 0.0F, maxWarmth); + } else if (heat < 0) { + //COLD + heat = Mth.clamp(heat + heatChange(heatTarget, food > 0), -maxWarmth, 0.0F); + } else if (heatTarget != 0) { + //MILD + heat = Mth.clamp(heatChange(heatTarget, false), -maxWarmth, maxWarmth); + } + } + + private float heatChange(float heatTarget, boolean feedback) { + float diff = feedback ? heatTarget - heat : heatTarget; + if (Config.SERVER.preciseFeedback()) { + return diff * heatCoefficent + diff * heatCoefficent * heatCoefficent / 2; + } else { + return diff * heatCoefficent; + } + } + + + public void consumeFood(float amount) { + float consumed = Math.min(amount, food); + + this.food -= consumed; + this.energy += consumed; + } + + public void consumeEnergy(float amount) { + float consumed = Math.min(amount, energy); + + this.energy -= consumed; +// this.warm(consumed * MetabolismConstants.ENERGY_WARMTH_CONVERSION_RATE); + } + + public void eat(Metabolite metabolite) { + this.warm(metabolite.warmth()); + this.food += metabolite.food(); + this.energy += metabolite.energy(); + } + + public void eat(float foodLevel, float saturationLevelModifier) { + this.food += foodLevel; + } + + public void peacefulWarmth() { + if (heat > 0) + this.heat = Math.max(heat - 1, 0); + else if (heat < 0) + this.heat = Math.min(heat + 1, 0); + + this.warm(1.0F); + } + + public void warm(float amount) { + warmth = Mth.clamp(warmth + amount, 0.0F, maxWarmth - Mth.abs(heat)); + } + + public float getWarmth() { + return warmth; + } + + public float getHeat() { + return heat; + } + + public float getEnergy() { + return energy; + } + + public float getFood() { + return food; + } + + private float calculateDrain() { + return Mth.abs(heat) * MetabolismConstants.DRAIN_COEFFICIENT; + } + + private void setAdaptationTicks(int ticks) { + this.adaptationTicks = ticks; + this.heatCoefficent = (float) (LN_10 * MetabolismConstants.WARMTH_TICK_COUNT / adaptationTicks); + } + + + private boolean canBeHurt(Player player) { + Difficulty difficulty = player.level().getDifficulty(); + return player.getHealth() > 10.0F || difficulty == Difficulty.HARD || player.getHealth() > 1.0F && difficulty == Difficulty.NORMAL; + } + + public boolean needsFood() { + return food < maxFood; + } + + public CompoundTag writeNBT() { + CompoundTag nbt = new CompoundTag(); + nbt.putFloat("maxWarmth", maxWarmth); + nbt.putFloat("maxFood", maxFood); + nbt.putFloat("warmth", warmth); + nbt.putFloat("heat", heat); + nbt.putFloat("energy", energy); + nbt.putFloat("food", food); + return nbt; + } + + public void readNBT(CompoundTag nbt) { + maxWarmth = nbt.getFloat("maxWarmth"); + maxFood = nbt.getFloat("maxFood"); + warmth = nbt.getFloat("warmth"); + heat = nbt.getFloat("heat"); + energy = nbt.getFloat("energy"); + food = nbt.getFloat("food"); + } + + public void syncOnClient(ClientSyncMessage msg) { + heat = msg.heat; + warmth = msg.warmth; + energy = msg.energy; + food = msg.food; + } + + + public float getMaxWarmth() { + return maxWarmth; + } +} diff --git a/src/main/java/lilypuree/metabolism/metabolism/MetabolismConstants.java b/src/main/java/lilypuree/metabolism/metabolism/MetabolismConstants.java new file mode 100644 index 0000000..73db95e --- /dev/null +++ b/src/main/java/lilypuree/metabolism/metabolism/MetabolismConstants.java @@ -0,0 +1,26 @@ +package lilypuree.metabolism.metabolism; + +public class MetabolismConstants { + public static final int MAX_WARMTH = 20; + public static final float MAX_CAPACITY = 99; + public static final float START_ENERGY = 9.0F; + public static final float START_FOOD = 9.0F; + public static final int WARMTH_TICK_COUNT = 40; + public static final int DAMAGE_TICK_COUNT = 40; + public static final int ADAPTATION_TICKS = 1200; + public static final float DRAIN_COEFFICIENT = 0.05F; + + + public static final float ENERGY_SPRINT_JUMP = 0.1F; + public static final float FOOD_JUMP = 0.05F; + public static final float FOOD_DMG = 0.1F; + public static final float FOOD_ATK = 0.1F; + public static final float FOOD_MINE = 0.005F; + public static final float FOOD_WALK = 0.001F; //about 4 minutes walking = 1 food + public static final float FOOD_CROUCH = 0.002F; + public static final float FOOD_CLIMB = 0.002F; + public static final float ENERGY_SWIM = 0.05F; + public static final float ENERGY_SPRINT = 0.1F; //about 3 minutes sprinting = 100 energy + public static final float ENERGY_FLY = 0.03F; //about 1 minutes of flying down = 100 energy + public static final float EXHAUSTION_MULTIPLIER = 1.0F; //about 1 minute of gliding = +} diff --git a/src/main/java/lilypuree/metabolism/mixin/FoodDataMixin.java b/src/main/java/lilypuree/metabolism/mixin/FoodDataMixin.java new file mode 100644 index 0000000..6ade107 --- /dev/null +++ b/src/main/java/lilypuree/metabolism/mixin/FoodDataMixin.java @@ -0,0 +1,90 @@ +package lilypuree.metabolism.mixin; + +import lilypuree.metabolism.data.Metabolite; +import lilypuree.metabolism.data.Metabolites; +import lilypuree.metabolism.metabolism.FoodDataDuck; +import lilypuree.metabolism.metabolism.Metabolism; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.food.FoodData; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import static lilypuree.metabolism.metabolism.MetabolismConstants.EXHAUSTION_MULTIPLIER; + +@Mixin(FoodData.class) +public class FoodDataMixin implements FoodDataDuck { + + @Shadow + private int foodLevel; + + @Shadow + private int lastFoodLevel; + + @Unique + private final Metabolism metabolism = new Metabolism(); + + @Inject(method = "eat(IF)V", at = @At("HEAD"), cancellable = true) + public void eat(int foodLevelModifier, float saturationLevelModifier, CallbackInfo ci) { + metabolism.eat(foodLevelModifier, saturationLevelModifier); + ci.cancel(); + } + + @Inject(method = "eat(Lnet/minecraft/world/item/Item;Lnet/minecraft/world/item/ItemStack;Lnet/minecraft/world/entity/LivingEntity;)V", at = @At("HEAD"), cancellable = true, remap = false) + public void onEat(Item item, ItemStack stack, LivingEntity entity, CallbackInfo ci) { + if (item.isEdible() && Metabolites.getMetabolites().containsKey(item)) { + Metabolite metabolite = Metabolites.getMetabolites().get(item); + metabolism.eat(metabolite); + ci.cancel(); + } + } + + @Inject(method = "tick", at = @At("HEAD"), cancellable = true) + public void onTick(Player player, CallbackInfo ci) { + metabolism.tick(player); + this.lastFoodLevel = foodLevel; + this.foodLevel = metabolism.getEnergy() > 0 ? 10 : 1; + ci.cancel(); + } + + @Inject(method = "readAdditionalSaveData", at = @At("RETURN")) + public void onRead(CompoundTag tag, CallbackInfo ci) { + if (tag.contains("metabolism", CompoundTag.TAG_COMPOUND)) { + metabolism.readNBT(tag.getCompound("metabolism")); + } + } + + @Inject(method = "addAdditionalSaveData", at = @At("RETURN")) + public void onWrite(CompoundTag tag, CallbackInfo ci) { + tag.put("metabolism", metabolism.writeNBT()); + } + + @Inject(method = "addExhaustion", at = @At("HEAD")) + public void addExhaustion(float exhaustion, CallbackInfo ci) { + //handles all other exhaustion gains + metabolism.consumeFood(exhaustion * EXHAUSTION_MULTIPLIER); + } + + @Inject(method = "setFoodLevel", at = @At("HEAD"), cancellable = true) + public void onSetFood(CallbackInfo ci) { + ci.cancel(); + } + + @Inject(method = "needsFood", at = @At("HEAD"), cancellable = true) + public void onNeedsFood(CallbackInfoReturnable cir) { + cir.setReturnValue(metabolism.needsFood()); + } + + @Override + public Metabolism getMetabolism() { + return metabolism; + } +} diff --git a/src/main/java/lilypuree/metabolism/mixin/LocationCheckAccessor.java b/src/main/java/lilypuree/metabolism/mixin/LocationCheckAccessor.java new file mode 100644 index 0000000..03abdb0 --- /dev/null +++ b/src/main/java/lilypuree/metabolism/mixin/LocationCheckAccessor.java @@ -0,0 +1,12 @@ +package lilypuree.metabolism.mixin; + +import net.minecraft.advancements.critereon.LocationPredicate; +import net.minecraft.world.level.storage.loot.predicates.LocationCheck; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(LocationCheck.class) +public interface LocationCheckAccessor { + @Accessor + LocationPredicate getPredicate(); +} diff --git a/src/main/java/lilypuree/metabolism/mixin/PlayerMixin.java b/src/main/java/lilypuree/metabolism/mixin/PlayerMixin.java new file mode 100644 index 0000000..010cdd8 --- /dev/null +++ b/src/main/java/lilypuree/metabolism/mixin/PlayerMixin.java @@ -0,0 +1,153 @@ +package lilypuree.metabolism.mixin; + +import lilypuree.metabolism.metabolism.FoodDataDuck; +import lilypuree.metabolism.metabolism.Metabolism; +import net.minecraft.tags.FluidTags; +import net.minecraft.world.damagesource.DamageSource; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.player.Abilities; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.food.FoodData; +import net.minecraft.world.level.Level; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import static lilypuree.metabolism.metabolism.MetabolismConstants.*; + +@Mixin(value = Player.class) +public abstract class PlayerMixin extends LivingEntity { + @Shadow + public abstract boolean isSwimming(); + + @Shadow + @Final + private Abilities abilities; + + @Shadow + public abstract FoodData getFoodData(); + + @Unique + private boolean exhaustionHandled = false; + + protected PlayerMixin(EntityType pEntityType, Level pLevel) { + super(pEntityType, pLevel); + } + + @Unique + private void consumeFood(float amount) { + if (!this.abilities.invulnerable) { + if (!this.level().isClientSide) { + getMetabolism().consumeFood(amount); + } + } + } + + @Unique + private void consumeEnergy(float amount) { + if (!this.abilities.invulnerable) { + if (!this.level().isClientSide) { + getMetabolism().consumeEnergy(amount); + } + } + } + + @Unique + private Metabolism getMetabolism() { + return ((FoodDataDuck) getFoodData()).getMetabolism(); + } + + @Inject(method = "aiStep", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/food/FoodData;needsFood()Z")) + public void onAiStep(CallbackInfo ci) { + if (this.tickCount % 10 == 0) + getMetabolism().peacefulWarmth(); + } + + @Inject(method = "jumpFromGround", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/player/Player;causeFoodExhaustion(F)V")) + public void onJump(CallbackInfo ci) { + if (this.isSprinting()) { + consumeEnergy(ENERGY_SPRINT_JUMP); + } else { + consumeFood(FOOD_JUMP); + } + exhaustionHandled = true; + } + + @Inject(method = "actuallyHurt", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/player/Player;causeFoodExhaustion(F)V")) + public void onActuallyHurt(DamageSource dmgSrc, float dmgAmount, CallbackInfo ci) { + if (dmgSrc.getFoodExhaustion() > 0) { + consumeFood(FOOD_DMG); + } + exhaustionHandled = true; + } + + @Inject(method = "attack", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/player/Player;causeFoodExhaustion(F)V")) + public void onAttack(CallbackInfo ci) { + consumeFood(FOOD_ATK); //0.1F + exhaustionHandled = true; + } + + @Inject(method = "checkMovementStatistics", at = @At(value = "HEAD")) + public void onMovement(double x, double y, double z, CallbackInfo ci) { + if (this.isPassenger()) return; + + if (this.isSwimming()) { + int dist = Math.round((float) Math.sqrt(x * x + y * y + z * z) * 100.0F); + if (dist > 0) { + consumeEnergy(dist * 0.01F * ENERGY_SWIM); + exhaustionHandled = true; + } + } else if (this.isEyeInFluid(FluidTags.WATER)) { + int dist = Math.round((float) Math.sqrt(x * x + y * y + z * z) * 100.0F); + if (dist > 0) { + consumeFood(dist * 0.01F * ENERGY_SWIM); + exhaustionHandled = true; + } + } else if (this.isInWater()) { + float dist = Math.round((float) Math.sqrt(x * x + z * z) * 100.0F); + if (dist > 0) { + consumeFood(dist * 0.01F * FOOD_WALK); + exhaustionHandled = true; + } + } else if (this.onClimbable()) { + if (y > 0) { + consumeFood((float) y * FOOD_CLIMB); + } + } else if (this.onGround()) { + int dist = Math.round((float) Math.sqrt(x * x + z * z) * 100.0F); + if (dist > 0) { + if (this.isSprinting()) { + consumeEnergy(dist * 0.01F * ENERGY_SPRINT); + } else if (this.isCrouching()) { + consumeFood(dist * 0.01F * FOOD_CROUCH); + } else { + consumeFood(dist * 0.01F * FOOD_WALK); + } + exhaustionHandled = true; + } + } else if (this.isFallFlying()) { + float dist = Math.round(Math.sqrt(x * x + y * y + z * z)); + consumeEnergy(dist * ENERGY_FLY); + } + } + + @Inject(method = "causeFoodExhaustion", at = @At(value = "HEAD"), cancellable = true) + public void onCauseFoodExhaustion(CallbackInfo ci) { + if (exhaustionHandled) { + ci.cancel(); + exhaustionHandled = false; + } + } + + @Inject(method = "tryToStartFallFlying", at = @At("HEAD"), cancellable = true) + public void onTryToStartFallFlying(CallbackInfoReturnable cir) { + if (getMetabolism().getEnergy() <= 0) + cir.setReturnValue(false); + } +} diff --git a/src/main/java/lilypuree/metabolism/network/ClientSyncMessage.java b/src/main/java/lilypuree/metabolism/network/ClientSyncMessage.java new file mode 100644 index 0000000..108377c --- /dev/null +++ b/src/main/java/lilypuree/metabolism/network/ClientSyncMessage.java @@ -0,0 +1,35 @@ +package lilypuree.metabolism.network; + +import net.minecraft.network.FriendlyByteBuf; + +public class ClientSyncMessage { + public float heat; + public float warmth; + public float food; + public float energy; + + public ClientSyncMessage(){} + + public ClientSyncMessage(float heat, float warmth, float food, float energy) { + this.heat = heat; + this.warmth = warmth; + this.food = food; + this.energy = energy; + } + + public static ClientSyncMessage fromBytes(FriendlyByteBuf buf){ + ClientSyncMessage msg = new ClientSyncMessage(); + msg.heat = buf.readFloat(); + msg.warmth = buf.readFloat(); + msg.food = buf.readFloat(); + msg.energy = buf.readFloat(); + return msg; + } + + public void toBytes(FriendlyByteBuf buf){ + buf.writeFloat(heat); + buf.writeFloat(warmth); + buf.writeFloat(food); + buf.writeFloat(energy); + } +} diff --git a/src/main/java/lilypuree/metabolism/network/MetabolitesPacket.java b/src/main/java/lilypuree/metabolism/network/MetabolitesPacket.java new file mode 100644 index 0000000..d5bfbdd --- /dev/null +++ b/src/main/java/lilypuree/metabolism/network/MetabolitesPacket.java @@ -0,0 +1,42 @@ +package lilypuree.metabolism.network; + +import com.google.common.collect.ImmutableMap; +import lilypuree.metabolism.client.ClientMetabolites; +import lilypuree.metabolism.data.Environment; +import lilypuree.metabolism.data.Metabolite; +import lilypuree.metabolism.data.Metabolites; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.Item; +import net.minecraftforge.network.NetworkEvent; +import net.minecraftforge.registries.ForgeRegistries; + +import java.util.Map; +import java.util.function.Supplier; + +public record MetabolitesPacket(Map metaboliteMap) { + public void encode(FriendlyByteBuf buffer) { + buffer.writeInt(metaboliteMap.size()); + metaboliteMap.forEach((item, metabolite) -> { + buffer.writeResourceLocation(ForgeRegistries.ITEMS.getKey(item)); + buffer.writeJsonWithCodec(Metabolite.CODEC, metabolite); + }); + } + + public static MetabolitesPacket decode(FriendlyByteBuf buffer) { + int size = buffer.readInt(); + if (size > 0) { + ImmutableMap.Builder builder = new ImmutableMap.Builder<>(); + for (int i = 0; i < size; i++) { + ResourceLocation key = buffer.readResourceLocation(); + Metabolite metabolite = buffer.readJsonWithCodec(Metabolite.CODEC); + builder.put(ForgeRegistries.ITEMS.getValue(key), metabolite); + } + return new MetabolitesPacket(builder.build()); + } else return new MetabolitesPacket(ImmutableMap.of()); + } + + public static void handle(MetabolitesPacket packet, Supplier ctx) { + ClientMetabolites.setClientMetabolites(packet.metaboliteMap); + } +} diff --git a/src/main/java/lilypuree/metabolism/network/Network.java b/src/main/java/lilypuree/metabolism/network/Network.java new file mode 100644 index 0000000..9ddd3e9 --- /dev/null +++ b/src/main/java/lilypuree/metabolism/network/Network.java @@ -0,0 +1,31 @@ +package lilypuree.metabolism.network; + +import lilypuree.metabolism.MetabolismMod; +import lilypuree.metabolism.client.ClientHandler; +import net.minecraft.resources.ResourceLocation; +import net.minecraftforge.network.NetworkRegistry; +import net.minecraftforge.network.simple.SimpleChannel; + +public class Network { + private static final ResourceLocation NAME = MetabolismMod.getID("network"); + private static final String PROTOCOL_VERSION = "1"; + public static SimpleChannel channel = NetworkRegistry.newSimpleChannel( + NAME, () -> PROTOCOL_VERSION, PROTOCOL_VERSION::equals, PROTOCOL_VERSION::equals + ); + + public static void init() { + int id = 1; + channel.messageBuilder(MetabolitesPacket.class, id++) + .decoder(MetabolitesPacket::decode) + .encoder(MetabolitesPacket::encode) + .consumerMainThread(MetabolitesPacket::handle) + .add(); + + channel.messageBuilder(ClientSyncMessage.class, id++) + .decoder(ClientSyncMessage::fromBytes) + .encoder(ClientSyncMessage::toBytes) + .consumerMainThread(ClientHandler::handleSyncMessage) + .add(); + + } +} diff --git a/src/main/java/lilypuree/metabolism/util/AdvancedWeatherCheck.java b/src/main/java/lilypuree/metabolism/util/AdvancedWeatherCheck.java new file mode 100644 index 0000000..193d777 --- /dev/null +++ b/src/main/java/lilypuree/metabolism/util/AdvancedWeatherCheck.java @@ -0,0 +1,93 @@ +package lilypuree.metabolism.util; + +import com.google.common.collect.ImmutableSet; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonObject; +import com.google.gson.JsonSerializationContext; +import lilypuree.metabolism.MetabolismTags; +import lilypuree.metabolism.Registration; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Holder; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.util.GsonHelper; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.levelgen.Heightmap; +import net.minecraft.world.level.storage.loot.LootContext; +import net.minecraft.world.level.storage.loot.parameters.LootContextParam; +import net.minecraft.world.level.storage.loot.parameters.LootContextParams; +import net.minecraft.world.level.storage.loot.predicates.LootItemCondition; +import net.minecraft.world.level.storage.loot.predicates.LootItemConditionType; +import net.minecraft.world.phys.Vec3; + +import java.util.Set; + +public class AdvancedWeatherCheck implements LootItemCondition { + + final WeatherType weatherType; + + public AdvancedWeatherCheck(WeatherType weatherType) { + this.weatherType = weatherType; + } + + @Override + public LootItemConditionType getType() { + return Registration.ADVANCED_WEATHER_CHECK.get(); + } + + @Override + public Set> getReferencedContextParams() { + return ImmutableSet.of(LootContextParams.ORIGIN); + } + + @Override + public boolean test(LootContext context) { + ServerLevel level = context.getLevel(); + Vec3 origin = context.getParam(LootContextParams.ORIGIN); + BlockPos pos = BlockPos.containing(origin.x, origin.y, origin.z); + if (canHaveWeather(level, pos)) { + Holder biome = level.getBiome(pos); + Biome.Precipitation precipitation = biome.value().getPrecipitationAt(pos); + switch (weatherType) { + case SUNNY -> { + return level.isDay(); + } + case RAIN -> { + return level.isRaining() && precipitation == Biome.Precipitation.RAIN; + } + case SNOW -> { + return level.isRaining() && precipitation == Biome.Precipitation.SNOW; + } + case HEATWAVE -> { + return level.isDay() && level.getBiome(pos).is(MetabolismTags.HOT_BIOMES); + } + case BLIZZARD -> { + } + } + } + return false; + } + + public boolean canHaveWeather(ServerLevel level, BlockPos pPos) { + if (!level.canSeeSky(pPos)) { + return false; + } else if (level.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING, pPos).getY() > pPos.getY()) { + return false; + } + return true; + } + + + public static class Serializer implements net.minecraft.world.level.storage.loot.Serializer { + public void serialize(JsonObject json, AdvancedWeatherCheck value, JsonSerializationContext context) { + json.addProperty("weather_type", value.weatherType.name()); + } + + /** + * Deserialize a value by reading it from the JsonObject. + */ + public AdvancedWeatherCheck deserialize(JsonObject json, JsonDeserializationContext context) { + String type = json.has("weather_type") ? GsonHelper.getAsString(json, "weather_type") : null; + return new AdvancedWeatherCheck(WeatherType.fromString(type)); + } + } +} diff --git a/src/main/java/lilypuree/metabolism/util/Anchor.java b/src/main/java/lilypuree/metabolism/util/Anchor.java new file mode 100644 index 0000000..e8a5d17 --- /dev/null +++ b/src/main/java/lilypuree/metabolism/util/Anchor.java @@ -0,0 +1,84 @@ +package lilypuree.metabolism.util; + + +public enum Anchor { + TOP_LEFT(Anchor.Horizontal.LEFT, Anchor.Vertical.TOP), + TOP_CENTER(Anchor.Horizontal.CENTER, Anchor.Vertical.TOP), + TOP_RIGHT(Anchor.Horizontal.RIGHT, Anchor.Vertical.TOP), + CENTER_LEFT(Anchor.Horizontal.LEFT, Anchor.Vertical.CENTER), + CENTER(Anchor.Horizontal.CENTER, Anchor.Vertical.CENTER), + CENTER_RIGHT(Anchor.Horizontal.RIGHT, Anchor.Vertical.CENTER), + BOTTOM_LEFT(Anchor.Horizontal.LEFT, Anchor.Vertical.BOTTOM), + BOTTOM_CENTER(Anchor.Horizontal.CENTER, Anchor.Vertical.BOTTOM), + BOTTOM_RIGHT(Anchor.Horizontal.RIGHT, Anchor.Vertical.BOTTOM); + + private final Horizontal horizontal; + private final Vertical vertical; + + private Anchor(Horizontal horizontal, Vertical vertical) { + this.horizontal = horizontal; + this.vertical = vertical; + } + + public int getX(int scaledScreenWidth, int elementWidth, int margin) { + return this.horizontal.getX(scaledScreenWidth, elementWidth, margin); + } + + public int getY(int scaledScreenHeight, int elementHeight, int margin) { + return this.vertical.getY(scaledScreenHeight, elementHeight, margin); + } + + public Horizontal getHorizontal() { + return this.horizontal; + } + + public Vertical getVertical() { + return this.vertical; + } + + public static enum Vertical { + TOP { + public int getY(int scaledScreenHeight, int elementHeight, int margin) { + return margin; + } + }, + CENTER { + public int getY(int scaledScreenHeight, int elementHeight, int margin) { + return (scaledScreenHeight - elementHeight) / 2; + } + }, + BOTTOM { + public int getY(int scaledScreenHeight, int elementHeight, int margin) { + return scaledScreenHeight - elementHeight - margin; + } + }; + + private Vertical() { + } + + public abstract int getY(int var1, int var2, int var3); + } + + public static enum Horizontal { + LEFT { + public int getX(int scaledScreenWidth, int elementWidth, int margin) { + return margin; + } + }, + CENTER { + public int getX(int scaledScreenWidth, int elementWidth, int margin) { + return (scaledScreenWidth - elementWidth) / 2; + } + }, + RIGHT { + public int getX(int scaledScreenWidth, int elementWidth, int margin) { + return scaledScreenWidth - elementWidth - margin; + } + }; + + private Horizontal() { + } + + public abstract int getX(int var1, int var2, int var3); + } +} diff --git a/src/main/java/lilypuree/metabolism/util/WeatherType.java b/src/main/java/lilypuree/metabolism/util/WeatherType.java new file mode 100644 index 0000000..162fce6 --- /dev/null +++ b/src/main/java/lilypuree/metabolism/util/WeatherType.java @@ -0,0 +1,33 @@ +package lilypuree.metabolism.util; + +import lilypuree.metabolism.MetabolismMod; + +import java.util.Arrays; +import java.util.Optional; + +public enum WeatherType { + SUNNY("sunny"), RAIN("rain"), SNOW("snow"), HEATWAVE("heatwave"), BLIZZARD("blizzard"); + + private String name; + + WeatherType(String name) { + this.name = name; + } + + @Override + public String toString() { + return name; + } + + public static WeatherType fromString(String name) { + if (name != null) { + Optional weatherType = Arrays.stream(values()).filter(type -> type.name.equals(name)).findAny(); + if (weatherType.isEmpty()) { + MetabolismMod.LOGGER.warn("invalid weather type " + name); + return null; + } else + return weatherType.get(); + } + return null; + } +} diff --git a/src/main/resources/META-INF/mods.toml b/src/main/resources/META-INF/mods.toml index b0ad9f8..30e45b7 100644 --- a/src/main/resources/META-INF/mods.toml +++ b/src/main/resources/META-INF/mods.toml @@ -1,64 +1,28 @@ -# This is an example mods.toml file. It contains the data relating to the loading mods. -# There are several mandatory fields (#mandatory), and many more that are optional (#optional). -# The overall format is standard TOML format, v0.5.0. -# Note that there are a couple of TOML lists in this file. -# Find more information on toml format here: https://github.com/toml-lang/toml -# The name of the mod loader type to load - for regular FML @Mod mods it should be javafml modLoader="javafml" #mandatory -# A version range to match for said mod loader - for regular FML @Mod it will be the the FML version. This is currently 47. loaderVersion="${loader_version_range}" #mandatory -# The license for you mod. This is mandatory metadata and allows for easier comprehension of your redistributive properties. -# Review your options at https://choosealicense.com/. All rights reserved is the default copyright stance, and is thus the default here. license="${mod_license}" -# A URL to refer people to when problems occur with this mod -#issueTrackerURL="https://change.me.to.your.issue.tracker.example.invalid/" #optional -# A list of mods - how many allowed here is determined by the individual mod loader + [[mods]] #mandatory -# The modid of the mod modId="${mod_id}" #mandatory -# The version number of the mod version="${mod_version}" #mandatory -# A display name for the mod displayName="${mod_name}" #mandatory -# A URL to query for updates for this mod. See the JSON update specification https://docs.neoforged.net/docs/misc/updatechecker/ -#updateJSONURL="https://change.me.example.invalid/updates.json" #optional -# A URL for the "homepage" for this mod, displayed in the mod UI -#displayURL="https://change.me.to.your.mods.homepage.example.invalid/" #optional + # A file name (in the root of the mod JAR) containing a logo for display -#logoFile="examplemod.png" #optional -# A text field displayed in the mod UI -#credits="" #optional -# A text field displayed in the mod UI +logoFile="logo.png" #optional + authors="${mod_authors}" #optional -# Display Test controls the display for your mod in the server connection screen -# MATCH_VERSION means that your mod will cause a red X if the versions on client and server differ. This is the default behaviour and should be what you choose if you have server and client elements to your mod. -# IGNORE_SERVER_VERSION means that your mod will not cause a red X if it's present on the server but not on the client. This is what you should use if you're a server only mod. -# IGNORE_ALL_VERSION means that your mod will not cause a red X if it's present on the client or the server. This is a special case and should only be used if your mod has no server component. -# NONE means that no display test is set on your mod. You need to do this yourself, see IExtensionPoint.DisplayTest for more information. You can define any scheme you wish with this value. -# IMPORTANT NOTE: this is NOT an instruction as to which environments (CLIENT or DEDICATED SERVER) your mod loads on. Your mod should load (and maybe do nothing!) whereever it finds itself. -#displayTest="MATCH_VERSION" # MATCH_VERSION is the default if nothing is specified (#optional) -# The description text for the mod (multi line!) (#mandatory) description='''${mod_description}''' -# A dependency - use the . to indicate dependency for a specific modid. Dependencies are optional. [[dependencies.${mod_id}]] #optional - # the modid of the dependency - modId="neoforge" #mandatory - # Does this dependency have to exist - if not, ordering below must be specified + modId="forge" #mandatory mandatory=true #mandatory - # The version range of the dependency - versionRange="${neo_version_range}" #mandatory - # An ordering relationship for the dependency - BEFORE or AFTER required if the dependency is not mandatory - # BEFORE - This mod is loaded BEFORE the dependency - # AFTER - This mod is loaded AFTER the dependency + versionRange="${forge_version_range}" #mandatory ordering="NONE" - # Side this dependency is applied on - BOTH, CLIENT, or SERVER side="BOTH" # Here's another dependency [[dependencies.${mod_id}]] modId="minecraft" mandatory=true - # This version range declares a minimum of the current minecraft version up to but not including the next major version versionRange="${minecraft_version_range}" ordering="NONE" side="BOTH" diff --git a/src/main/resources/assets/metabolism/lang/en_us.json b/src/main/resources/assets/metabolism/lang/en_us.json new file mode 100644 index 0000000..e69de29 diff --git a/src/main/resources/assets/metabolism/textures/gui/hud.png b/src/main/resources/assets/metabolism/textures/gui/hud.png new file mode 100644 index 0000000..43e19fb Binary files /dev/null and b/src/main/resources/assets/metabolism/textures/gui/hud.png differ diff --git a/src/main/resources/data/metabolism/environment_effects/campfire.json b/src/main/resources/data/metabolism/environment_effects/campfire.json new file mode 100644 index 0000000..c51454f --- /dev/null +++ b/src/main/resources/data/metabolism/environment_effects/campfire.json @@ -0,0 +1,17 @@ +{ + "conditions": { + "condition": "minecraft:location_check", + "predicate": { + "block": { + "blocks": [ + "minecraft:campfire" + ], + "state": { + "lit": "true" + } + } + } + }, + "warmth_effect": 1.0, + "range": 4.0 +} \ No newline at end of file diff --git a/src/main/resources/data/metabolism/environment_effects/dark.json b/src/main/resources/data/metabolism/environment_effects/dark.json new file mode 100644 index 0000000..8dfff21 --- /dev/null +++ b/src/main/resources/data/metabolism/environment_effects/dark.json @@ -0,0 +1,17 @@ +{ + "conditions": [ + { + "condition": "minecraft:location_check", + "predicate": { + "dimension": "minecraft:overworld", + "light": { + "light": { + "max": 0 + } + } + } + } + ], + "is_additive": true, + "heat_target": -4.0 +} \ No newline at end of file diff --git a/src/main/resources/data/metabolism/environment_effects/deep.json b/src/main/resources/data/metabolism/environment_effects/deep.json new file mode 100644 index 0000000..766b314 --- /dev/null +++ b/src/main/resources/data/metabolism/environment_effects/deep.json @@ -0,0 +1,17 @@ +{ + "conditions": [ + { + "condition": "minecraft:location_check", + "predicate": { + "dimension": "minecraft:overworld", + "position": { + "y": { + "max" : 0, + "min" : -25 + } + } + } + } + ], + "heat_target": 4.0 +} \ No newline at end of file diff --git a/src/main/resources/data/metabolism/environment_effects/deeper.json b/src/main/resources/data/metabolism/environment_effects/deeper.json new file mode 100644 index 0000000..9c018c7 --- /dev/null +++ b/src/main/resources/data/metabolism/environment_effects/deeper.json @@ -0,0 +1,16 @@ +{ + "conditions": [ + { + "condition": "minecraft:location_check", + "predicate": { + "dimension": "minecraft:overworld", + "position": { + "y": { + "max" : -25 + } + } + } + } + ], + "heat_target": 8.0 +} \ No newline at end of file diff --git a/src/main/resources/data/metabolism/environment_effects/has_gold_armor.json b/src/main/resources/data/metabolism/environment_effects/has_gold_armor.json new file mode 100644 index 0000000..c9b2193 --- /dev/null +++ b/src/main/resources/data/metabolism/environment_effects/has_gold_armor.json @@ -0,0 +1,21 @@ +{ + "conditions": { + "condition": "minecraft:entity_properties", + "entity": "this", + "predicate": { + "equipment": { + "head": { + "items": [ + "minecraft:gold_chestplate", + "minecraft:gold_leggings", + "minecraft:gold_boots", + "minecraft:gold_helmet" + ] + } + } + } + }, + "is_resistance": true, + "is_additive": true, + "heat_target": -3.0 +} \ No newline at end of file diff --git a/src/main/resources/data/metabolism/environment_effects/has_leather_armor.json b/src/main/resources/data/metabolism/environment_effects/has_leather_armor.json new file mode 100644 index 0000000..dc99904 --- /dev/null +++ b/src/main/resources/data/metabolism/environment_effects/has_leather_armor.json @@ -0,0 +1,21 @@ +{ + "conditions": { + "condition": "minecraft:entity_properties", + "entity": "this", + "predicate": { + "equipment": { + "head": { + "items": [ + "minecraft:leather_chestplate", + "minecraft:leather_leggings", + "minecraft:leather_boots", + "minecraft:leather_helmet" + ] + } + } + } + }, + "is_resistance": true, + "is_additive": true, + "heat_target": 3.0 +} \ No newline at end of file diff --git a/src/main/resources/data/metabolism/environment_effects/has_netherite_armor.json b/src/main/resources/data/metabolism/environment_effects/has_netherite_armor.json new file mode 100644 index 0000000..fdbcc06 --- /dev/null +++ b/src/main/resources/data/metabolism/environment_effects/has_netherite_armor.json @@ -0,0 +1,21 @@ +{ + "conditions": { + "condition": "minecraft:entity_properties", + "entity": "this", + "predicate": { + "equipment": { + "head": { + "items": [ + "minecraft:netherite_chestplate", + "minecraft:netherite_leggings", + "minecraft:netherite_boots", + "minecraft:netherite_helmet" + ] + } + } + } + }, + "is_resistance": true, + "is_additive": true, + "heat_target": -6.0 +} \ No newline at end of file diff --git a/src/main/resources/data/metabolism/environment_effects/high.json b/src/main/resources/data/metabolism/environment_effects/high.json new file mode 100644 index 0000000..b14976d --- /dev/null +++ b/src/main/resources/data/metabolism/environment_effects/high.json @@ -0,0 +1,17 @@ +{ + "conditions": [ + { + "condition": "minecraft:location_check", + "predicate": { + "dimension": "minecraft:overworld", + "position": { + "y": { + "max" : 125, + "min" : 100 + } + } + } + } + ], + "heat_target": -4.0 +} \ No newline at end of file diff --git a/src/main/resources/data/metabolism/environment_effects/higher.json b/src/main/resources/data/metabolism/environment_effects/higher.json new file mode 100644 index 0000000..de9fa47 --- /dev/null +++ b/src/main/resources/data/metabolism/environment_effects/higher.json @@ -0,0 +1,17 @@ +{ + "conditions": [ + { + "condition": "minecraft:location_check", + "predicate": { + "dimension": "minecraft:overworld", + "position": { + "y": { + "max" : 149, + "min" : 125 + } + } + } + } + ], + "heat_target": -8.0 +} \ No newline at end of file diff --git a/src/main/resources/data/metabolism/environment_effects/highest.json b/src/main/resources/data/metabolism/environment_effects/highest.json new file mode 100644 index 0000000..34ecb86 --- /dev/null +++ b/src/main/resources/data/metabolism/environment_effects/highest.json @@ -0,0 +1,16 @@ +{ + "conditions": [ + { + "condition": "minecraft:location_check", + "predicate": { + "dimension": "minecraft:overworld", + "position": { + "y": { + "min" : 150 + } + } + } + } + ], + "heat_target": -12.0 +} \ No newline at end of file diff --git a/src/main/resources/data/metabolism/environment_effects/in_cold_block.json b/src/main/resources/data/metabolism/environment_effects/in_cold_block.json new file mode 100644 index 0000000..63bc451 --- /dev/null +++ b/src/main/resources/data/metabolism/environment_effects/in_cold_block.json @@ -0,0 +1,20 @@ +{ + "conditions": [ + { + "condition": "minecraft:entity_properties", + "entity": "this", + "predicate": { + "location": { + "block": { + "blocks": [ + "minecraft:powder_snow", + "minecraft:powder_snow_cauldron" + ] + } + } + } + } + ], + "is_additive": true, + "heat_target": -8.0 +} \ No newline at end of file diff --git a/src/main/resources/data/metabolism/environment_effects/in_deep_dark.json b/src/main/resources/data/metabolism/environment_effects/in_deep_dark.json new file mode 100644 index 0000000..b7d82c4 --- /dev/null +++ b/src/main/resources/data/metabolism/environment_effects/in_deep_dark.json @@ -0,0 +1,11 @@ +{ + "conditions": [ + { + "condition": "minecraft:location_check", + "predicate": { + "biome": "minecraft:the_deep_dark" + } + } + ], + "heat_target": -20.0 +} \ No newline at end of file diff --git a/src/main/resources/data/metabolism/environment_effects/in_end.json b/src/main/resources/data/metabolism/environment_effects/in_end.json new file mode 100644 index 0000000..108b9f5 --- /dev/null +++ b/src/main/resources/data/metabolism/environment_effects/in_end.json @@ -0,0 +1,11 @@ +{ + "conditions": [ + { + "condition": "minecraft:location_check", + "predicate": { + "dimension": "minecraft:the_end" + } + } + ], + "heat_target": -10.0 +} \ No newline at end of file diff --git a/src/main/resources/data/metabolism/environment_effects/in_nether.json b/src/main/resources/data/metabolism/environment_effects/in_nether.json new file mode 100644 index 0000000..fe1e3ef --- /dev/null +++ b/src/main/resources/data/metabolism/environment_effects/in_nether.json @@ -0,0 +1,11 @@ +{ + "conditions": [ + { + "condition": "minecraft:location_check", + "predicate": { + "dimension": "minecraft:the_nether" + } + } + ], + "heat_target": 12.0 +} \ No newline at end of file diff --git a/src/main/resources/data/metabolism/environment_effects/in_water.json b/src/main/resources/data/metabolism/environment_effects/in_water.json new file mode 100644 index 0000000..e576845 --- /dev/null +++ b/src/main/resources/data/metabolism/environment_effects/in_water.json @@ -0,0 +1,17 @@ +{ + "conditions": [ + { + "condition": "minecraft:entity_properties", + "entity": "this", + "predicate": { + "location": { + "fluid": { + "tag": "minecraft:water" + } + } + } + } + ], + "is_additive": true, + "heat_target": -4.0 +} \ No newline at end of file diff --git a/src/main/resources/data/metabolism/environment_effects/on_cold_block.json b/src/main/resources/data/metabolism/environment_effects/on_cold_block.json new file mode 100644 index 0000000..aa7be59 --- /dev/null +++ b/src/main/resources/data/metabolism/environment_effects/on_cold_block.json @@ -0,0 +1,33 @@ +{ + "conditions": [ + { + "condition" : "minecraft:any_of", + "terms" : [ + { + "condition": "minecraft:entity_properties", + "entity": "this", + "predicate": { + "stepping_on": { + "block": { + "tag" : "minecraft:ice" + } + } + } + }, + { + "condition": "minecraft:entity_properties", + "entity": "this", + "predicate": { + "stepping_on": { + "block": { + "tag" : "minecraft:snow" + } + } + } + } + ] + } + ], + "is_additive" : true, + "heat_target": -4.0 +} \ No newline at end of file diff --git a/src/main/resources/data/metabolism/environment_effects/on_hot_block.json b/src/main/resources/data/metabolism/environment_effects/on_hot_block.json new file mode 100644 index 0000000..371f772 --- /dev/null +++ b/src/main/resources/data/metabolism/environment_effects/on_hot_block.json @@ -0,0 +1,20 @@ +{ + "conditions": [ + { + "condition": "minecraft:entity_properties", + "entity": "this", + "predicate": { + "stepping_on": { + "block": { + "blocks": [ + "minecraft:magma_block", + "minecraft:campfire" + ] + } + } + } + } + ], + "is_additive" : true, + "heat_target": 4.0 +} \ No newline at end of file diff --git a/src/main/resources/data/metabolism/environment_effects/weather_heatwave.json b/src/main/resources/data/metabolism/environment_effects/weather_heatwave.json new file mode 100644 index 0000000..3c5cc27 --- /dev/null +++ b/src/main/resources/data/metabolism/environment_effects/weather_heatwave.json @@ -0,0 +1,9 @@ +{ + "conditions": [ + { + "condition": "metabolism:advanced_weather_check", + "weather_type": "heatwave" + } + ], + "heat_target": 8.0 +} diff --git a/src/main/resources/data/metabolism/environment_effects/weather_snow.json b/src/main/resources/data/metabolism/environment_effects/weather_snow.json new file mode 100644 index 0000000..02b37a2 --- /dev/null +++ b/src/main/resources/data/metabolism/environment_effects/weather_snow.json @@ -0,0 +1,10 @@ +{ + "conditions": [ + { + "condition": "metabolism:advanced_weather_check", + "weather_type": "snow" + } + ], + "is_additive": true, + "heat_target": -8.0 +} diff --git a/src/main/resources/data/metabolism/environment_effects/weather_sunny.json b/src/main/resources/data/metabolism/environment_effects/weather_sunny.json new file mode 100644 index 0000000..6cd5b40 --- /dev/null +++ b/src/main/resources/data/metabolism/environment_effects/weather_sunny.json @@ -0,0 +1,10 @@ +{ + "conditions": [ + { + "condition": "metabolism:advanced_weather_check", + "weather_type": "sunny" + } + ], + "is_additive": true, + "warmth_effect": 1.0 +} diff --git a/src/main/resources/data/metabolism/tags/worldgen/biome/hot_biomes.json b/src/main/resources/data/metabolism/tags/worldgen/biome/hot_biomes.json new file mode 100644 index 0000000..1058dec --- /dev/null +++ b/src/main/resources/data/metabolism/tags/worldgen/biome/hot_biomes.json @@ -0,0 +1,8 @@ +{ + "values" : [ + "minecraft:desert", + "minecraft:badlands", + "minecraft:eroded_badlands", + "#minecraft:is_jungle" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/minecraft/metabolites/apple.json b/src/main/resources/data/minecraft/metabolites/apple.json new file mode 100644 index 0000000..c4df67f --- /dev/null +++ b/src/main/resources/data/minecraft/metabolites/apple.json @@ -0,0 +1,5 @@ +{ + "warmth": 2.0, + "food": 4.0, + "energy": 6.0 +} \ No newline at end of file diff --git a/src/main/resources/data/minecraft/metabolites/baked_potato.json b/src/main/resources/data/minecraft/metabolites/baked_potato.json new file mode 100644 index 0000000..899d6de --- /dev/null +++ b/src/main/resources/data/minecraft/metabolites/baked_potato.json @@ -0,0 +1,5 @@ +{ + "warmth": 5.0, + "food": 8.0, + "energy": 8.0 +} \ No newline at end of file diff --git a/src/main/resources/data/minecraft/metabolites/beetroot.json b/src/main/resources/data/minecraft/metabolites/beetroot.json new file mode 100644 index 0000000..3a3f480 --- /dev/null +++ b/src/main/resources/data/minecraft/metabolites/beetroot.json @@ -0,0 +1,5 @@ +{ + "warmth": 1.0, + "food": 1.0, + "energy": 3.0 +} \ No newline at end of file diff --git a/src/main/resources/data/minecraft/metabolites/beetroot_soup.json b/src/main/resources/data/minecraft/metabolites/beetroot_soup.json new file mode 100644 index 0000000..de8e539 --- /dev/null +++ b/src/main/resources/data/minecraft/metabolites/beetroot_soup.json @@ -0,0 +1,5 @@ +{ + "warmth": 7.0, + "food": 6.0, + "energy": 14.0 +} \ No newline at end of file diff --git a/src/main/resources/data/minecraft/metabolites/bread.json b/src/main/resources/data/minecraft/metabolites/bread.json new file mode 100644 index 0000000..899d6de --- /dev/null +++ b/src/main/resources/data/minecraft/metabolites/bread.json @@ -0,0 +1,5 @@ +{ + "warmth": 5.0, + "food": 8.0, + "energy": 8.0 +} \ No newline at end of file diff --git a/src/main/resources/data/minecraft/metabolites/carrot.json b/src/main/resources/data/minecraft/metabolites/carrot.json new file mode 100644 index 0000000..89030a6 --- /dev/null +++ b/src/main/resources/data/minecraft/metabolites/carrot.json @@ -0,0 +1,5 @@ +{ + "warmth": 3.0, + "food": 4.0, + "energy": 6.0 +} \ No newline at end of file diff --git a/src/main/resources/data/minecraft/metabolites/chorus_fruit.json b/src/main/resources/data/minecraft/metabolites/chorus_fruit.json new file mode 100644 index 0000000..46d5f89 --- /dev/null +++ b/src/main/resources/data/minecraft/metabolites/chorus_fruit.json @@ -0,0 +1,5 @@ +{ + "warmth": 1.0, + "food": 2.0, + "energy": 2.0 +} \ No newline at end of file diff --git a/src/main/resources/data/minecraft/metabolites/cooked_chicken.json b/src/main/resources/data/minecraft/metabolites/cooked_chicken.json new file mode 100644 index 0000000..bb431b5 --- /dev/null +++ b/src/main/resources/data/minecraft/metabolites/cooked_chicken.json @@ -0,0 +1,5 @@ +{ + "warmth": 7.0, + "food": 15.0, + "energy": 2.0 +} \ No newline at end of file diff --git a/src/main/resources/data/minecraft/metabolites/cooked_cod.json b/src/main/resources/data/minecraft/metabolites/cooked_cod.json new file mode 100644 index 0000000..3771c90 --- /dev/null +++ b/src/main/resources/data/minecraft/metabolites/cooked_cod.json @@ -0,0 +1,5 @@ +{ + "warmth": 6.0, + "food": 12.0, + "energy": 2.0 +} \ No newline at end of file diff --git a/src/main/resources/data/minecraft/metabolites/cooked_mutton.json b/src/main/resources/data/minecraft/metabolites/cooked_mutton.json new file mode 100644 index 0000000..a7f1b25 --- /dev/null +++ b/src/main/resources/data/minecraft/metabolites/cooked_mutton.json @@ -0,0 +1,5 @@ +{ + "warmth": 8.0, + "food": 15.0, + "energy": 2.0 +} \ No newline at end of file diff --git a/src/main/resources/data/minecraft/metabolites/cooked_porkchop.json b/src/main/resources/data/minecraft/metabolites/cooked_porkchop.json new file mode 100644 index 0000000..ce1b547 --- /dev/null +++ b/src/main/resources/data/minecraft/metabolites/cooked_porkchop.json @@ -0,0 +1,5 @@ +{ + "warmth": 10.0, + "food": 18.0, + "energy": 2.0 +} \ No newline at end of file diff --git a/src/main/resources/data/minecraft/metabolites/cooked_rabbit.json b/src/main/resources/data/minecraft/metabolites/cooked_rabbit.json new file mode 100644 index 0000000..3771c90 --- /dev/null +++ b/src/main/resources/data/minecraft/metabolites/cooked_rabbit.json @@ -0,0 +1,5 @@ +{ + "warmth": 6.0, + "food": 12.0, + "energy": 2.0 +} \ No newline at end of file diff --git a/src/main/resources/data/minecraft/metabolites/cooked_salmon.json b/src/main/resources/data/minecraft/metabolites/cooked_salmon.json new file mode 100644 index 0000000..ab9edad --- /dev/null +++ b/src/main/resources/data/minecraft/metabolites/cooked_salmon.json @@ -0,0 +1,5 @@ +{ + "warmth": 9.0, + "food": 12.0, + "energy": 2.0 +} \ No newline at end of file diff --git a/src/main/resources/data/minecraft/metabolites/cookie.json b/src/main/resources/data/minecraft/metabolites/cookie.json new file mode 100644 index 0000000..0578218 --- /dev/null +++ b/src/main/resources/data/minecraft/metabolites/cookie.json @@ -0,0 +1,5 @@ +{ + "warmth": 0.0, + "food": 2.0, + "energy": 8.0 +} \ No newline at end of file diff --git a/src/main/resources/data/minecraft/metabolites/dried_kelp.json b/src/main/resources/data/minecraft/metabolites/dried_kelp.json new file mode 100644 index 0000000..f6b3eb6 --- /dev/null +++ b/src/main/resources/data/minecraft/metabolites/dried_kelp.json @@ -0,0 +1,5 @@ +{ + "warmth": 0.0, + "food": 0.0, + "energy": 2.0 +} \ No newline at end of file diff --git a/src/main/resources/data/minecraft/metabolites/enchanted_golden_apple.json b/src/main/resources/data/minecraft/metabolites/enchanted_golden_apple.json new file mode 100644 index 0000000..61c41d0 --- /dev/null +++ b/src/main/resources/data/minecraft/metabolites/enchanted_golden_apple.json @@ -0,0 +1,5 @@ +{ + "warmth": 12.0, + "food": 4.0, + "energy": 100.0 +} \ No newline at end of file diff --git a/src/main/resources/data/minecraft/metabolites/glow_berries.json b/src/main/resources/data/minecraft/metabolites/glow_berries.json new file mode 100644 index 0000000..3a3f480 --- /dev/null +++ b/src/main/resources/data/minecraft/metabolites/glow_berries.json @@ -0,0 +1,5 @@ +{ + "warmth": 1.0, + "food": 1.0, + "energy": 3.0 +} \ No newline at end of file diff --git a/src/main/resources/data/minecraft/metabolites/golden_apple.json b/src/main/resources/data/minecraft/metabolites/golden_apple.json new file mode 100644 index 0000000..de67797 --- /dev/null +++ b/src/main/resources/data/minecraft/metabolites/golden_apple.json @@ -0,0 +1,5 @@ +{ + "warmth": 9.0, + "food": 4.0, + "energy": 40.0 +} \ No newline at end of file diff --git a/src/main/resources/data/minecraft/metabolites/golden_carrot.json b/src/main/resources/data/minecraft/metabolites/golden_carrot.json new file mode 100644 index 0000000..bce795a --- /dev/null +++ b/src/main/resources/data/minecraft/metabolites/golden_carrot.json @@ -0,0 +1,5 @@ +{ + "warmth": 6.0, + "food": 4.0, + "energy": 20.0 +} \ No newline at end of file diff --git a/src/main/resources/data/minecraft/metabolites/honey_bottle.json b/src/main/resources/data/minecraft/metabolites/honey_bottle.json new file mode 100644 index 0000000..dfb49a6 --- /dev/null +++ b/src/main/resources/data/minecraft/metabolites/honey_bottle.json @@ -0,0 +1,5 @@ +{ + "warmth": 0.0, + "food": 2.0, + "energy": 10.0 +} \ No newline at end of file diff --git a/src/main/resources/data/minecraft/metabolites/melon_slice.json b/src/main/resources/data/minecraft/metabolites/melon_slice.json new file mode 100644 index 0000000..0adc7a6 --- /dev/null +++ b/src/main/resources/data/minecraft/metabolites/melon_slice.json @@ -0,0 +1,5 @@ +{ + "warmth": 1.0, + "food": 2.0, + "energy": 6.0 +} \ No newline at end of file diff --git a/src/main/resources/data/minecraft/metabolites/mushroom_stew.json b/src/main/resources/data/minecraft/metabolites/mushroom_stew.json new file mode 100644 index 0000000..e57cf29 --- /dev/null +++ b/src/main/resources/data/minecraft/metabolites/mushroom_stew.json @@ -0,0 +1,5 @@ +{ + "warmth": 10.0, + "food": 8.0, + "energy": 8.0 +} \ No newline at end of file diff --git a/src/main/resources/data/minecraft/metabolites/poisonous_potato.json b/src/main/resources/data/minecraft/metabolites/poisonous_potato.json new file mode 100644 index 0000000..ec8afe4 --- /dev/null +++ b/src/main/resources/data/minecraft/metabolites/poisonous_potato.json @@ -0,0 +1,5 @@ +{ + "warmth": 0.0, + "food": 2.0, + "energy": 0.0 +} \ No newline at end of file diff --git a/src/main/resources/data/minecraft/metabolites/potato.json b/src/main/resources/data/minecraft/metabolites/potato.json new file mode 100644 index 0000000..2193436 --- /dev/null +++ b/src/main/resources/data/minecraft/metabolites/potato.json @@ -0,0 +1,5 @@ +{ + "warmth": 1.0, + "food": 3.0, + "energy": 2.0 +} \ No newline at end of file diff --git a/src/main/resources/data/minecraft/metabolites/pufferfish.json b/src/main/resources/data/minecraft/metabolites/pufferfish.json new file mode 100644 index 0000000..c5a0421 --- /dev/null +++ b/src/main/resources/data/minecraft/metabolites/pufferfish.json @@ -0,0 +1,5 @@ +{ + "warmth": 0.0, + "food": 1.0, + "energy": 0.0 +} \ No newline at end of file diff --git a/src/main/resources/data/minecraft/metabolites/pumpkin_pie.json b/src/main/resources/data/minecraft/metabolites/pumpkin_pie.json new file mode 100644 index 0000000..77b646f --- /dev/null +++ b/src/main/resources/data/minecraft/metabolites/pumpkin_pie.json @@ -0,0 +1,5 @@ +{ + "warmth": 7.0, + "food": 4.0, + "energy": 10.0 +} \ No newline at end of file diff --git a/src/main/resources/data/minecraft/metabolites/rabbit_stew.json b/src/main/resources/data/minecraft/metabolites/rabbit_stew.json new file mode 100644 index 0000000..e12e7e2 --- /dev/null +++ b/src/main/resources/data/minecraft/metabolites/rabbit_stew.json @@ -0,0 +1,5 @@ +{ + "warmth": 15.0, + "food": 12.0, + "energy": 12.0 +} \ No newline at end of file diff --git a/src/main/resources/data/minecraft/metabolites/raw_beef.json b/src/main/resources/data/minecraft/metabolites/raw_beef.json new file mode 100644 index 0000000..05c2c0f --- /dev/null +++ b/src/main/resources/data/minecraft/metabolites/raw_beef.json @@ -0,0 +1,5 @@ +{ + "warmth": 1.0, + "food": 4.0, + "energy": 0.0 +} \ No newline at end of file diff --git a/src/main/resources/data/minecraft/metabolites/raw_chicken.json b/src/main/resources/data/minecraft/metabolites/raw_chicken.json new file mode 100644 index 0000000..a83cf4d --- /dev/null +++ b/src/main/resources/data/minecraft/metabolites/raw_chicken.json @@ -0,0 +1,5 @@ +{ + "warmth": 1.0, + "food": 2.0, + "energy": 0.0 +} \ No newline at end of file diff --git a/src/main/resources/data/minecraft/metabolites/raw_cod.json b/src/main/resources/data/minecraft/metabolites/raw_cod.json new file mode 100644 index 0000000..a83cf4d --- /dev/null +++ b/src/main/resources/data/minecraft/metabolites/raw_cod.json @@ -0,0 +1,5 @@ +{ + "warmth": 1.0, + "food": 2.0, + "energy": 0.0 +} \ No newline at end of file diff --git a/src/main/resources/data/minecraft/metabolites/raw_mutton.json b/src/main/resources/data/minecraft/metabolites/raw_mutton.json new file mode 100644 index 0000000..a83cf4d --- /dev/null +++ b/src/main/resources/data/minecraft/metabolites/raw_mutton.json @@ -0,0 +1,5 @@ +{ + "warmth": 1.0, + "food": 2.0, + "energy": 0.0 +} \ No newline at end of file diff --git a/src/main/resources/data/minecraft/metabolites/raw_porkchop.json b/src/main/resources/data/minecraft/metabolites/raw_porkchop.json new file mode 100644 index 0000000..05c2c0f --- /dev/null +++ b/src/main/resources/data/minecraft/metabolites/raw_porkchop.json @@ -0,0 +1,5 @@ +{ + "warmth": 1.0, + "food": 4.0, + "energy": 0.0 +} \ No newline at end of file diff --git a/src/main/resources/data/minecraft/metabolites/raw_rabbit.json b/src/main/resources/data/minecraft/metabolites/raw_rabbit.json new file mode 100644 index 0000000..a83cf4d --- /dev/null +++ b/src/main/resources/data/minecraft/metabolites/raw_rabbit.json @@ -0,0 +1,5 @@ +{ + "warmth": 1.0, + "food": 2.0, + "energy": 0.0 +} \ No newline at end of file diff --git a/src/main/resources/data/minecraft/metabolites/raw_salmon.json b/src/main/resources/data/minecraft/metabolites/raw_salmon.json new file mode 100644 index 0000000..a83cf4d --- /dev/null +++ b/src/main/resources/data/minecraft/metabolites/raw_salmon.json @@ -0,0 +1,5 @@ +{ + "warmth": 1.0, + "food": 2.0, + "energy": 0.0 +} \ No newline at end of file diff --git a/src/main/resources/data/minecraft/metabolites/rotten_flesh.json b/src/main/resources/data/minecraft/metabolites/rotten_flesh.json new file mode 100644 index 0000000..ec8afe4 --- /dev/null +++ b/src/main/resources/data/minecraft/metabolites/rotten_flesh.json @@ -0,0 +1,5 @@ +{ + "warmth": 0.0, + "food": 2.0, + "energy": 0.0 +} \ No newline at end of file diff --git a/src/main/resources/data/minecraft/metabolites/spider_eye.json b/src/main/resources/data/minecraft/metabolites/spider_eye.json new file mode 100644 index 0000000..5faaeb0 --- /dev/null +++ b/src/main/resources/data/minecraft/metabolites/spider_eye.json @@ -0,0 +1,5 @@ +{ + "warmth": 0.0, + "food": 2.0, + "energy": 1.0 +} \ No newline at end of file diff --git a/src/main/resources/data/minecraft/metabolites/steak.json b/src/main/resources/data/minecraft/metabolites/steak.json new file mode 100644 index 0000000..ce1b547 --- /dev/null +++ b/src/main/resources/data/minecraft/metabolites/steak.json @@ -0,0 +1,5 @@ +{ + "warmth": 10.0, + "food": 18.0, + "energy": 2.0 +} \ No newline at end of file diff --git a/src/main/resources/data/minecraft/metabolites/suspicious_stew.json b/src/main/resources/data/minecraft/metabolites/suspicious_stew.json new file mode 100644 index 0000000..e57cf29 --- /dev/null +++ b/src/main/resources/data/minecraft/metabolites/suspicious_stew.json @@ -0,0 +1,5 @@ +{ + "warmth": 10.0, + "food": 8.0, + "energy": 8.0 +} \ No newline at end of file diff --git a/src/main/resources/data/minecraft/metabolites/sweet_berries.json b/src/main/resources/data/minecraft/metabolites/sweet_berries.json new file mode 100644 index 0000000..17e1a18 --- /dev/null +++ b/src/main/resources/data/minecraft/metabolites/sweet_berries.json @@ -0,0 +1,5 @@ +{ + "warmth": 1.0, + "food": 3.0, + "energy": 1.0 +} \ No newline at end of file diff --git a/src/main/resources/data/minecraft/metabolites/tropical_fish.json b/src/main/resources/data/minecraft/metabolites/tropical_fish.json new file mode 100644 index 0000000..2055b90 --- /dev/null +++ b/src/main/resources/data/minecraft/metabolites/tropical_fish.json @@ -0,0 +1,5 @@ +{ + "warmth": 0.0, + "food": 1.0, + "energy": 1.0 +} \ No newline at end of file diff --git a/src/main/resources/metabolism.mixins.json b/src/main/resources/metabolism.mixins.json new file mode 100644 index 0000000..776423d --- /dev/null +++ b/src/main/resources/metabolism.mixins.json @@ -0,0 +1,17 @@ +{ + "required": true, + "package": "lilypuree.metabolism.mixin", + "compatibilityLevel": "JAVA_17", + "refmap": "metabolism.refmap.json", + "mixins": [ + "LocationCheckAccessor", + "FoodDataMixin", + "PlayerMixin" + ], + "client": [ + ], + "injectors": { + "defaultRequire": 1 + }, + "minVersion": "0.8" +} \ No newline at end of file diff --git a/src/main/resources/pack.mcmeta b/src/main/resources/pack.mcmeta index 59c5240..69e7a59 100644 --- a/src/main/resources/pack.mcmeta +++ b/src/main/resources/pack.mcmeta @@ -3,6 +3,6 @@ "description": { "text": "${mod_id} resources" }, - "pack_format": ${pack_format_number} + "pack_format": 15 } }