diff --git a/src/main/java/noppes/npcs/DataAI.java b/src/main/java/noppes/npcs/DataAI.java index 7f4a7aa9a..cd0a801b6 100644 --- a/src/main/java/noppes/npcs/DataAI.java +++ b/src/main/java/noppes/npcs/DataAI.java @@ -3,10 +3,7 @@ package noppes.npcs; import net.minecraft.entity.SharedMonsterAttributes; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.util.MathHelper; -import noppes.npcs.constants.EnumAnimation; -import noppes.npcs.constants.EnumMovingType; -import noppes.npcs.constants.EnumNavType; -import noppes.npcs.constants.EnumStandingType; +import noppes.npcs.constants.*; import noppes.npcs.entity.EntityNPCInterface; import java.util.ArrayList; @@ -30,6 +27,7 @@ public class DataAI { public boolean canSprint = false; public boolean stopAndInteract = true; public EnumNavType tacticalVariant = EnumNavType.Default; + public EnumCombatPolicy combatPolicy = EnumCombatPolicy.Flip; public int useRangeMelee = 0; public int tacticalRadius = 8; @@ -88,6 +86,7 @@ public class DataAI { standingType = EnumStandingType.values()[compound.getInteger("StandingState") % EnumStandingType.values().length]; movingType = EnumMovingType.values()[compound.getInteger("MovingState") % EnumMovingType.values().length]; tacticalVariant = EnumNavType.values()[compound.getInteger("TacticalVariant") % EnumNavType.values().length]; + combatPolicy = EnumCombatPolicy.values()[compound.getInteger("CombatPolicy") % EnumCombatPolicy.values().length]; orientation = compound.getInteger("Orientation"); bodyOffsetY = compound.getFloat("PositionOffsetY"); @@ -155,6 +154,7 @@ public class DataAI { compound.setInteger("StandingState", standingType.ordinal()); compound.setInteger("MovingState", movingType.ordinal()); compound.setInteger("TacticalVariant", tacticalVariant.ordinal()); + compound.setInteger("CombatPolicy", combatPolicy.ordinal()); compound.setInteger("Orientation", orientation); compound.setFloat("PositionOffsetX", bodyOffsetX); diff --git a/src/main/java/noppes/npcs/NoppesUtilServer.java b/src/main/java/noppes/npcs/NoppesUtilServer.java index bb7af2bbf..beabdcd84 100644 --- a/src/main/java/noppes/npcs/NoppesUtilServer.java +++ b/src/main/java/noppes/npcs/NoppesUtilServer.java @@ -855,7 +855,7 @@ public class NoppesUtilServer { return MinecraftServer.getServer().getConfigurationManager().func_152612_a(playername); } - public static Entity GetDamageSourcee(DamageSource damagesource) { + public static Entity GetDamageSource(DamageSource damagesource) { Entity entity = damagesource.getEntity(); if(entity == null) { entity = damagesource.getSourceOfDamage(); diff --git a/src/main/java/noppes/npcs/ScriptPlayerEventHandler.java b/src/main/java/noppes/npcs/ScriptPlayerEventHandler.java index e392b4ac9..8335a47b3 100644 --- a/src/main/java/noppes/npcs/ScriptPlayerEventHandler.java +++ b/src/main/java/noppes/npcs/ScriptPlayerEventHandler.java @@ -394,7 +394,7 @@ public class ScriptPlayerEventHandler { return; if(event.entityLiving.worldObj instanceof WorldServer) { - Entity source = NoppesUtilServer.GetDamageSourcee(event.source); + Entity source = NoppesUtilServer.GetDamageSource(event.source); PlayerDataScript handler = ScriptController.Instance.playerScripts; if(event.entityLiving instanceof EntityPlayer) { try { @@ -417,7 +417,7 @@ public class ScriptPlayerEventHandler { return; if(event.entityLiving.worldObj instanceof WorldServer) { - Entity source = NoppesUtilServer.GetDamageSourcee(event.source); + Entity source = NoppesUtilServer.GetDamageSource(event.source); PlayerDataScript handler = ScriptController.Instance.playerScripts; if(event.entityLiving instanceof EntityPlayer) { noppes.npcs.scripted.event.PlayerEvent.AttackedEvent pevent = new noppes.npcs.scripted.event.PlayerEvent.AttackedEvent((IPlayer)NpcAPI.Instance().getIEntity((EntityPlayer)event.entityLiving), source, event.ammount, event.source); @@ -437,7 +437,7 @@ public class ScriptPlayerEventHandler { return; if(event.entityLiving.worldObj instanceof WorldServer) { - Entity source = NoppesUtilServer.GetDamageSourcee(event.source); + Entity source = NoppesUtilServer.GetDamageSource(event.source); PlayerDataScript handler = ScriptController.Instance.playerScripts; if(event.entityLiving instanceof EntityPlayer) { noppes.npcs.scripted.event.PlayerEvent.DamagedEvent pevent = new noppes.npcs.scripted.event.PlayerEvent.DamagedEvent((IPlayer)NpcAPI.Instance().getIEntity((EntityPlayer)event.entityLiving), source, event.ammount, event.source); diff --git a/src/main/java/noppes/npcs/ai/CombatHandler.java b/src/main/java/noppes/npcs/ai/CombatHandler.java index 5063977ba..9cd564e8f 100644 --- a/src/main/java/noppes/npcs/ai/CombatHandler.java +++ b/src/main/java/noppes/npcs/ai/CombatHandler.java @@ -1,6 +1,7 @@ package noppes.npcs.ai; import java.util.HashMap; +import java.util.LinkedList; import java.util.Map; import net.minecraft.entity.Entity; @@ -11,8 +12,9 @@ import noppes.npcs.NoppesUtilServer; import noppes.npcs.entity.EntityNPCInterface; public class CombatHandler { - + private Map aggressors = new HashMap(); + private Map> recentDamages = new HashMap<>(); private EntityNPCInterface npc; private long startTime = 0; private int combatResetTimer = 0; @@ -47,18 +49,28 @@ public class CombatHandler { return isValidTarget(npc.getAttackTarget()); } - public void damage(DamageSource source, float damageAmount){ + public void damage(DamageSource source, float damageAmount) { combatResetTimer = 0; - Entity e = NoppesUtilServer.GetDamageSourcee(source); - System.out.println(e); - if(e instanceof EntityLivingBase){ + Entity e = NoppesUtilServer.GetDamageSource(source); + + if (e instanceof EntityLivingBase) { EntityLivingBase el = (EntityLivingBase) e; + + // Update recent damages + LinkedList recentDamageList = recentDamages.computeIfAbsent(el, k -> new LinkedList<>()); + recentDamageList.addLast(damageAmount); + if (recentDamageList.size() > 15) { + recentDamageList.removeFirst(); + } + + // Update total aggressor damage Float f = aggressors.get(el); - if(f == null) + if (f == null) { f = 0f; + } aggressors.put(el, f + damageAmount); } - } + } public void start(){ combatResetTimer = 0; @@ -68,8 +80,10 @@ public class CombatHandler { public void reset(){ combatResetTimer = 0; + startTime = 0; aggressors.clear(); - npc.setBoolFlag(true, 4); + recentDamages.clear(); + npc.setBoolFlag(false, 4); } public boolean checkTarget() { @@ -103,4 +117,19 @@ public class CombatHandler { return npc.isInRange(target, npc.stats.aggroRange); } + public float calculateThreatLevel(EntityLivingBase entity) { + float threatLevel = 0.0f; + LinkedList recentDamageList = recentDamages.get(entity); + + if (recentDamageList != null) { + long currentTime = npc.worldObj.getWorldInfo().getWorldTotalTime(); + for (Float damage : recentDamageList) { + float decayFactor = Math.max(0, 1 - (float) startTime / currentTime); + + threatLevel += damage * decayFactor; + } + } + + return threatLevel; + } } diff --git a/src/main/java/noppes/npcs/client/gui/mainmenu/GuiNpcAI.java b/src/main/java/noppes/npcs/client/gui/mainmenu/GuiNpcAI.java index 6e93f7d1d..da1325d49 100644 --- a/src/main/java/noppes/npcs/client/gui/mainmenu/GuiNpcAI.java +++ b/src/main/java/noppes/npcs/client/gui/mainmenu/GuiNpcAI.java @@ -6,6 +6,7 @@ import noppes.npcs.DataAI; import noppes.npcs.client.Client; import noppes.npcs.client.gui.SubGuiNpcMovement; import noppes.npcs.client.gui.util.*; +import noppes.npcs.constants.EnumCombatPolicy; import noppes.npcs.constants.EnumNavType; import noppes.npcs.constants.EnumPacketServer; import noppes.npcs.entity.EntityNPCInterface; @@ -68,19 +69,22 @@ public class GuiNpcAI extends GuiNPCInterface2 implements ITextfieldListener, IG getTextField(3).integersOnly = true; getTextField(3).setMinMaxDefault(1, npc.stats.aggroRange, 5); } + addLabel(new GuiNpcLabel(25,"ai.combatpolicy", guiLeft + 150, guiTop + 165)); + addButton(new GuiNpcButton(25 ,guiLeft + 230, guiTop + 160, 60, 20, EnumCombatPolicy.names(), ai.combatPolicy.ordinal())); - addLabel(new GuiNpcLabel(22,"ai.cobwebAffected", guiLeft + 150, guiTop + 165)); - addButton(new GuiNpcButton(22 ,guiLeft + 230, guiTop + 160, 60, 20, new String[]{"gui.no", "gui.yes"}, npc.ai.ignoreCobweb ? 0:1)); - - getButton(17).setEnabled(this.ai.onAttack == 0); + getButton(17).setEnabled(this.ai.onAttack == 0); getButton(15).setEnabled(this.ai.onAttack == 0); + getButton(25).setEnabled(this.ai.onAttack == 0); getButton(13).setEnabled(this.npc.inventory.getProjectile() != null); getButton(14).setEnabled(this.npc.inventory.getProjectile() != null); - getButton(10).setEnabled(ai.tacticalVariant != EnumNavType.Stalk || ai.tacticalVariant != EnumNavType.None); + getButton(10).setEnabled(ai.tacticalVariant != EnumNavType.Stalk && ai.tacticalVariant != EnumNavType.None); addLabel(new GuiNpcLabel(2,"ai.movement", guiLeft + 4, guiTop + 165)); - addButton(new GuiNpcButton(2, guiLeft + 86, guiTop + 160, 60, 20, "selectServer.edit")); - } + addButton(new GuiNpcButton(2, guiLeft + 86, guiTop + 160, 60, 20, "selectServer.edit")); + + addLabel(new GuiNpcLabel(22,"ai.cobwebAffected", guiLeft + 4, guiTop + 190)); + addButton(new GuiNpcButton(22 ,guiLeft + 86, guiTop + 185, 60, 20, new String[]{"gui.no", "gui.yes"}, npc.ai.ignoreCobweb ? 0:1)); + } @Override public void unFocused(GuiNpcTextField textfield){ @@ -142,6 +146,9 @@ public class GuiNpcAI extends GuiNPCInterface2 implements ITextfieldListener, IG else if (button.id == 22) { ai.ignoreCobweb = (button.getValue() == 0); } + else if (button.id == 25) { + ai.combatPolicy = EnumCombatPolicy.values()[button.getValue()]; + } } @Override diff --git a/src/main/java/noppes/npcs/constants/EnumCombatPolicy.java b/src/main/java/noppes/npcs/constants/EnumCombatPolicy.java new file mode 100644 index 000000000..f94f8faaa --- /dev/null +++ b/src/main/java/noppes/npcs/constants/EnumCombatPolicy.java @@ -0,0 +1,23 @@ +package noppes.npcs.constants; + +import java.util.ArrayList; + +public enum EnumCombatPolicy { + Flip("aicombat.flip"), + Brute("aicombat.brute"), + Stubborn("aicombat.stubborn"), + Tactical("aicombat.tactical"); + + public final String name; + + EnumCombatPolicy(String name){ + this.name = name; + } + public static String[] names(){ + ArrayList list = new ArrayList(); + for(EnumCombatPolicy e : values()) + list.add(e.name); + + return list.toArray(new String[list.size()]); + } +} diff --git a/src/main/java/noppes/npcs/entity/EntityNPCInterface.java b/src/main/java/noppes/npcs/entity/EntityNPCInterface.java index ea473122d..316ce4b8a 100644 --- a/src/main/java/noppes/npcs/entity/EntityNPCInterface.java +++ b/src/main/java/noppes/npcs/entity/EntityNPCInterface.java @@ -313,7 +313,6 @@ public abstract class EntityNPCInterface extends EntityCreature implements IEnti updateTasks(); updateHitbox(); } - // setBoolFlag(this.getAttackTarget() != null, 4); setBoolFlag(!getNavigator().noPath(), 1); setBoolFlag(isInteracting(), 2); combatHandler.update(); @@ -496,18 +495,12 @@ public abstract class EntityNPCInterface extends EntityCreature implements IEnti if((float)this.hurtResistantTime > (float)this.maxHurtResistantTime / 2.0F && i <= this.lastDamage) return false; - Entity entity = damagesource.getEntity(); - + Entity entity = NoppesUtilServer.GetDamageSource(damagesource); EntityLivingBase attackingEntity = null; if (entity instanceof EntityLivingBase) attackingEntity = (EntityLivingBase) entity; - if ((entity instanceof EntityArrow) && ((EntityArrow) entity).shootingEntity instanceof EntityLivingBase) - attackingEntity = (EntityLivingBase) ((EntityArrow) entity).shootingEntity; - else if ((entity instanceof EntityThrowable)) - attackingEntity = ((EntityThrowable) entity).getThrower(); - if(attackingEntity != null && attackingEntity == getOwner()) return false; else if (attackingEntity instanceof EntityNPCInterface){ @@ -532,13 +525,28 @@ public abstract class EntityNPCInterface extends EntityCreature implements IEnti try{ if (isAttacking()){ - if(getAttackTarget() != null && attackingEntity != null && this.getDistanceSqToEntity(getAttackTarget()) > this.getDistanceSqToEntity(attackingEntity)){ - setAttackTarget(attackingEntity); + if (getAttackTarget() != null) { + if (ai.combatPolicy != EnumCombatPolicy.Brute) { + boolean closerTargetFound = this.getDistanceSqToEntity(getAttackTarget()) > this.getDistanceSqToEntity(attackingEntity); + switch (ai.combatPolicy) { + case Flip: + if (closerTargetFound) { + setAttackTarget(attackingEntity); + } + break; + case Stubborn: + if (closerTargetFound && shouldChangeTarget(5.0)) { + setAttackTarget(attackingEntity); + } + break; + default: + break; + } + } } return super.attackEntityFrom(damagesource, i); } - - if (i > 0) { + if (i > 0){ List inRange = worldObj.getEntitiesWithinAABB(EntityNPCInterface.class, this.boundingBox.expand(32D, 16D, 32D)); for (EntityNPCInterface npc : inRange) { if (npc.isKilled() || !npc.advanced.defendFaction || npc.faction.id != faction.id) @@ -1919,4 +1927,10 @@ public abstract class EntityNPCInterface extends EntityCreature implements IEnti } protected void updateLeashedState(){} + + private boolean shouldChangeTarget(double chance) { + // Assuming randomNum is a random number between 0 and 100 + double randomNum = Math.random() * 100; // Generates a random number between 0 and 100 + return randomNum <= chance; + } } diff --git a/src/main/java/noppes/npcs/scripted/entity/ScriptNpc.java b/src/main/java/noppes/npcs/scripted/entity/ScriptNpc.java index da55bca12..925f6db7e 100644 --- a/src/main/java/noppes/npcs/scripted/entity/ScriptNpc.java +++ b/src/main/java/noppes/npcs/scripted/entity/ScriptNpc.java @@ -726,6 +726,34 @@ public class ScriptNpc extends ScriptLiving imp return npc.ai.tacticalVariant.name(); } + public void setCombatPolicy(int variant){ + if(variant > EnumCombatPolicy.values().length-1 || variant < 0) + return; + + npc.ai.combatPolicy = EnumCombatPolicy.values()[variant]; + } + + public int getCombatPolicy(){ + return npc.ai.combatPolicy.ordinal(); + } + + public void setCombatPolicy(String variant){ + boolean found = false; + for(String s : EnumCombatPolicy.names()){ + if(s.equals(variant)) + found = true; + } + + if(!found) + return; + + npc.ai.combatPolicy = EnumCombatPolicy.valueOf(variant); + } + + public String getCombatPolicyName(){ + return npc.ai.combatPolicy.name(); + } + public void setTacticalRadius(int tacticalRadius){ if(tacticalRadius < 0) tacticalRadius = 0;