split entity ai to server #1

This commit is contained in:
Sen 2025-05-10 00:02:02 +02:00
parent 6e5c9e842e
commit 02c0bfcbf6
10 changed files with 706 additions and 634 deletions

View file

@ -0,0 +1,224 @@
package server.ai;
import java.util.function.Predicate;
import common.ai.AIRangedAttack;
import common.ai.EntityAIAttackOnCollide;
import common.ai.EntityAIAvoidEntity;
import common.ai.EntityAIHurtByTarget;
import common.ai.EntityAILookAtTalkingPlayer;
import common.ai.EntityAINagPlayer;
import common.ai.EntityAINearestAttackableTarget;
import common.ai.EntityAINpcInteract;
import common.ai.EntityAINpcMate;
import common.ai.EntityAIOpenDoor;
import common.ai.EntityAIPlay;
import common.ai.EntityAISwimming;
import common.ai.EntityAIWander;
import common.ai.EntityAIWatchClosest;
import common.ai.EntityAIWatchClosest2;
import common.entity.npc.EntityNPC;
import common.entity.types.EntityLiving;
import common.item.ItemStack;
import common.pathfinding.PathNavigateGround;
public class EntityNPCNode extends EntityNode {
protected final EntityAIAttackOnCollide aiMelee;
protected final AIRangedAttack aiRanged;
private boolean fleeing;
public EntityNPCNode(EntityNPC entity) {
super(entity);
this.aiMelee = new EntityAIAttackOnCollide(entity, EntityLiving.class, 1.0D, true, true) {
public boolean shouldExecute()
{
return !EntityNPCNode.this.fleeing && super.shouldExecute();
}
public boolean continueExecuting()
{
return !EntityNPCNode.this.fleeing && super.shouldExecute();
}
};
this.aiRanged = new AIRangedAttack(entity, 1.0D, entity.getAttackSpeed(), entity.getAttackSpeed() * 3, 15.0F) {
// public void startExecuting()
// {
// super.startExecuting();
// EntityNPC.this.setUsingItem(true);
// }
//
// public void resetTask()
// {
// super.resetTask();
// EntityNPC.this.setUsingItem(false);
// }
public boolean shouldExecute()
{
return !EntityNPCNode.this.fleeing && super.shouldExecute();
}
public boolean continueExecuting()
{
return !EntityNPCNode.this.fleeing && super.shouldExecute();
}
};
}
public EntityNPC getEntity() {
return (EntityNPC)this.entity;
}
protected void registerTasks() {
if(this.getNavigator() instanceof PathNavigateGround) {
((PathNavigateGround)this.getNavigator()).setBreakDoors(true);
((PathNavigateGround)this.getNavigator()).setAvoidsWater(true);
}
this.tasks.addTask(0, new EntityAISwimming(this.getEntity()));
// this.tasks.addTask(1, new EntityAIAttackOnCollide(this, EntityNPC.class, 0.6D, true, true));
// this.tasks.addTask(1, new EntityAIAttackOnCollide(this, EntityLivingBase.class, 0.6D, true, true));
this.tasks.addTask(1, new EntityAINpcMate(this.getEntity()));
this.tasks.addTask(2, new EntityAIAvoidEntity(this.getEntity(), EntityLiving.class, new Predicate<EntityLiving>() {
public boolean test(EntityLiving entity) {
return entity != EntityNPCNode.this.entity && EntityNPC.this.shouldFlee(entity);
}
}, 8.0F, 1.1D, 1.1D) {
{
this.setMutexBits(0);
}
public void startExecuting() {
EntityNPCNode.this.setAttackTarget(null);
EntityNPCNode.this.fleeing = true;
}
public boolean continueExecuting() {
return false;
}
public void updateTask()
{
}
// public void resetTask() {
// super.resetTask();
// EntityNPC.this.isFleeing = false;
// }
});
this.tasks.addTask(3, new EntityAIAvoidEntity(this, EntityLiving.class, new Predicate<EntityLiving>() {
public boolean test(EntityLiving entity) {
return entity != EntityNPC.this && EntityNPC.this.shouldFlee(entity);
}
}, 8.0F, 1.1D, 1.1D) {
public void startExecuting() {
super.startExecuting();
EntityNPC.this.setAttackTarget(null);
EntityNPC.this.fleeing = true;
}
public void resetTask() {
super.resetTask();
EntityNPC.this.fleeing = false;
}
});
// this.tasks.addTask(2, new EntityAITempt(this, 1.0D, Items.golden_apple, false));
this.tasks.addTask(4, new EntityAIOpenDoor(this, true));
this.tasks.addTask(5, new EntityAINagPlayer(this));
this.tasks.addTask(5, new EntityAILookAtTalkingPlayer(this));
this.tasks.addTask(6, new EntityAIWatchClosest2(this, null, 3.0F, 1.0F) {
private int sneakTime;
public void updateTask()
{
super.updateTask();
boolean flag = this.closestEntity.isPlayer() && this.closestEntity.isSneaking();
if(this.sneakTime > 0) {
if(--this.sneakTime == 0) {
EntityNPC.this.setSneaking(false);
}
}
else if(EntityNPC.this.getAttackTarget() == null && EntityNPC.this.rand.chance(flag ? 5 : 200)) {
EntityNPC.this.setSneaking(true);
this.sneakTime = EntityNPC.this.rand.range(60, flag ? 160 : 120);
}
else if(EntityNPC.this.getAttackTarget() != null) {
EntityNPC.this.setSneaking(false);
this.sneakTime = 0;
}
}
public void resetTask()
{
super.resetTask();
EntityNPC.this.setSneaking(false);
}
});
this.tasks.addTask(7, new EntityAINpcInteract(this));
this.tasks.addTask(8, new EntityAIWander(this, 1.0D));
this.tasks.addTask(8, new EntityAIPlay(this, 1.1D));
this.tasks.addTask(9, new EntityAIWatchClosest(this, EntityLiving.class, 8.0F));
this.targets.addTask(1, new EntityAIHurtByTarget(this, false, /* EntityNPC.class, */ EntityLiving.class) {
protected boolean isSuitableTarget(EntityLiving entity) {
if(entity != null && entity != EntityNPC.this && EntityNPC.this.shouldFlee(entity)) {
EntityNPC.this.setAttackTarget(null);
EntityNPC.this.fleeing = true;
return false;
}
return entity != null && entity != EntityNPC.this && (!(entity.isPlayer()) || EntityNPC.this.rand.chance(entity.getAttackedBy() == EntityNPC.this ? 2 : 4))
&& EntityNPC.this.canCounter(entity) && super.isSuitableTarget(entity);
}
});
this.targets.addTask(2, new EntityAINearestAttackableTarget(this, EntityLiving.class, 10, true, false, new Predicate<EntityLiving>() {
public boolean test(EntityLiving entity) {
if(entity != null && entity != EntityNPC.this && EntityNPC.this.shouldFlee(entity)) {
EntityNPC.this.setAttackTarget(null);
EntityNPC.this.fleeing = true;
return false;
}
return entity != null && entity != EntityNPC.this && !EntityNPC.this.fleeing && EntityNPC.this.canAmbush(entity) && EntityNPC.this.canAttack(entity);
}
}));
// this.setCanPickUpLoot(true);
if (worldIn != null && !worldIn.client)
{
this.setCombatTask();
}
}
public void updateLeashedState() {
if(!this.player)
super.updateLeashedState();
}
public void sendDeathMessage() {
this.sendDeathMessage(this.player, true);
}
public int getMaxFallHeight()
{
return this.player ? 3 : super.getMaxFallHeight();
}
public void setCombatTask()
{
if(!this.player) {
this.tasks.removeTask(this.aiMelee);
this.tasks.removeTask(this.aiRanged);
ItemStack itemstack = this.getEntity().getHeldItem();
if (this.getEntity().isRangedWeapon(itemstack))
{
this.tasks.addTask(3, this.aiRanged);
}
else
{
this.tasks.addTask(3, this.aiMelee);
}
}
}
public boolean isFleeing() {
return this.fleeing;
}
}

View file

@ -0,0 +1,436 @@
package server.ai;
import java.util.List;
import common.ai.EntityAIBase;
import common.ai.EntityAIMoveTowardsRestriction;
import common.ai.EntityAITasks;
import common.ai.EntityJumpHelper;
import common.ai.EntityLookHelper;
import common.ai.EntityMoveHelper;
import common.ai.EntitySenses;
import common.ai.IEntityNode;
import common.block.Block;
import common.collect.Lists;
import common.color.TextColor;
import common.entity.DamageSource;
import common.entity.Entity;
import common.entity.EntityDamageSource;
import common.entity.item.EntityLeashKnot;
import common.entity.npc.EntityNPC;
import common.entity.types.CombatEntry;
import common.entity.types.EntityBodyHelper;
import common.entity.types.EntityLiving;
import common.entity.types.EntityTameable;
import common.init.Blocks;
import common.item.ItemStack;
import common.nbt.NBTTagCompound;
import common.network.IPlayer;
import common.pathfinding.PathNavigate;
import common.pathfinding.PathNavigateGround;
import common.util.BlockPos;
import common.world.WorldServer;
public abstract class EntityNode implements IEntityNode {
protected final EntityLiving entity;
protected final boolean player;
private final List<CombatEntry> combat = Lists.<CombatEntry>newArrayList();
private EntityLookHelper lookHelper;
protected EntityMoveHelper moveHelper;
protected EntityJumpHelper jumpHelper;
private EntityBodyHelper bodyHelper;
protected PathNavigate navigator;
protected final EntityAITasks tasks;
protected final EntityAITasks targets;
private EntityLiving target;
private EntitySenses senses;
private Runnable tickFunc;
private NBTTagCompound leashTag;
private boolean isMovementAITaskSet;
private EntityAIBase aiBase;
private boolean attacked;
private boolean damaged;
private String blockType;
private int lastDamaged;
public EntityNode(EntityLiving entity) {
this.entity = entity;
this.player = entity.isPlayer();
this.tasks = new EntityAITasks();
this.targets = new EntityAITasks();
this.lookHelper = new EntityLookHelper(entity);
this.moveHelper = new EntityMoveHelper(entity);
this.jumpHelper = new EntityJumpHelper(entity);
this.bodyHelper = new EntityBodyHelper(entity);
this.navigator = this.getNewNavigator();
this.senses = new EntitySenses(entity);
this.aiBase = new EntityAIMoveTowardsRestriction(entity, 1.0D);
this.registerTasks();
}
protected PathNavigate getNewNavigator() {
return new PathNavigateGround(this.entity, this.entity.worldObj);
}
// protected PathNavigate getNewNavigator() { EntityArachnoid
// return new PathNavigateClimber(this.entity, this.entity.worldObj);
// }
protected abstract void registerTasks();
public void setTickFunction(Runnable func) {
this.tickFunc = func;
}
public EntityLookHelper getLookHelper() {
return this.lookHelper;
}
public EntityMoveHelper getMoveHelper() {
return this.moveHelper;
}
public EntityJumpHelper getJumpHelper() {
return this.jumpHelper;
}
public PathNavigate getNavigator() {
return this.navigator;
}
public EntitySenses getEntitySenses() {
return this.senses;
}
public EntityLiving getAttackTarget() {
return this.target;
}
public void setAttackTarget(EntityLiving entitylivingbaseIn) {
this.target = entitylivingbaseIn;
}
public boolean hasPath()
{
return !this.navigator.noPath();
}
public void updateRenderAngles() {
this.bodyHelper.updateRenderAngles();
}
public void update() {
this.senses.clearSensingCache();
this.targets.onUpdateTasks();
this.tasks.onUpdateTasks();
this.navigator.onUpdateNavigation();
if(this.tickFunc != null)
this.tickFunc.run();
this.moveHelper.onUpdateMoveHelper();
this.lookHelper.onUpdateLook();
this.jumpHelper.doJump();
}
public void setLeashTag(NBTTagCompound tag) {
this.leashTag = tag;
}
private void recreateLeash() {
if(this.entity.getLeashed() && this.leashTag != null) {
// if(this.leashTag.hasKey("PlayerName", 8)) {
// String id = this.leashTag.getString("PlayerName");
// if(!id.isEmpty()) {
// for(EntityNPC entitylivingbase : this.worldObj.getEntitiesWithinAABB(EntityNPC.class,
// this.getEntityBoundingBox().expand(10.0D, 10.0D, 10.0D))) {
// if(entitylivingbase.getUser().equals(id)) {
// this.leashedTo = entitylivingbase;
// break;
// }
// }
// }
// }
// else
if(this.leashTag.hasKey("X", 99) && this.leashTag.hasKey("Y", 99) && this.leashTag.hasKey("Z", 99)) {
BlockPos blockpos = new BlockPos(this.leashTag.getInteger("X"), this.leashTag.getInteger("Y"),
this.leashTag.getInteger("Z"));
EntityLeashKnot entityleashknot = EntityLeashKnot.getKnotForPosition(this.entity.worldObj, blockpos);
if(entityleashknot == null) {
entityleashknot = EntityLeashKnot.createKnot(this.entity.worldObj, blockpos);
}
this.entity.setLeashedTo(entityleashknot, false);
}
else {
this.entity.clearLeashed(false, true);
}
}
this.leashTag = null;
}
protected void onUpdateLeashed(float distance)
{
}
// protected void onUpdateLeashed(float distance) EntityHorse
// {
// if (distance > 6.0F && this.entity.isEatingHaystack())
// {
// this.entity.setEatingHaystack(false);
// }
// }
public void updateLeashedState() {
if(this.leashTag != null) {
this.recreateLeash();
}
if(this.entity.getLeashed()) {
if(!this.entity.isEntityAlive()) {
this.entity.clearLeashed(true, true);
}
if(this.entity.getLeashedTo() == null || this.entity.getLeashedTo().dead) {
this.entity.clearLeashed(true, true);
}
}
if (this.entity.getLeashed() && this.entity.getLeashedTo() != null && this.entity.getLeashedTo().worldObj == this.entity.worldObj)
{
Entity entity = this.entity.getLeashedTo();
this.entity.setHomePosAndDistance(new BlockPos((int)entity.posX, (int)entity.posY, (int)entity.posZ), 5);
float f = this.entity.getDistanceToEntity(entity);
if (this.entity instanceof EntityTameable && ((EntityTameable)this.entity).isSitting())
{
if (f > 10.0F)
{
this.entity.clearLeashed(true, true);
}
return;
}
if (!this.isMovementAITaskSet)
{
this.tasks.addTask(2, this.aiBase);
if (this.getNavigator() instanceof PathNavigateGround)
{
((PathNavigateGround)this.getNavigator()).setAvoidsWater(false);
}
this.isMovementAITaskSet = true;
}
this.onUpdateLeashed(f);
if (f > 4.0F)
{
this.getNavigator().tryMoveToEntityLiving(entity, 1.0D);
}
if (f > 6.0F)
{
double d0 = (entity.posX - this.entity.posX) / (double)f;
double d1 = (entity.posY - this.entity.posY) / (double)f;
double d2 = (entity.posZ - this.entity.posZ) / (double)f;
this.entity.motionX += d0 * Math.abs(d0) * 0.4D;
this.entity.motionY += d1 * Math.abs(d1) * 0.4D;
this.entity.motionZ += d2 * Math.abs(d2) * 0.4D;
}
if (f > 10.0F)
{
this.entity.clearLeashed(true, true);
}
}
else if (!this.entity.getLeashed() && this.isMovementAITaskSet)
{
this.isMovementAITaskSet = false;
this.tasks.removeTask(this.aiBase);
if (this.getNavigator() instanceof PathNavigateGround)
{
((PathNavigateGround)this.getNavigator()).setAvoidsWater(true);
}
this.entity.detachHome();
}
}
public void trackDamage(DamageSource source, int amount) {
this.resetCombat();
this.blockType = null;
if(this.entity.isOnLadder()) {
Block block = this.entity.worldObj
.getState(new BlockPos(this.entity.posX, this.entity.getEntityBoundingBox().minY, this.entity.posZ)).getBlock();
if(block == Blocks.ladder)
this.blockType = "von einer Leiter";
else if(block == Blocks.vine)
this.blockType = "von Ranken";
}
else if(this.entity.isInLiquid()) {
this.blockType = "aus dem Wasser";
}
CombatEntry entry = new CombatEntry(source, amount, this.blockType, this.entity.fallDistance);
this.combat.add(entry);
this.lastDamaged = this.entity.ticksExisted;
this.damaged = true;
if(entry.getSource().getEntity() instanceof EntityLiving && !this.attacked && this.entity.isEntityAlive())
this.attacked = true;
}
public void sendDeathMessage() {
this.sendDeathMessage(false, false);
}
protected void sendDeathMessage(boolean natural, boolean forAll) {
// if(this.entity.worldObj.client)
// return;
String msg;
String kill;
IPlayer receiver = null;
if(this.combat.size() == 0) {
msg = kill = natural ? String.format("%s starb", this.entity.getColoredName(TextColor.LGRAY)) : null;
}
else {
CombatEntry strong = null;
CombatEntry block = null;
int min = 0;
float max = 0.0F;
for(int z = 0; z < this.combat.size(); ++z) {
CombatEntry entry = (CombatEntry)this.combat.get(z);
CombatEntry last = z > 0 ? (CombatEntry)this.combat.get(z - 1) : null;
if((entry.getSource() == DamageSource.fall || entry.getSource() == DamageSource.outOfWorld) &&
entry.getFallDistance() > 0.0F && (strong == null || entry.getFallDistance() > max)) {
if(z > 0) {
strong = last;
}
else {
strong = entry;
}
max = entry.getFallDistance();
}
if(entry.getBlockType() != null && (block == null || entry.getDamage() > min)) {
block = entry;
}
}
CombatEntry fall = max > 5.0F && strong != null ? strong : (min > 5 && block != null ? block : null);
CombatEntry last = (CombatEntry)this.combat.get(this.combat.size() - 1);
Entity lastEnt = last.getSource().getEntity();
if(fall != null && last.getSource() == DamageSource.fall) {
if(fall.getSource() != DamageSource.fall && fall.getSource() != DamageSource.outOfWorld) {
Entity fallEnt = fall.getSource().getEntity();
if(fallEnt != null && (lastEnt == null || fallEnt != lastEnt)) {
ItemStack fallItem = fallEnt instanceof EntityLiving ? ((EntityLiving)fallEnt).getHeldItem() : null;
receiver = fallEnt.isPlayer() ? ((EntityNPC)fallEnt).connection : null;
if(fallItem != null) { // && fallItem.hasDisplayName()) {
msg = String.format("%s wurde von %s mit %s zum Fallen verdammt", this.entity.getColoredName(TextColor.CYAN),
fallEnt.getColoredName(TextColor.CYAN), fallItem.getColoredName(TextColor.CYAN));
kill = String.format(TextColor.CYAN + "* %s mit %s zum Fallen verdammt",
this.entity.getColoredName(TextColor.CYAN), fallItem.getColoredName(TextColor.CYAN));
}
else {
msg = String.format("%s wurde von %s zum Fallen verdammt", this.entity.getColoredName(TextColor.CYAN),
fallEnt.getColoredName(TextColor.CYAN));
kill = String.format(TextColor.CYAN + "* %s zum Fallen verdammt",
this.entity.getColoredName(TextColor.CYAN));
}
}
else if(lastEnt != null) {
ItemStack lastItem = lastEnt instanceof EntityLiving ? ((EntityLiving)lastEnt).getHeldItem() : null;
receiver = lastEnt.isPlayer() ? ((EntityNPC)lastEnt).connection : null;
if(lastItem != null) { // && lastItem.hasDisplayName()) {
msg = String.format("%s fiel zu tief und wurde von %s mit %s erledigt",
this.entity.getColoredName(TextColor.BLUE),
lastEnt.getColoredName(TextColor.BLUE), lastItem.getColoredName(TextColor.BLUE));
kill = String.format(TextColor.BLUE + "* %s mit %s erledigt",
this.entity.getColoredName(TextColor.BLUE), lastItem.getColoredName(TextColor.BLUE));
}
else {
msg = String.format("%s fiel zu tief und wurde von %s erledigt", this.entity.getColoredName(TextColor.BLUE),
lastEnt.getColoredName(TextColor.BLUE));
kill = String.format(TextColor.BLUE + "%s erledigt", this.entity.getColoredName(TextColor.BLUE));
}
}
else {
msg = kill = natural ? String.format("%s wurde zum Fallen verdammt", this.entity.getColoredName(TextColor.CYAN)) : null;
}
}
else {
msg = kill = natural ? String.format("%s fiel " + (fall.getBlockType() == null ? "aus zu großer Höhe" : fall.getBlockType()),
this.entity.getColoredName(TextColor.NEON)) : null;
}
}
else {
receiver = last.getSource().getEntity() != null && last.getSource().getEntity().isPlayer() ? ((EntityNPC)last.getSource().getEntity()).connection : null;
msg = natural || (last.getSource() instanceof EntityDamageSource ? last.getSource().getEntity() != null : this.entity.getAttackingEntity() != null) ? last.getSource().getDeathMessage(this.entity) : null;
kill = msg == null ? null : last.getSource().getKillMessage(this.entity);
}
}
if(msg == null)
return;
if(receiver != null)
receiver.addFeed(kill);
if(forAll)
for(IPlayer player : ((WorldServer)this.entity.worldObj).getServer().getIPlayers()) {
if(player != receiver)
player.addFeed(msg);
}
}
public EntityLiving getAttacking() {
EntityLiving entity = null;
EntityNPC player = null;
int edmg = 0;
int pdmg = 0;
for(CombatEntry entry : this.combat) {
if(entry.getSource().getEntity() != null && entry.getSource().getEntity().isPlayer() && (player == null || entry.getDamage() > pdmg)) {
pdmg = entry.getDamage();
player = (EntityNPC)entry.getSource().getEntity();
}
if(entry.getSource().getEntity() instanceof EntityLiving && (entity == null || entry.getDamage() > edmg)) {
edmg = entry.getDamage();
entity = (EntityLiving)entry.getSource().getEntity();
}
}
return player != null && pdmg >= edmg / 3 ? player : entity;
}
public void resetCombat() {
int timeout = this.attacked ? 300 : 100;
if(this.damaged && (!this.entity.isEntityAlive() || this.entity.ticksExisted - this.lastDamaged > timeout)) {
this.damaged = false;
this.attacked = false;
this.combat.clear();
}
}
public int getMaxFallHeight() {
if(this.target == null) {
return 3;
}
else {
int i = (int)((float)this.entity.getHealth() - (float)this.entity.getMaxHealth() * 0.33F);
// i = i - (3 - this.worldObj.getDifficulty().getId()) * 4;
if(i < 0) {
i = 0;
}
return i + 3;
}
}
// public int getMaxFallHeight() EntityHaunter
// {
// return this.getAttackTarget() == null ? 3 : 3 + (this.getHealth() - 1);
// }
}