diff --git a/src/main/java/com/zivilon/cinder_loe/CinderLoE.java b/src/main/java/com/zivilon/cinder_loe/CinderLoE.java index f6eab5a..efe189a 100644 --- a/src/main/java/com/zivilon/cinder_loe/CinderLoE.java +++ b/src/main/java/com/zivilon/cinder_loe/CinderLoE.java @@ -12,6 +12,7 @@ import com.zivilon.cinder_loe.command.CommandCinderCharacter; import com.zivilon.cinder_loe.command.CommandOpenCarriageMenu; import com.zivilon.cinder_loe.command.CommandSetDestination; import com.zivilon.cinder_loe.command.CommandWarband; +import com.zivilon.cinder_loe.command.CommandUnitConversion; import com.zivilon.cinder_loe.entity.*; import com.zivilon.cinder_loe.entity.animals.Monkey; import com.zivilon.cinder_loe.entity.boss.CryptBoss; @@ -377,6 +378,7 @@ public class CinderLoE { event.registerServerCommand(new CommandWarband()); event.registerServerCommand(new CommandOpenCarriageMenu()); event.registerServerCommand(new CommandSetDestination()); + event.registerServerCommand(new CommandUnitConversion()); // event.registerServerCommand(new CommandMobileSound()); } diff --git a/src/main/java/com/zivilon/cinder_loe/CinderLoE_Config.java b/src/main/java/com/zivilon/cinder_loe/CinderLoE_Config.java index 92a742d..b277d8e 100644 --- a/src/main/java/com/zivilon/cinder_loe/CinderLoE_Config.java +++ b/src/main/java/com/zivilon/cinder_loe/CinderLoE_Config.java @@ -27,7 +27,7 @@ public class CinderLoE_Config { public static boolean objective_rhudaur; public static boolean warbands_enabled; - + public static boolean unit_conversion_enabled; public static void init(FMLPreInitializationEvent event) { File configFile = new File(event.getModConfigurationDirectory(), "CinderLoE.cfg"); @@ -61,6 +61,7 @@ public class CinderLoE_Config { objective_rhudaur = config.getBoolean("Rhudaur", Configuration.CATEGORY_GENERAL, false, "set true if Rhudaur Objective Complete"); warbands_enabled = config.getBoolean("Warbands", Configuration.CATEGORY_GENERAL, true, "Set false to disable warbands"); + unit_conversion_enabled = config.getBoolean("Unit conversion", Configuration.CATEGORY_GENERAL, false, "Set false to disable unit stat updates when units are readied"); // Save the configuration if it has changed if (config.hasChanged()) { config.save(); diff --git a/src/main/java/com/zivilon/cinder_loe/command/CommandUnitConversion.java b/src/main/java/com/zivilon/cinder_loe/command/CommandUnitConversion.java new file mode 100644 index 0000000..f334b62 --- /dev/null +++ b/src/main/java/com/zivilon/cinder_loe/command/CommandUnitConversion.java @@ -0,0 +1,46 @@ +package com.zivilon.cinder_loe.command; + +import com.zivilon.cinder_loe.CinderLoE_Config; + +import net.minecraft.command.CommandBase; +import net.minecraft.command.ICommandSender; +import net.minecraft.util.ChatComponentText; + +import java.util.UUID; + +public class CommandUnitConversion extends CommandBase { + + @Override + public String getCommandName() { + return "unit_conversion"; + } + + @Override + public String getCommandUsage(ICommandSender sender) { + return "/unit_conversion "; + } + + @Override + public int getRequiredPermissionLevel() { + return 4; + } + + @Override + public void processCommand(ICommandSender sender, String[] args) { + if(args.length < 1) { + sender.addChatMessage(new ChatComponentText("Incorrect arguments. Usage: " + getCommandUsage(sender))); + return; + } + + String action = args[0]; + if (action.equals("on")) { + CinderLoE_Config.unit_conversion_enabled = true; + sender.addChatMessage(new ChatComponentText("Unit conversion enabled.")); + } else if (action.equals("off")) { + CinderLoE_Config.unit_conversion_enabled = false; + sender.addChatMessage(new ChatComponentText("Unit conversion disabled.")); + } else if (action.equals("status")) { + sender.addChatMessage(new ChatComponentText("Unit conversion status: " + CinderLoE_Config.unit_conversion_enabled)); + } + } +} diff --git a/src/main/java/com/zivilon/cinder_loe/mixins/overrides/MixinLOTRHiredNPCInfo.java b/src/main/java/com/zivilon/cinder_loe/mixins/overrides/MixinLOTRHiredNPCInfo.java index 2a1b796..418e04c 100644 --- a/src/main/java/com/zivilon/cinder_loe/mixins/overrides/MixinLOTRHiredNPCInfo.java +++ b/src/main/java/com/zivilon/cinder_loe/mixins/overrides/MixinLOTRHiredNPCInfo.java @@ -1,10 +1,14 @@ package com.zivilon.cinder_loe.mixins.overrides; +import com.zivilon.cinder_loe.util.Utilities; +import com.zivilon.cinder_loe.CinderLoE_Config; + import lotr.common.entity.npc.LOTREntityNPC; import lotr.common.entity.npc.LOTRHiredNPCInfo; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.SharedMonsterAttributes; +import net.minecraft.entity.ai.attributes.AttributeModifier; import net.minecraft.entity.ai.attributes.IAttribute; import net.minecraft.entity.ai.attributes.IAttributeInstance; import net.minecraft.entity.ai.attributes.RangedAttribute; @@ -24,17 +28,26 @@ import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Unique; import java.util.UUID; +import java.lang.reflect.Method; @Mixin(LOTRHiredNPCInfo.class) -public class MixinLOTRHiredNPCInfo { +public abstract class MixinLOTRHiredNPCInfo { + + private static UUID LEVEL_HEALTH_UUID = UUID.fromString("11111111-0000-0000-0000-000000000000"); + private static UUID LEVEL_DAMAGE_UUID = UUID.fromString("22222222-0000-0000-0000-000000000000"); + private static UUID LEVEL_MOVEMENT_UUID = UUID.fromString("33333333-0000-0000-0000-000000000000"); + private static double HEALTH_PER_UPGRADE = 1.0D; + private static double DAMAGE_PER_UPGRADE = 0.25D; + private static double SPEED_PER_UPGRADE = 0.0025D; + @Shadow private LOTREntityNPC theEntity; @Shadow private UUID hiringPlayerUUID; @Shadow public int xpLevel = 1; - @Unique - public int levelUpCounter = 0; + @Shadow + private boolean canMove; /** * @author @@ -57,9 +70,9 @@ public class MixinLOTRHiredNPCInfo { public void rotateLevelUpStat() { - EntityLivingBase entity = (EntityLivingBase) this.theEntity; + LOTREntityNPC entity = this.theEntity; - switch (levelUpCounter) { + switch (entity.hiredNPCInfo.xpLevel % 3) { case 0: // +1 HP this.addLevelUpHealthGain(entity); @@ -72,12 +85,7 @@ public class MixinLOTRHiredNPCInfo { // +0.005 movement Speed this.increaseMovementGain(entity); break; - case 3: - // +0.1 knockback resistance - this.increaseKnockbackGain(entity); - break; } - levelUpCounter = (levelUpCounter +1) % 4; } /** @@ -85,30 +93,24 @@ public class MixinLOTRHiredNPCInfo { * @reason */ @Overwrite(remap = false) - public void addLevelUpHealthGain(EntityLivingBase gainingEntity) { - float healthBoost = 1.0f; - IAttributeInstance attrHealth = gainingEntity.getEntityAttribute(SharedMonsterAttributes.maxHealth); - attrHealth.setBaseValue(attrHealth.getBaseValue() + (double)healthBoost); - gainingEntity.heal(healthBoost); + public void addLevelUpHealthGain(EntityLivingBase entity) { + IAttributeInstance attr = entity.getEntityAttribute(SharedMonsterAttributes.maxHealth); + Utilities.increment_modifier(attr, LEVEL_HEALTH_UUID, "levelup_health", HEALTH_PER_UPGRADE, 0); + entity.heal(1.0f); } - public void increaseDamageGain(EntityLivingBase gainingEntity) { - float damageBoost = 0.25f; - IAttributeInstance attribute = gainingEntity.getEntityAttribute(LOTREntityNPC.npcAttackDamageExtra); - attribute.setBaseValue(attribute.getBaseValue() + (double)damageBoost); + public void increaseDamageGain(EntityLivingBase entity) { + IAttributeInstance attr = entity.getEntityAttribute(LOTREntityNPC.npcAttackDamageExtra); + Utilities.increment_modifier(attr, LEVEL_DAMAGE_UUID, "levelup_damage", DAMAGE_PER_UPGRADE, 0); } - public void increaseMovementGain(EntityLivingBase gainingEntity) { - float movementBoost = 0.0025f; - IAttributeInstance attribute = gainingEntity.getEntityAttribute(SharedMonsterAttributes.movementSpeed); - attribute.setBaseValue(attribute.getBaseValue() + (double)movementBoost); + public void increaseMovementGain(EntityLivingBase entity) { + IAttributeInstance attr = entity.getEntityAttribute(SharedMonsterAttributes.movementSpeed); + Utilities.increment_modifier(attr, LEVEL_MOVEMENT_UUID, "levelup_speed", SPEED_PER_UPGRADE, 0); } - public void increaseKnockbackGain(EntityLivingBase gainingEntity) { - float kbResBoost = 0.1f; - IAttributeInstance attribute = gainingEntity.getEntityAttribute(SharedMonsterAttributes.knockbackResistance); - attribute.setBaseValue(attribute.getBaseValue() + (double)kbResBoost); - } + + /** * @author @@ -151,4 +153,25 @@ public class MixinLOTRHiredNPCInfo { world.spawnEntityInWorld((Entity)firework); } + @Shadow + public void sendClientPacket(boolean shouldOpenGui) {} + + @Overwrite(remap = false) + public void ready() { + this.canMove = true; + if (CinderLoE_Config.unit_conversion_enabled) convert_stats(); + sendClientPacket(false); + } + + public void convert_stats() { + LOTREntityNPC entity = this.theEntity; + Utilities.reset_attributes(entity); + int level = entity.hiredNPCInfo.xpLevel; + int health_upgrades = (int)Math.ceil(level/3); + int damage_upgrades = (int)Math.ceil((level-health_upgrades)/2); + int speed_upgrades = level-health_upgrades-damage_upgrades; + Utilities.set_modifier(entity.getEntityAttribute(SharedMonsterAttributes.maxHealth), LEVEL_HEALTH_UUID, "levelup_health", HEALTH_PER_UPGRADE*health_upgrades, 0); + Utilities.set_modifier(entity.getEntityAttribute(LOTREntityNPC.npcAttackDamageExtra), LEVEL_DAMAGE_UUID, "levelup_damage", DAMAGE_PER_UPGRADE*damage_upgrades, 0); + Utilities.set_modifier(entity.getEntityAttribute(SharedMonsterAttributes.movementSpeed), LEVEL_MOVEMENT_UUID, "levelup_speed", SPEED_PER_UPGRADE*speed_upgrades, 0); + } } diff --git a/src/main/java/com/zivilon/cinder_loe/util/Utilities.java b/src/main/java/com/zivilon/cinder_loe/util/Utilities.java index d6602e8..61bfaa6 100644 --- a/src/main/java/com/zivilon/cinder_loe/util/Utilities.java +++ b/src/main/java/com/zivilon/cinder_loe/util/Utilities.java @@ -28,6 +28,8 @@ import lotr.common.item.LOTRItemArmor; import lotr.common.item.LOTRMaterial; import lotr.common.enchant.LOTREnchantment; import lotr.common.enchant.LOTREnchantmentHelper; +import lotr.common.entity.npc.LOTREntityNPC; + import net.minecraft.client.renderer.Tessellator; import net.minecraft.enchantment.EnchantmentHelper; @@ -35,6 +37,11 @@ import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.SharedMonsterAttributes; +import net.minecraft.entity.ai.attributes.AttributeModifier; +import net.minecraft.entity.ai.attributes.BaseAttributeMap; +import net.minecraft.entity.ai.attributes.IAttribute; +import net.minecraft.entity.ai.attributes.IAttributeInstance; +import net.minecraft.entity.ai.attributes.ServersideAttributeMap; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.item.ItemArmor; @@ -338,4 +345,61 @@ public class Utilities { } return null; } + + public static void reset_attributes(LOTREntityNPC entity) { + try { + BaseAttributeMap new_map = new ServersideAttributeMap(); + + Field attr_map_field; + try { + attr_map_field = EntityLivingBase.class.getDeclaredField("attributeMap"); + } catch (NoSuchFieldException e) { + attr_map_field = EntityLivingBase.class.getDeclaredField("field_110155_d"); + } + attr_map_field.setAccessible(true); + attr_map_field.set(entity, new_map); + + Method method = find_most_specific_method(entity.getClass(), "applyEntityAttributes", "func_110147_ax"); + if (method == null) throw new RuntimeException("No applyEntityAttributes method found"); + method.setAccessible(true); + method.invoke(entity); + + + } catch (Exception e) { + throw new RuntimeException("Failed to reset attribute map", e); + } + } + public static void increment_modifier(IAttributeInstance attr, UUID uuid, String name, double amount, int op) { + AttributeModifier existing = attr.getModifier(uuid); + double new_amount = amount; + + if (existing != null) { + new_amount += existing.getAmount(); + attr.removeModifier(existing); + } + + AttributeModifier updated = new AttributeModifier(uuid, name, new_amount, op); + attr.applyModifier(updated); + } + public static void set_modifier(IAttributeInstance attr, UUID uuid, String name, double amount, int op) { + AttributeModifier existing = attr.getModifier(uuid); + if (existing != null) { + attr.removeModifier(existing); + } + + AttributeModifier modifier = new AttributeModifier(uuid, name, amount, op); + attr.applyModifier(modifier); + } + public static Method find_most_specific_method(Class cls, String... names) { + while (cls != null) { + for (String name : names) { + try { + Method m = cls.getDeclaredMethod(name); + if (m.getParameterCount() == 0) return m; + } catch (NoSuchMethodException ignored) {} + } + cls = cls.getSuperclass(); + } + return null; + } }