From 5103577f1d3c23abb76cee5568dd4bd96c94838d Mon Sep 17 00:00:00 2001 From: George Paton Date: Thu, 24 Oct 2024 15:17:31 +1100 Subject: [PATCH] Gun firing AI improvements and skeletons are only given guns when soot is high enough, higher soot adds more powerful weapons --- .../hbm/entity/mob/ai/EntityAIFireGun.java | 162 ++++++++++++------ .../java/com/hbm/main/ModEventHandler.java | 66 ++++--- 2 files changed, 153 insertions(+), 75 deletions(-) diff --git a/src/main/java/com/hbm/entity/mob/ai/EntityAIFireGun.java b/src/main/java/com/hbm/entity/mob/ai/EntityAIFireGun.java index 4793802a0..c9729c782 100644 --- a/src/main/java/com/hbm/entity/mob/ai/EntityAIFireGun.java +++ b/src/main/java/com/hbm/entity/mob/ai/EntityAIFireGun.java @@ -13,72 +13,124 @@ import net.minecraft.item.ItemStack; public class EntityAIFireGun extends EntityAIBase { - private final EntityLiving host; + private final EntityLiving host; - private double attackMoveSpeed = 1.0D; + private double attackMoveSpeed = 1.0D; // how fast we move while in this state + private double maxRange = 20; // how far our target can be before we stop shooting + private int burstTime = 6; // maximum number of ticks in a burst (for automatic weapons) + private int minWait = 10; // minimum number of ticks to wait between bursts/shots + private int maxWait = 60; // maximum number of ticks to wait between bursts/shots + private float inaccuracy = 30; // how many degrees of inaccuracy does the AI have - private int attackTimer = 0; - private double maxRange = 20; - - public EntityAIFireGun(EntityLiving host) { - this.host = host; - } + // state timers + private int attackTimer = 0; + private FireState state = FireState.IDLE; + private int stateTimer = 0; - @Override - public boolean shouldExecute() { - return host.getAttackTarget() != null && getYerGun() != null; - } + private static enum FireState { + IDLE, + WAIT, + FIRING, + RELOADING, + } + + public EntityAIFireGun(EntityLiving host) { + this.host = host; + } - @Override - public void updateTask() { - EntityLivingBase target = host.getAttackTarget(); - ItemStack stack = host.getHeldItem(); - ItemGunBaseNT gun = getYerGun(); + @Override + public boolean shouldExecute() { + return host.getAttackTarget() != null && getYerGun() != null; + } - gun.onUpdate(stack, host.worldObj, host, 0, true); + @Override + public void updateTask() { + EntityLivingBase target = host.getAttackTarget(); + ItemStack stack = host.getHeldItem(); + ItemGunBaseNT gun = getYerGun(); - double distanceToTargetSquared = host.getDistanceSq(target.posX, target.posY, target.posZ); - boolean canSeeTarget = host.getEntitySenses().canSee(target); + gun.onUpdate(stack, host.worldObj, host, 0, true); - if(canSeeTarget) { - attackTimer++; - } else { - attackTimer = 0; - } + double distanceToTargetSquared = host.getDistanceSq(target.posX, target.posY, target.posZ); + boolean canSeeTarget = host.getEntitySenses().canSee(target); - if(distanceToTargetSquared < maxRange * maxRange && attackTimer > 20) { - host.getNavigator().clearPathEntity(); - } else { - host.getNavigator().tryMoveToEntityLiving(target, attackMoveSpeed); - } + if(canSeeTarget) { + attackTimer++; + } else { + attackTimer = 0; + } - host.getLookHelper().setLookPositionWithEntity(target, 30.0F, 30.0F); + if(distanceToTargetSquared < maxRange * maxRange && attackTimer > 20) { + host.getNavigator().clearPathEntity(); + } else { + host.getNavigator().tryMoveToEntityLiving(target, attackMoveSpeed); + } - if(canSeeTarget && distanceToTargetSquared < maxRange * maxRange) { - GunConfig config = gun.getConfig(stack, 0); - Receiver rec = config.getReceivers(stack)[0]; - if(rec.getMagazine(stack).getAmount(stack) <= 0) { - gun.handleKeybind(host, null, stack, EnumKeybind.GUN_PRIMARY, false); - gun.handleKeybind(host, null, stack, EnumKeybind.RELOAD, true); - } else if(ItemGunBaseNT.getState(stack, 0) == GunState.IDLE) { - gun.handleKeybind(host, null, stack, EnumKeybind.GUN_PRIMARY, true); - gun.handleKeybind(host, null, stack, EnumKeybind.RELOAD, false); - } else { - gun.handleKeybind(host, null, stack, EnumKeybind.GUN_PRIMARY, false); - gun.handleKeybind(host, null, stack, EnumKeybind.RELOAD, false); - } - } else { - gun.handleKeybind(host, null, stack, EnumKeybind.GUN_PRIMARY, false); - gun.handleKeybind(host, null, stack, EnumKeybind.RELOAD, false); - } - } + host.getLookHelper().setLookPositionWithEntity(target, 30.0F, 30.0F); - public ItemGunBaseNT getYerGun() { - ItemStack stack = host.getHeldItem(); + stateTimer--; + if(stateTimer < 0) { + stateTimer = 0; - if(stack == null || !(stack.getItem() instanceof ItemGunBaseNT)) return null; + if(state == FireState.WAIT) { + updateState(FireState.IDLE, 0, gun, stack); + } else if(state != FireState.IDLE) { + updateState(FireState.WAIT, host.worldObj.rand.nextInt(maxWait - minWait) + minWait, gun, stack); + } + } else if(state == FireState.FIRING) { + // Keep the trigger held throughout the duration of firing + updateKeybind(gun, stack, EnumKeybind.GUN_PRIMARY); + } - return (ItemGunBaseNT) stack.getItem(); - } - + if(canSeeTarget && distanceToTargetSquared < maxRange * maxRange) { + if(state == FireState.IDLE) { + GunConfig config = gun.getConfig(stack, 0); + Receiver rec = config.getReceivers(stack)[0]; + if(rec.getMagazine(stack).getAmount(stack) <= 0) { + updateState(FireState.RELOADING, 40, gun, stack); + } else if(ItemGunBaseNT.getState(stack, 0) == GunState.IDLE) { + updateState(FireState.FIRING, host.worldObj.rand.nextInt(burstTime), gun, stack); + } + } + } + } + + private void updateState(FireState toState, int time, ItemGunBaseNT gun, ItemStack stack) { + state = toState; + stateTimer = time; + + switch(state) { + case FIRING: updateKeybind(gun, stack, EnumKeybind.GUN_PRIMARY); + case RELOADING: updateKeybind(gun, stack, EnumKeybind.RELOAD); + default: clearKeybinds(gun, stack); break; + } + } + + private void clearKeybinds(ItemGunBaseNT gun, ItemStack stack) { + updateKeybind(gun, stack, null); + } + + private void updateKeybind(ItemGunBaseNT gun, ItemStack stack, EnumKeybind bind) { + // Turn body to face firing direction, since the gun is attached to that, not the head + // Also apply accuracy debuff just before firing + if(bind != null && bind != EnumKeybind.RELOAD) { + host.rotationYawHead += (host.worldObj.rand.nextFloat() - 0.5F) * 2 * inaccuracy; + host.rotationPitch += (host.worldObj.rand.nextFloat() - 0.5F) * 2 * inaccuracy; + host.rotationYaw = host.rotationYawHead; + } + + gun.handleKeybind(host, null, stack, EnumKeybind.GUN_PRIMARY, bind == EnumKeybind.GUN_PRIMARY); + gun.handleKeybind(host, null, stack, EnumKeybind.GUN_SECONDARY, bind == EnumKeybind.GUN_SECONDARY); + gun.handleKeybind(host, null, stack, EnumKeybind.GUN_TERTIARY, bind == EnumKeybind.GUN_TERTIARY); + gun.handleKeybind(host, null, stack, EnumKeybind.RELOAD, bind == EnumKeybind.RELOAD); + } + + public ItemGunBaseNT getYerGun() { + ItemStack stack = host.getHeldItem(); + + if(stack == null || !(stack.getItem() instanceof ItemGunBaseNT)) return null; + + return (ItemGunBaseNT) stack.getItem(); + } + } diff --git a/src/main/java/com/hbm/main/ModEventHandler.java b/src/main/java/com/hbm/main/ModEventHandler.java index 507a1dd55..b75df6509 100644 --- a/src/main/java/com/hbm/main/ModEventHandler.java +++ b/src/main/java/com/hbm/main/ModEventHandler.java @@ -54,6 +54,7 @@ import com.hbm.items.armor.ItemModShackles; import com.hbm.items.food.ItemConserve.EnumFoodType; import com.hbm.items.tool.ItemGuideBook.BookType; import com.hbm.items.weapon.ItemGunBase; +import com.hbm.items.weapon.sedna.ItemGunBaseNT; import com.hbm.lib.HbmCollection; import com.hbm.lib.ModDamageSource; import com.hbm.lib.RefStrings; @@ -65,14 +66,7 @@ import com.hbm.saveddata.AuxSavedData; import com.hbm.tileentity.machine.TileEntityMachineRadarNT; import com.hbm.tileentity.network.RTTYSystem; import com.hbm.tileentity.network.RequestNetwork; -import com.hbm.util.AchievementHandler; -import com.hbm.util.ArmorRegistry; -import com.hbm.util.ArmorUtil; -import com.hbm.util.ContaminationUtil; -import com.hbm.util.EnchantmentUtil; -import com.hbm.util.EnumUtil; -import com.hbm.util.InventoryUtil; -import com.hbm.util.ShadyUtil; +import com.hbm.util.*; import com.hbm.util.ArmorRegistry.HazardClass; import com.hbm.world.generator.TimedGenerator; @@ -89,8 +83,10 @@ import net.minecraft.block.Block; import net.minecraft.enchantment.Enchantment; import net.minecraft.enchantment.EnchantmentHelper; 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.EntityAITasks; import net.minecraft.entity.ai.attributes.AttributeModifier; import net.minecraft.entity.item.EntityItem; import net.minecraft.entity.monster.EntityCaveSpider; @@ -116,13 +112,7 @@ import net.minecraft.nbt.NBTTagCompound; import net.minecraft.potion.Potion; import net.minecraft.potion.PotionEffect; import net.minecraft.tileentity.TileEntitySign; -import net.minecraft.util.ChatComponentText; -import net.minecraft.util.ChatStyle; -import net.minecraft.util.EntityDamageSource; -import net.minecraft.util.EnumChatFormatting; -import net.minecraft.util.FoodStats; -import net.minecraft.util.MathHelper; -import net.minecraft.util.Vec3; +import net.minecraft.util.*; import net.minecraft.world.World; import net.minecraftforge.common.util.FakePlayer; import net.minecraftforge.common.util.ForgeDirection; @@ -423,12 +413,48 @@ public class ModEventHandler { } if(rand.nextInt(64) == 0) entity.setCurrentItemOrArmor(3, new ItemStack(ModItems.steel_plate, 1, world.rand.nextInt(ModItems.steel_plate.getMaxDamage()))); - - // Give them a gun and the AI task to fire it - entity.setCurrentItemOrArmor(0, new ItemStack(ModItems.gun_am180)); + + float soot = PollutionHandler.getPollution(entity.worldObj, MathHelper.floor_double(event.x), MathHelper.floor_double(event.y), MathHelper.floor_double(event.z), PollutionType.SOOT); + ItemStack bowReplacement = getSkelegun(soot, entity.worldObj.rand); + if(bowReplacement != null) { + entity.setCurrentItemOrArmor(0, bowReplacement); + addFireTask((EntityLiving) entity); + } + } + } - EntitySkeleton skeleton = (EntitySkeleton) entity; - skeleton.tasks.addTask(3, new EntityAIFireGun(skeleton)); + private static ItemStack getSkelegun(float soot, Random rand) { + if(rand.nextDouble() > Math.log(soot) * 0.25) return null; + + ArrayList pool = new ArrayList(); + pool.add(new WeightedRandomObject(new ItemStack(ModItems.gun_heavy_revolver), 12)); + + if(soot > 8) pool.add(new WeightedRandomObject(new ItemStack(ModItems.gun_am180), 4)); + + WeightedRandomObject selected = (WeightedRandomObject) WeightedRandom.getRandomItem(rand, pool); + + return selected.asStack(); + } + + // these fucking tasks keep stacking on top of themselves + private static void addFireTask(EntityLiving entity) { + for(Object entry : entity.tasks.taskEntries) { + EntityAITasks.EntityAITaskEntry task = (EntityAITasks.EntityAITaskEntry) entry; + if(task.action instanceof EntityAIFireGun) return; + } + + entity.tasks.addTask(3, new EntityAIFireGun(entity)); + } + + @SubscribeEvent + public void addAITasks(EntityJoinWorldEvent event) { + if(event.world.isRemote || !(event.entity instanceof EntityLiving)) return; + + EntityLiving living = (EntityLiving) event.entity; + ItemStack held = living.getHeldItem(); + + if(held != null && held.getItem() instanceof ItemGunBaseNT) { + addFireTask(living); } }