Wither Config allows you to adjust the Wither's stats and behavior for a tailored boss experience.

Done

+212 -53
+8 -1
Changelog.md
··· 4 4 5 5 The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project follows to [Ragnarök Versioning Convention](https://shor.cz/ragnarok_versioning_convention). 6 6 7 - ## [Unreleased] Wither Config Version 1 Changelog 7 + ## Wither Config Version 1 Changelog - 2023-09-06 8 8 9 9 ### Added 10 10 ··· 14 14 - The Wither follow range 15 15 - The Wither armor 16 16 - The Wither follow distance 17 + - The Wither unarmored fly height 17 18 - The Wither summoning sequence length 18 19 - The Wither summoning sequence end explosion strength 20 + - The Wither skulls damage 21 + - The Wither skulls magic damage 22 + - The Wither skulls heal towards the Wither when getting a kill 19 23 - The Wither skulls explosion strength 24 + - The Wither skulls potion effects 25 + - Whether the Wither should attempt to break blocks around it when targeting a player 26 + - Fix for the Vanilla broken Wither target following behavior
+23 -1
README.md
··· 10 10 11 11 # Wither Config 12 12 13 - This also fixes the Minecraft bug where the Wither overshoots the distance between it and it's target making it always go towards it's target instead of stopping 9 blocks away. 13 + Wither Config allows you to adjust the Wither's stats for a tailored boss experience. 14 + 15 + This also fixes the Minecraft bug where the Wither overshoots the distance between it, and its target making it always go towards its target instead of stopping nine blocks away. 16 + 17 + This mod was commissioned for the [Trepidation modpack](https://curseforge.com/minecraft/modpacks/trepidation) 18 + 19 + Available Configs: 20 + - The Wither health 21 + - The Wither movement speed 22 + - The Wither follow range 23 + - The Wither armor 24 + - The Wither follow distance 25 + - The Wither unarmored fly height 26 + - The Wither summoning sequence length 27 + - The Wither summoning sequence end explosion strength 28 + - The Wither skulls damage 29 + - The Wither skulls magic damage 30 + - The Wither skulls heal towards the Wither when getting a kill 31 + - The Wither skulls explosion strength 32 + - The Wither skulls potion effects 33 + - Whether the Wither should attempt to break blocks around it when targeting a player 34 + 35 + *Something is missing? Go on the issue page and suggest it* 14 36 15 37 --- 16 38
+3 -7
build.gradle
··· 34 34 35 35 username = "Desoroxxx" 36 36 37 - extraRunJvmArguments.addAll("-Xmx4G", "-Dforge.logging.console.level=debug", "-Dmixin.hotSwap=true", "-Dmixin.checks.interfaces=true", "-Dmixin.debug.export=true", "-Dfml.coreMods.load=io.redstudioragnarok.${project.id}.asm.WitherConfigPlugin") 37 + extraRunJvmArguments.addAll("-Xmx4G", "-Dforge.logging.console.level=debug", "-Dmixin.hotSwap=true", "-Dmixin.checks.interfaces=true", "-Dmixin.debug.export=true", "-Dfml.coreMods.load=dev.redstudio.${project.id}.asm.WitherConfigPlugin") 38 38 39 39 injectedTags.put("ID", project.id) 40 40 injectedTags.put("VERSION", project.version) ··· 42 42 43 43 // Generate a my.project.Tags class with the version number as a field 44 44 tasks.injectTags.configure { 45 - outputClassName.set("io.redstudioragnarok.${project.id}.Tags") 45 + outputClassName.set("dev.redstudio.${project.id}.Tags") 46 46 } 47 47 48 48 configurations { ··· 80 80 } 81 81 82 82 dependencies { 83 - implementation "Red-Studio-Ragnarok:Red-Core:0.4-Dev-6" 84 - sources "Red-Studio-Ragnarok:Red-Core:0.4-Dev-6:sources@jar" 85 - 86 83 String mixin = modUtils.enableMixins("zone.rong:mixinbooter:8.4", "mixins.${project.id}.refmap.json") 87 84 api (mixin) { transitive = false } 88 85 annotationProcessor "org.ow2.asm:asm-debug-all:5.2" ··· 107 104 jar { 108 105 manifest.attributes([ 109 106 "ModSide": "BOTH", 110 - "FMLAT": "${project.id}_at.cfg", 111 - "FMLCorePlugin": "io.redstudioragnarok.${project.id}.asm.WitherConfigPlugin", 107 + "FMLCorePlugin": "dev.redstudio.${project.id}.asm.WitherConfigPlugin", 112 108 "FMLCorePluginContainsFMLMod": true, 113 109 "ForceLoadAsMod": true 114 110 ])
+1 -1
gradle.properties
··· 10 10 # Mod Constants 11 11 # Versioning must follow Ragnar�k versioning convention: https://shor.cz/ragnarok_versioning_convention 12 12 id = witherconfig 13 - version = 1-Dev-2 13 + version = 1
+65
src/main/java/dev/redstudio/witherconfig/mixin/EntityWitherSkullMixin.java
··· 1 + package dev.redstudio.witherconfig.mixin; 2 + 3 + import com.llamalad7.mixinextras.sugar.Local; 4 + import dev.redstudio.witherconfig.config.WitherConfigConfig; 5 + import net.minecraft.entity.EntityLivingBase; 6 + import net.minecraft.entity.projectile.EntityWitherSkull; 7 + import net.minecraft.potion.Potion; 8 + import net.minecraft.potion.PotionEffect; 9 + import org.spongepowered.asm.mixin.Mixin; 10 + import org.spongepowered.asm.mixin.injection.At; 11 + import org.spongepowered.asm.mixin.injection.ModifyArg; 12 + import org.spongepowered.asm.mixin.injection.Redirect; 13 + 14 + import static dev.redstudio.witherconfig.utils.ModReference.LOG; 15 + 16 + /** 17 + * @author Luna Lage (Desoroxxx) 18 + * @since 1 19 + */ 20 + @Mixin(EntityWitherSkull.class) 21 + public final class EntityWitherSkullMixin { 22 + 23 + @ModifyArg(method = "onImpact", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/Entity;attackEntityFrom(Lnet/minecraft/util/DamageSource;F)Z", ordinal = 0)) 24 + private float changeDamage(final float original) { 25 + return WitherConfigConfig.common.skulls.damage; 26 + } 27 + 28 + @ModifyArg(method = "onImpact", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/Entity;attackEntityFrom(Lnet/minecraft/util/DamageSource;F)Z", ordinal = 1)) 29 + private float changeMagicDamage(final float original) { 30 + return WitherConfigConfig.common.skulls.magicDamage; 31 + } 32 + 33 + @ModifyArg(method = "onImpact", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/EntityLivingBase;heal(F)V")) 34 + private float changeHeal(final float original) { 35 + return WitherConfigConfig.common.skulls.healOnKill; 36 + } 37 + 38 + @ModifyArg(method = "onImpact", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/World;newExplosion(Lnet/minecraft/entity/Entity;DDDFZZ)Lnet/minecraft/world/Explosion;")) 39 + private float changeExplosionStrength(final float original) { 40 + return WitherConfigConfig.common.skulls.explosionStrength; 41 + } 42 + 43 + @Redirect(method = "onImpact", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/EntityLivingBase;addPotionEffect(Lnet/minecraft/potion/PotionEffect;)V")) 44 + private void redirectAddPotionEffect(final EntityLivingBase entityLiving, final PotionEffect potionEffect, @Local int durationMultiplier) { 45 + for (final String line : WitherConfigConfig.common.skulls.effects) { 46 + // Split the string based on the delimiter ';' 47 + final String[] parts = line.split(";"); 48 + 49 + // Make sure there are exactly three parts, otherwise the string is malformed 50 + if (parts.length != 3) { 51 + LOG.error("Malformed potion effect config: {}", line); 52 + return; 53 + } 54 + 55 + final Potion potion = Potion.getPotionFromResourceLocation(parts[0]); 56 + final int duration = Integer.parseInt(parts[1]) * durationMultiplier; 57 + final int level = Integer.parseInt(parts[2]); 58 + 59 + if (potion != null) 60 + entityLiving.addPotionEffect(new PotionEffect(potion, duration, level)); 61 + else 62 + LOG.error("Mob effect was not found {}", parts[0]); 63 + } 64 + } 65 + }
+6 -6
src/main/java/io/redstudioragnarok/witherconfig/WitherConfig.java src/main/java/dev/redstudio/witherconfig/WitherConfig.java
··· 1 - package io.redstudioragnarok.witherconfig; 1 + package dev.redstudio.witherconfig; 2 2 3 3 import net.minecraftforge.fml.common.Mod; 4 4 5 - import static io.redstudioragnarok.witherconfig.utils.ModReference.ID; 6 - import static io.redstudioragnarok.witherconfig.utils.ModReference.NAME; 7 - import static io.redstudioragnarok.witherconfig.utils.ModReference.VERSION; 5 + import static dev.redstudio.witherconfig.utils.ModReference.ID; 6 + import static dev.redstudio.witherconfig.utils.ModReference.NAME; 7 + import static dev.redstudio.witherconfig.utils.ModReference.VERSION; 8 8 9 9 // /$$ /$$ /$$ /$$ /$$ /$$$$$$ /$$$$$$ /$$ 10 10 // | $$ /$ | $$|__/ | $$ | $$ /$$__ $$ /$$__ $$|__/ ··· 17 17 // /$$ \ $$ 18 18 // | $$$$$$/ 19 19 // \______/ 20 - @Mod(modid = ID, name = NAME, version = VERSION/*, updateJSON = "https://raw.githubusercontent.com/Red-Studio-Ragnarok/witherconfig/main/update.json"*/) 21 - public class WitherConfig { 20 + @Mod(modid = ID, name = NAME, version = VERSION, updateJSON = "https://raw.githubusercontent.com/Desoroxxx/Wither-Config/main/update.json") 21 + public final class WitherConfig { 22 22 }
+2 -2
src/main/java/io/redstudioragnarok/witherconfig/asm/WitherConfigPlugin.java src/main/java/dev/redstudio/witherconfig/asm/WitherConfigPlugin.java
··· 1 - package io.redstudioragnarok.witherconfig.asm; 1 + package dev.redstudio.witherconfig.asm; 2 2 3 3 import net.minecraftforge.fml.relauncher.IFMLLoadingPlugin; 4 4 import zone.rong.mixinbooter.IEarlyMixinLoader; ··· 9 9 10 10 @IFMLLoadingPlugin.Name() 11 11 @IFMLLoadingPlugin.MCVersion("1.12.2") 12 - @IFMLLoadingPlugin.TransformerExclusions("io.redstudioragnarok.witherconfig.asm") 12 + @IFMLLoadingPlugin.TransformerExclusions("dev.redstudio.witherconfig.asm") 13 13 public class WitherConfigPlugin implements IFMLLoadingPlugin, IEarlyMixinLoader { 14 14 15 15 @Override
+13 -5
src/main/java/io/redstudioragnarok/witherconfig/config/WitherConfigConfig.java src/main/java/dev/redstudio/witherconfig/config/WitherConfigConfig.java
··· 1 - package io.redstudioragnarok.witherconfig.config; 1 + package dev.redstudio.witherconfig.config; 2 2 3 3 import net.minecraftforge.common.config.Config; 4 4 import net.minecraftforge.common.config.ConfigManager; ··· 6 6 import net.minecraftforge.fml.common.Mod; 7 7 import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; 8 8 9 - import static io.redstudioragnarok.witherconfig.utils.ModReference.ID; 10 - import static io.redstudioragnarok.witherconfig.utils.ModReference.NAME; 9 + import static dev.redstudio.witherconfig.utils.ModReference.ID; 10 + import static dev.redstudio.witherconfig.utils.ModReference.NAME; 11 11 12 12 @Config(modid = ID, name = NAME) 13 13 public class WitherConfigConfig { 14 14 15 - public static final Common common = new Common(); // Todo: Is this really needed? 15 + public static final Common common = new Common(); 16 16 17 17 public static class Common { 18 18 ··· 25 25 public double followRange = 40; // Range for the Wither to look for it's target to follow it 26 26 public double armor = 4; 27 27 28 - public double followDistance = 9; 28 + public float unarmoredFlyHeight = 5; 29 + public float followDistance = 9; 30 + 31 + public boolean breakBlocksWhenTargetingPlayer = false; 29 32 30 33 public static class SummonSequence { 31 34 ··· 35 38 36 39 public static class Skulls { 37 40 41 + public float damage = 8; 42 + public float magicDamage = 5; 43 + public float healOnKill = 5; 38 44 public float explosionStrength = 1; 45 + 46 + public String[] effects = new String[]{"minecraft:wither;20;1"}; 39 47 } 40 48 } 41 49
+45 -3
src/main/java/io/redstudioragnarok/witherconfig/mixin/EntityWitherMixin.java src/main/java/dev/redstudio/witherconfig/mixin/EntityWitherMixin.java
··· 1 - package io.redstudioragnarok.witherconfig.mixin; 1 + package dev.redstudio.witherconfig.mixin; 2 2 3 - import io.redstudioragnarok.witherconfig.config.WitherConfigConfig; 3 + import dev.redstudio.witherconfig.config.WitherConfigConfig; 4 + import net.minecraft.block.state.IBlockState; 4 5 import net.minecraft.entity.Entity; 5 6 import net.minecraft.entity.boss.EntityWither; 6 7 import net.minecraft.entity.monster.EntityMob; 8 + import net.minecraft.entity.player.EntityPlayer; 9 + import net.minecraft.util.math.BlockPos; 10 + import net.minecraft.util.math.MathHelper; 7 11 import net.minecraft.world.World; 12 + import net.minecraftforge.event.ForgeEventFactory; 8 13 import org.spongepowered.asm.mixin.Mixin; 9 14 import org.spongepowered.asm.mixin.Shadow; 15 + import org.spongepowered.asm.mixin.Unique; 10 16 import org.spongepowered.asm.mixin.injection.At; 11 17 import org.spongepowered.asm.mixin.injection.Inject; 12 18 import org.spongepowered.asm.mixin.injection.ModifyArg; ··· 23 29 @Shadow public abstract int getWatchedTargetId(final int head); 24 30 25 31 @Shadow public abstract boolean isArmored(); 32 + 33 + @Shadow private int blockBreakCounter; 26 34 27 35 private EntityWitherMixin(final World world) { 28 36 super(world); ··· 73 81 if (target == null) 74 82 return; 75 83 84 + if (target instanceof EntityPlayer && WitherConfigConfig.common.breakBlocksWhenTargetingPlayer) 85 + wither_Config$destroyBlocks(); 86 + 76 87 final double movementSpeed = WitherConfigConfig.common.movementSpeed; 77 88 78 - if (posY < target.posY || !isArmored() && posY < target.posY + 5) { 89 + if (posY < target.posY || !isArmored() && posY < target.posY + WitherConfigConfig.common.unarmoredFlyHeight) { 79 90 if (motionY < 0) 80 91 motionY = 0; 81 92 ··· 89 100 if (distanceToTarget > WitherConfigConfig.common.followDistance) { 90 101 motionX += (deltaX / distanceToTarget * 0.5 - motionX) * movementSpeed; 91 102 motionZ += (deltaZ / distanceToTarget * 0.5 - motionZ) * movementSpeed; 103 + } 104 + } 105 + 106 + @Unique 107 + private void wither_Config$destroyBlocks() { 108 + if (net.minecraftforge.event.ForgeEventFactory.getMobGriefingEvent(this.world, this)) { 109 + final int x = MathHelper.floor(posX); 110 + final int y = MathHelper.floor(posY); 111 + final int z = MathHelper.floor(posZ); 112 + 113 + boolean flag = false; 114 + 115 + for (int xOffset = -1; xOffset <= 1; ++xOffset) { 116 + for (int zOffset = -1; zOffset <= 1; ++zOffset) { 117 + for (int yOffset = 0; yOffset <= 4; ++yOffset) { 118 + final int currentX = x + xOffset; 119 + final int currentY = y + yOffset; 120 + final int currentZ = z + zOffset; 121 + 122 + final BlockPos blockPos = new BlockPos(currentX, currentY, currentZ); 123 + final IBlockState blockState = this.world.getBlockState(blockPos); 124 + 125 + if (blockState.getBlock().canEntityDestroy(blockState, world, blockPos, this) && ForgeEventFactory.onEntityDestroyBlock(this, blockPos, blockState)) 126 + flag = this.world.destroyBlock(blockPos, true) || flag; 127 + } 128 + } 129 + } 130 + 131 + if (flag) { 132 + this.world.playEvent(null, 1022, new BlockPos(this), 0); 133 + } 92 134 } 93 135 } 94 136 }
-20
src/main/java/io/redstudioragnarok/witherconfig/mixin/EntityWitherSkullMixin.java
··· 1 - package io.redstudioragnarok.witherconfig.mixin; 2 - 3 - import io.redstudioragnarok.witherconfig.config.WitherConfigConfig; 4 - import net.minecraft.entity.projectile.EntityWitherSkull; 5 - import org.spongepowered.asm.mixin.Mixin; 6 - import org.spongepowered.asm.mixin.injection.At; 7 - import org.spongepowered.asm.mixin.injection.ModifyArg; 8 - 9 - /** 10 - * @author Luna Lage (Desoroxxx) 11 - * @since 1 12 - */ 13 - @Mixin(EntityWitherSkull.class) 14 - public final class EntityWitherSkullMixin { 15 - 16 - @ModifyArg(method = "onImpact", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/World;newExplosion(Lnet/minecraft/entity/Entity;DDDFZZ)Lnet/minecraft/world/Explosion;")) 17 - private float changeExplosionStrength(final float original) { 18 - return WitherConfigConfig.common.skulls.explosionStrength; 19 - } 20 - }
+2 -2
src/main/java/io/redstudioragnarok/witherconfig/utils/ModReference.java src/main/java/dev/redstudio/witherconfig/utils/ModReference.java
··· 1 - package io.redstudioragnarok.witherconfig.utils; 1 + package dev.redstudio.witherconfig.utils; 2 2 3 - import io.redstudioragnarok.witherconfig.Tags; 3 + import dev.redstudio.witherconfig.Tags; 4 4 import org.apache.logging.log4j.LogManager; 5 5 import org.apache.logging.log4j.Logger; 6 6
+39
src/main/resources/assets/witherconfig/lang/en_us.lang
··· 1 + witherconfig.general.common=Common 2 + witherconfig.general.common.tooltip=Common configuration for the Wither 3 + 4 + witherconfig.general.common.maxhealth=Max Health 5 + witherconfig.general.common.maxhealth.tooltip=The maximum health of the Wither 6 + witherconfig.general.common.movementspeed=Movement Speed 7 + witherconfig.general.common.movementspeed.tooltip=The movement speed of the Wither 8 + witherconfig.general.common.followrange=Follow Range 9 + witherconfig.general.common.followrange.tooltip=The range in which the Wither will search for targets 10 + witherconfig.general.common.armor=Armor 11 + witherconfig.general.common.armor.tooltip=The armor of the Wither 12 + witherconfig.general.common.unarmoredflyheight=Unarmored Fly Height 13 + witherconfig.general.common.unarmoredflyheight.tooltip=The height at which the Wither will fly above the ground while unarmored 14 + witherconfig.general.common.followdistance=Follow Distance 15 + witherconfig.general.common.followdistance.tooltip=The target distance between the Wither and it's target 16 + witherconfig.general.common.breakblockswhentargetingplayer=Break Blocks when Targeting a Player 17 + witherconfig.general.common.breakblockswhentargetingplayer.tooltip=Whether or not the Wither should attempt to break blocks around it when targeting a player 18 + 19 + witherconfig.general.common.summonsequence=Summon Sequence 20 + witherconfig.general.common.summonsequence.tooltip=Configuration for the Wither summon sequence 21 + 22 + witherconfig.general.common.summonsequence.length=Length 23 + witherconfig.general.common.summonsequence.length.tooltip=The length of the summon sequence, in ticks 24 + witherconfig.general.common.summonsequence.endexplosionstrength=End Explosion Strength 25 + witherconfig.general.common.summonsequence.endexplosionstrength.tooltip=The strength of the explosion at the end of the summon sequence, affecting the size of the explosion and the damage it inflict 26 + 27 + witherconfig.general.common.skulls=Skulls 28 + witherconfig.general.common.skulls.tooltip=Configuration for the Wither Skulls 29 + 30 + witherconfig.general.common.skulls.damage=Damage 31 + witherconfig.general.common.skulls.damage.tooltip=The damage that the skulls inflict 32 + witherconfig.general.common.skulls.magicdamage=Magic Damage 33 + witherconfig.general.common.skulls.magicdamage.tooltip=The magic damage that the skulls inflict, magic damage is the damage that happens once the Wither that sent the skulls died before the skulls hit something 34 + witherconfig.general.common.skulls.healonkill=Heal On Kill 35 + witherconfig.general.common.skulls.healonkill.tooltip=The ammount of heal the Wither should receive when skulls kills an entity 36 + witherconfig.general.common.skulls.explosionstrength=Explosion Strength 37 + witherconfig.general.common.skulls.explosionstrength.tooltip=The strength of the explosion of the skulls, affecting the size of the explosion and the damage it inflict 38 + witherconfig.general.common.skulls.effects=Effects 39 + witherconfig.general.common.skulls.effects.tooltip=The effects that the explosion of the skulls will inflict
src/main/resources/assets/witherconfig/textures/logo.png

This is a binary file and will not be displayed.

+2 -2
src/main/resources/mcmod.info
··· 2 2 { 3 3 "modid": "${id}", 4 4 "name": "Wither Config", 5 - "description": "", 5 + "description": "Wither Config allows you to adjust the Wither's stats for a tailored boss experience\n\n§lWant to have your own mod or support me?§r\nIf you're looking for a mod but don't have the development skills or time, consider commissioning me!\nMy commissions are currently open and I would be happy to create a custom mod to fit your needs as long as you provide assets.\nhttps://www.buymeacoffee.com/desoroxxx/commissions\n\nYou can also support me on a monthly basis by becoming a member.\nTo thank you will have the possibility to access exclusive post and messages, Discord channel for WIP content, and even access to unreleased Prototypes or WIP Projects.\nhttps://www.buymeacoffee.com/desoroxxx/membership\n\nYou can also buy me a hot chocolate\nhttps://www.buymeacoffee.com/desoroxxx.", 6 6 "version": "${version}", 7 7 "url": "https://www.curseforge.com/minecraft/mc-mods/witherconfig", 8 8 "authorList": ["Red Studio"], 9 - "credits": "Desoroxxx", 9 + "credits": "Desoroxxx, CalaMariGold", 10 10 "logoFile": "assets/witherconfig/textures/logo.png" 11 11 } 12 12 ]
+2 -2
src/main/resources/mixins.witherconfig.json
··· 1 1 { 2 2 "required": true, 3 3 "minVersion": "0.8", 4 - "package": "io.redstudioragnarok.witherconfig.mixin", 4 + "package": "dev.redstudio.witherconfig.mixin", 5 5 "compatibilityLevel": "JAVA_8", 6 - "refmap": "mixins.Replace.refmap.json", 6 + "refmap": "mixins.witherconfig.refmap.json", 7 7 "client": [ 8 8 ], 9 9 "injectors": {
+1 -1
update.json
··· 1 1 { 2 - "homepage": "https://www.curseforge.com/minecraft/mc-mods/Replace", 2 + "homepage": "https://www.curseforge.com/minecraft/mc-mods/witherconfig", 3 3 "promos": { 4 4 "1.12.2-latest": "0.1", 5 5 "1.12.2-recommended": "0.1"