diff --git a/src/api/java/com/minecolonies/api/configuration/Configurations.java b/src/api/java/com/minecolonies/api/configuration/Configurations.java index 83a2be00c58..0ee14ac3019 100644 --- a/src/api/java/com/minecolonies/api/configuration/Configurations.java +++ b/src/api/java/com/minecolonies/api/configuration/Configurations.java @@ -95,6 +95,9 @@ public static class Gameplay @Config.Comment("The max size of a barbarian horde") public int maxBarbarianSize = 20; + @Config.Comment("Whether or not to barbarians can break, scale, bridge obstacles") + public boolean doBarbariansBreakThroughWalls = true; + @Config.Comment("The average amount of nights between raids") public int averageNumberOfNightsBetweenRaids = 3; diff --git a/src/api/java/com/minecolonies/api/util/constant/BarbarianConstants.java b/src/api/java/com/minecolonies/api/util/constant/BarbarianConstants.java new file mode 100644 index 00000000000..b7f9a090b7a --- /dev/null +++ b/src/api/java/com/minecolonies/api/util/constant/BarbarianConstants.java @@ -0,0 +1,63 @@ +package com.minecolonies.api.util.constant; + +import net.minecraft.potion.Potion; + +/** + * Barbarian constants class. + */ +public final class BarbarianConstants +{ + /** + * The amount of EXP to drop on entity death. + */ + public static final int BARBARIAN_EXP_DROP = 1; + + /** + * The range for the barb to move away. + */ + public static final int MOVE_AWAY_RANGE = 4; + + public static final int BARBARIAN_HORDE_DIFFICULTY_FIVE = 5; + + /** + * Values used to choose whether or not to play sound + */ + public static final int OUT_OF_ONE_HUNDRED = 100; + + public static final int ONE = 1; + + /** + * Values used for sword effect. + */ + public static final Potion SPEED_EFFECT = Potion.getPotionById(1); + public static final int TIME_TO_COUNTDOWN = 240; + public static final int COUNTDOWN_SECOND_MULTIPLIER = 4; + public static final int SPEED_EFFECT_DISTANCE = 7; + public static final int SPEED_EFFECT_DURATION = 240; + public static final int SPEED_EFFECT_MULTIPLIER = 2; + + /** + * Amount of ladders to place before destroying blocks. + */ + public static final int LADDERS_TO_PLACE = 10; + + /** + * Amount of ticks to despawn the barbarian. + */ + public static final int TICKS_TO_DESPAWN = Constants.TICKS_SECOND * Constants.SECONDS_A_MINUTE * 10; + + /** + * Randomly execute it every this ticks. + */ + public static final int EVERY_X_TICKS = 20; + + /** + * Private constructor to hide implicit one. + */ + private BarbarianConstants() + { + /* + * Intentionally left empty. + */ + } +} diff --git a/src/api/java/com/minecolonies/api/util/constant/NbtTagConstants.java b/src/api/java/com/minecolonies/api/util/constant/NbtTagConstants.java index a41cc01d536..4acf44b50af 100644 --- a/src/api/java/com/minecolonies/api/util/constant/NbtTagConstants.java +++ b/src/api/java/com/minecolonies/api/util/constant/NbtTagConstants.java @@ -351,6 +351,21 @@ public final class NbtTagConstants public static final String TAG_PROGRESS_LIST = "progressList"; public static final String TAG_PRINT_PROGRESS = "printProgrss"; + /** + * String to store the existing time to NBT. + */ + public static final String TAG_TIME = "time"; + + /** + * String to store the stuck counter to NBT. + */ + public static final String TAG_STUCK_COUNTER = "stuck"; + + /** + * String to store the ladder counter to NBT. + */ + public static final String TAG_LADDER_COUNTER = "ladder"; + /** * Private constructor to hide the implicit one. */ diff --git a/src/main/java/com/minecolonies/coremod/entity/EntityCitizen.java b/src/main/java/com/minecolonies/coremod/entity/EntityCitizen.java index 1fb93e3a624..2365c59f50c 100644 --- a/src/main/java/com/minecolonies/coremod/entity/EntityCitizen.java +++ b/src/main/java/com/minecolonies/coremod/entity/EntityCitizen.java @@ -58,7 +58,6 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.lang.reflect.Field; import java.util.Arrays; import java.util.Objects; @@ -68,20 +67,13 @@ import static com.minecolonies.api.util.constant.NbtTagConstants.*; import static com.minecolonies.api.util.constant.Suppression.INCREMENT_AND_DECREMENT_OPERATORS_SHOULD_NOT_BE_USED_IN_A_METHOD_CALL_OR_MIXED_WITH_OTHER_OPERATORS_IN_AN_EXPRESSION; import static com.minecolonies.api.util.constant.Suppression.UNCHECKED; -import static com.minecolonies.api.util.constant.TranslationConstants.CITIZEN_RENAME_NOT_ALLOWED; -import static com.minecolonies.api.util.constant.TranslationConstants.CITIZEN_RENAME_SAME; -import static com.minecolonies.api.util.constant.TranslationConstants.COM_MINECOLONIES_COREMOD_MOURN; +import static com.minecolonies.api.util.constant.TranslationConstants.*; /** * The Class used to represent the citizen entities. */ public class EntityCitizen extends AbstractEntityCitizen { - /** - * The navigator field of the citizen. - */ - private static Field navigatorField; - /** * The New PathNavigate navigator. */ @@ -189,7 +181,7 @@ public EntityCitizen(final World world) this.enablePersistence(); this.setAlwaysRenderNameTag(Configurations.gameplay.alwaysRenderNameTag); this.newNavigator = new PathNavigate(this, world); - updateNavigatorField(); + this.navigator = newNavigator; if (CompatibilityUtils.getWorld(this).isRemote) { setRenderDistanceWeight(RENDER_DISTANCE_WEIGHT); @@ -199,41 +191,6 @@ public EntityCitizen(final World world) initTasks(); } - /** - * Method used to update the navigator field. - * Gets the minecraft path navigate through reflection. - */ - private synchronized void updateNavigatorField() - { - if (navigatorField == null) - { - final Field[] fields = EntityLiving.class.getDeclaredFields(); - for (@NotNull final Field field : fields) - { - if (field.getType().equals(net.minecraft.pathfinding.PathNavigate.class)) - { - field.setAccessible(true); - navigatorField = field; - break; - } - } - } - - if (navigatorField == null) - { - throw new IllegalStateException("Navigator field should not be null, contact developers."); - } - - try - { - navigatorField.set(this, this.newNavigator); - } - catch (final IllegalAccessException e) - { - Log.getLogger().error("Navigator error", e); - } - } - /** * Initiates citizen tasks * Suppressing Sonar Rule Squid:S881 diff --git a/src/main/java/com/minecolonies/coremod/entity/ai/mobs/barbarians/AbstractEntityBarbarian.java b/src/main/java/com/minecolonies/coremod/entity/ai/mobs/barbarians/AbstractEntityBarbarian.java index 168eb193664..8a4e4ee4b86 100644 --- a/src/main/java/com/minecolonies/coremod/entity/ai/mobs/barbarians/AbstractEntityBarbarian.java +++ b/src/main/java/com/minecolonies/coremod/entity/ai/mobs/barbarians/AbstractEntityBarbarian.java @@ -2,11 +2,11 @@ import com.minecolonies.api.configuration.Configurations; import com.minecolonies.api.util.CompatibilityUtils; -import com.minecolonies.api.util.constant.Constants; import com.minecolonies.coremod.colony.Colony; import com.minecolonies.coremod.colony.ColonyManager; import com.minecolonies.coremod.entity.ai.mobs.util.BarbarianSpawnUtils; import com.minecolonies.coremod.entity.ai.mobs.util.BarbarianUtils; +import com.minecolonies.coremod.entity.pathfinding.PathNavigate; import com.minecolonies.coremod.items.ItemChiefSword; import com.minecolonies.coremod.sounds.BarbarianSounds; import net.minecraft.entity.IEntityLivingData; @@ -14,7 +14,6 @@ import net.minecraft.entity.monster.EntityMob; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.nbt.NBTTagCompound; -import net.minecraft.potion.Potion; import net.minecraft.potion.PotionEffect; import net.minecraft.util.DamageSource; import net.minecraft.util.ResourceLocation; @@ -22,53 +21,23 @@ import net.minecraft.world.DifficultyInstance; import net.minecraft.world.World; import net.minecraft.world.WorldServer; +import org.jetbrains.annotations.NotNull; import javax.annotation.Nullable; import java.util.Random; +import static com.minecolonies.api.util.constant.BarbarianConstants.*; +import static com.minecolonies.api.util.constant.NbtTagConstants.*; + /** * Abstract for all Barbarian entities. */ public abstract class AbstractEntityBarbarian extends EntityMob { /** - * String to store the existing time to NBT. - */ - private static final String TAG_TIME = "time"; - - /** - * The amount of EXP to drop on entity death. - */ - private static final int BARBARIAN_EXP_DROP = 1; - - private static final int BARBARIAN_HORDE_DIFFICULTY_FIVE = 5; - - /** - * Values used to choose whether or not to play sound + * The New PathNavigate navigator. */ - private static final int OUT_OF_ONE_HUNDRED = 100; - - private static final int ONE = 1; - - /** - * Values used for sword effect. - */ - private static final Potion SPEED_EFFECT = Potion.getPotionById(1); - private static final int TIME_TO_COUNTDOWN = 240; - private static final int COUNTDOWN_SECOND_MULTIPLIER = 4; - private static final int SPEED_EFFECT_DISTANCE = 7; - private static final int SPEED_EFFECT_DURATION = 240; - private static final int SPEED_EFFECT_MULTIPLIER = 2; - - /** - * Amount of ticks to despawn the barbarian. - */ - private static final int TICKS_TO_DESPAWN = Constants.TICKS_SECOND * Constants.SECONDS_A_MINUTE * 5; - - /** - * Randomly execute it every this ticks. - */ - private static final int EVERY_X_TICKS = 20; + private PathNavigate newNavigator; /** * Sets the barbarians target colony on spawn Thus it never changes. @@ -79,19 +48,32 @@ public abstract class AbstractEntityBarbarian extends EntityMob * Random object. */ private final Random random = new Random(); + /** * Current count of ticks. */ private int currentCount = 0; + /** * The world time when the barbarian spawns. */ private long worldTimeAtSpawn = 0; + /** * The current tick since creation. */ private int currentTick = 1; + /** + * Amount of time the barb got stuck. + */ + private int stuckCounter = 0; + + /** + * Amount of time the barb got stuck. + */ + private int ladderCounter = 0; + /** * Constructor method for Abstract Barbarians. * @@ -235,6 +217,42 @@ public void onLivingUpdate() super.onLivingUpdate(); } + /** + * Get the stack counter. + * @return the amount it got stuck already. + */ + public int getStuckCounter() + { + return stuckCounter; + } + + /** + * Set the stack counter. + * @param stuckCounter the amount. + */ + public void setStuckCounter(final int stuckCounter) + { + this.stuckCounter = stuckCounter; + } + + /** + * Get the ladder counter. + * @return the amount it got stuck and placed a ladder already. + */ + public int getLadderCounter() + { + return ladderCounter; + } + + /** + * Set the ladder counter. + * @param ladderCounter the amount. + */ + public void setLadderCounter(final int ladderCounter) + { + this.ladderCounter = ladderCounter; + } + @Override protected SoundEvent getHurtSound(final DamageSource damageSourceIn) { @@ -251,6 +269,8 @@ protected SoundEvent getDeathSound() public NBTTagCompound writeToNBT(final NBTTagCompound compound) { compound.setLong(TAG_TIME, worldTimeAtSpawn); + compound.setInteger(TAG_STUCK_COUNTER, stuckCounter); + compound.setInteger(TAG_LADDER_COUNTER, ladderCounter); return super.writeToNBT(compound); } @@ -258,6 +278,9 @@ public NBTTagCompound writeToNBT(final NBTTagCompound compound) public void readFromNBT(final NBTTagCompound compound) { worldTimeAtSpawn = compound.getLong(TAG_TIME); + stuckCounter = compound.getInteger(TAG_STUCK_COUNTER); + ladderCounter = compound.getInteger(TAG_LADDER_COUNTER); + super.readFromNBT(compound); } @@ -271,6 +294,20 @@ public void onDeath(final DamageSource cause) } } + @NotNull + @Override + public PathNavigate getNavigator() + { + if (this.newNavigator == null) + { + this.newNavigator = new PathNavigate(this, world); + this.navigator = newNavigator; + this.newNavigator.setCanSwim(true); + this.newNavigator.setEnterDoors(false); + } + return newNavigator; + } + @Override protected void onDeathUpdate() { diff --git a/src/main/java/com/minecolonies/coremod/entity/ai/mobs/barbarians/EntityAIWalkToRandomHuts.java b/src/main/java/com/minecolonies/coremod/entity/ai/mobs/barbarians/EntityAIWalkToRandomHuts.java index 80435bd3fe2..4d926d20215 100644 --- a/src/main/java/com/minecolonies/coremod/entity/ai/mobs/barbarians/EntityAIWalkToRandomHuts.java +++ b/src/main/java/com/minecolonies/coremod/entity/ai/mobs/barbarians/EntityAIWalkToRandomHuts.java @@ -1,21 +1,23 @@ package com.minecolonies.coremod.entity.ai.mobs.barbarians; -import com.minecolonies.api.util.Log; +import com.minecolonies.api.configuration.Configurations; import com.minecolonies.coremod.colony.Colony; import com.minecolonies.coremod.colony.ColonyManager; import com.minecolonies.coremod.colony.buildings.AbstractBuilding; import com.minecolonies.coremod.entity.pathfinding.GeneralEntityWalkToProxy; -import com.minecolonies.coremod.entity.pathfinding.PathNavigate; -import net.minecraft.entity.EntityCreature; -import net.minecraft.entity.EntityLiving; +import net.minecraft.block.BlockLadder; +import net.minecraft.block.state.IBlockState; import net.minecraft.entity.ai.EntityAIBase; +import net.minecraft.init.Blocks; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; import org.jetbrains.annotations.NotNull; -import java.lang.reflect.Field; -import java.util.Collection; -import java.util.Random; +import java.util.*; + +import static com.minecolonies.api.util.constant.BarbarianConstants.*; /** * Barbarian Pathing Class @@ -23,20 +25,55 @@ public class EntityAIWalkToRandomHuts extends EntityAIBase { - protected final EntityCreature entity; - protected final World world; - protected final double speed; - protected Colony colony; /** - * The navigator for this entity. + * The moving entity. + */ + protected final AbstractEntityBarbarian entity; + + /** + * All directions. + */ + private final List directions = Arrays.asList(EnumFacing.HORIZONTALS); + + /** + * The world. + */ + protected final World world; + + /** + * The set speed. */ - private final PathNavigate newNavigator; - private BlockPos targetBlock; + protected final double speed; + + /** + * The colony. + */ + protected Colony colony; + + /** + * Random obj. + */ + private final Random random = new Random(); + + /** + * The target block. + */ + private BlockPos targetBlock; + /** * Walk to proxy. */ private GeneralEntityWalkToProxy proxy; - private Field navigatorField; + + /** + * The last position he was at. + */ + private BlockPos lastPos = null; + + /** + * Time the entity is at the same position already. + */ + private int stuckTime = 0; /** * Constructor for AI @@ -44,53 +81,16 @@ public class EntityAIWalkToRandomHuts extends EntityAIBase * @param creatureIn the creature that the AI applies to * @param speedIn The speed at which the Entity walks */ - public EntityAIWalkToRandomHuts(final EntityCreature creatureIn, final double speedIn) + public EntityAIWalkToRandomHuts(final AbstractEntityBarbarian creatureIn, final double speedIn) { super(); this.entity = creatureIn; this.speed = speedIn; this.world = creatureIn.getEntityWorld(); - this.newNavigator = new PathNavigate(entity, world); - updateNavigatorField(); - this.newNavigator.setCanSwim(true); - this.newNavigator.setEnterDoors(false); + lastPos = entity.getPosition(); this.setMutexBits(1); } - /** - * Updates the navigator field for the AI - */ - private synchronized void updateNavigatorField() - { - if (navigatorField == null) - { - final Field[] fields = EntityLiving.class.getDeclaredFields(); - for (@NotNull final Field field : fields) - { - if (field.getType().equals(net.minecraft.pathfinding.PathNavigate.class)) - { - field.setAccessible(true); - navigatorField = field; - break; - } - } - } - - if (navigatorField == null) - { - throw new IllegalStateException("Navigator field should not be null, contact developers."); - } - - try - { - navigatorField.set(entity, this.newNavigator); - } - catch (final IllegalAccessException e) - { - Log.getLogger().error("Navigator error", e); - } - } - @Override public boolean shouldExecute() { @@ -119,7 +119,6 @@ public boolean shouldContinueExecuting() @Override public void startExecuting() { - updateNavigatorField(); if (targetBlock != null) { if (this.isEntityAtSiteWithMove(targetBlock, 2)) @@ -146,7 +145,102 @@ private boolean isEntityAtSiteWithMove(@NotNull final BlockPos site, final int r { proxy = new GeneralEntityWalkToProxy(entity); } - return proxy.walkToBlock(site, range); + + if (new AxisAlignedBB(entity.getPosition()).expand(1, 1, 1).expand(-1, -1, -1) + .intersects(new AxisAlignedBB(lastPos))) + { + stuckTime++; + } + else + { + stuckTime = 0; + } + + if (stuckTime > EVERY_X_TICKS) + { + entity.getNavigator().clearPath(); + stuckTime = 0; + entity.setStuckCounter(entity.getStuckCounter() + 1); + final BlockPos front = entity.getPosition().down().offset(entity.getHorizontalFacing()); + + if (!world.getBlockState(front).getMaterial().isSolid()) + { + world.setBlockState(front, Blocks.COBBLESTONE.getDefaultState()); + } + + if (entity.getStuckCounter() > 1 && Configurations.gameplay.doBarbariansBreakThroughWalls) + { + Collections.shuffle(directions); + + entity.setStuckCounter(0); + entity.setLadderCounter(entity.getLadderCounter() + 1); + + final IBlockState ladderHere = world.getBlockState(entity.getPosition()); + final IBlockState ladderUp = world.getBlockState(entity.getPosition().up()); + if (entity.getLadderCounter() <= LADDERS_TO_PLACE || random.nextBoolean()) + { + if (ladderHere.getBlock() == Blocks.LADDER && ladderUp.getBlock() != Blocks.LADDER && !ladderHere.getMaterial().isLiquid()) + { + world.setBlockState(entity.getPosition().up(), ladderHere); + } + else if(ladderUp.getBlock() == Blocks.LADDER && ladderHere.getBlock() != Blocks.LADDER && !ladderUp.getMaterial().isLiquid()) + { + world.setBlockState(entity.getPosition(), ladderUp); + } + else if (ladderUp.getBlock() != Blocks.LADDER && ladderHere.getBlock() != Blocks.LADDER) + { + for (final EnumFacing dir : directions) + { + if (world.getBlockState(entity.getPosition().offset(dir)).getMaterial().isSolid()) + { + if (random.nextBoolean()) + { + world.setBlockState(entity.getPosition().up(), Blocks.LADDER.getDefaultState().withProperty(BlockLadder.FACING, dir.getOpposite())); + } + else if (!ladderHere.getMaterial().isLiquid()) + { + world.setBlockState(entity.getPosition(), Blocks.LADDER.getDefaultState().withProperty(BlockLadder.FACING, dir.getOpposite())); + } + break; + } + } + } + } + else + { + for (final EnumFacing dir : directions) + { + final IBlockState state = world.getBlockState(entity.getPosition().offset(dir)); + if (state.getMaterial().isSolid() && state.getBlock() != Blocks.LADDER) + { + final BlockPos posToDestroy; + switch (random.nextInt(4)) + { + case 1: + posToDestroy = entity.getPosition().offset(dir).up(); + break; + case 2: + posToDestroy = entity.getPosition().offset(dir); + break; + default: + posToDestroy = entity.getPosition().up(2); + break; + } + world.destroyBlock(posToDestroy, true); + break; + } + } + } + } + else + { + entity.getNavigator().moveAwayFromXYZ(entity.getPosition(), random.nextInt(4), 2); + } + return false; + } + + lastPos = entity.getPosition(); + return proxy.walkToBlock(site, range, true); } /** @@ -165,8 +259,8 @@ private BlockPos getRandomBuilding() final Object[] buildingArray = buildingList.toArray(); if (buildingArray.length != 0) { - final int random = new Random().nextInt(buildingArray.length); - final AbstractBuilding building = (AbstractBuilding) buildingArray[random]; + final int rand = random.nextInt(buildingArray.length); + final AbstractBuilding building = (AbstractBuilding) buildingArray[rand]; return building.getLocation(); } diff --git a/src/main/java/com/minecolonies/coremod/entity/pathfinding/PathJobMoveAwayFromLocation.java b/src/main/java/com/minecolonies/coremod/entity/pathfinding/PathJobMoveAwayFromLocation.java index f4dc464ef65..56a1c7edbe7 100644 --- a/src/main/java/com/minecolonies/coremod/entity/pathfinding/PathJobMoveAwayFromLocation.java +++ b/src/main/java/com/minecolonies/coremod/entity/pathfinding/PathJobMoveAwayFromLocation.java @@ -62,8 +62,8 @@ public PathJobMoveAwayFromLocation(final World world, @NotNull final BlockPos st this.avoid = new BlockPos(avoid); this.avoidDistance = avoidDistance; - double dx = (double) (start.getX() - avoid.getX()); - double dz = (double) (start.getZ() - avoid.getZ()); + double dx = (double) (start.getX() - avoid.getX()) + 1; + double dz = (double) (start.getZ() - avoid.getZ()) + 1; final double scalar = avoidDistance / Math.sqrt(dx * dx + dz * dz); dx *= scalar; @@ -145,7 +145,9 @@ protected double computeHeuristic(@NotNull final BlockPos pos) protected boolean isAtDestination(@NotNull final Node n) { final BlockPos vector = n.pos.subtract(avoid); - return getNodeResultScore(n) >= (avoidDistance * avoidDistance) && EnumFacing.getFacingFromVector(vector.getX(), 0, vector.getZ()).equals(direction); + final double nodeResult = getNodeResultScore(n); + final int avoidSq = (avoidDistance * avoidDistance); + return nodeResult >= avoidSq && (EnumFacing.getFacingFromVector(vector.getX(), 0, vector.getZ()).equals(direction) || nodeResult > avoidSq * avoidDistance); } /** diff --git a/src/main/java/com/minecolonies/coremod/entity/pathfinding/PathNavigate.java b/src/main/java/com/minecolonies/coremod/entity/pathfinding/PathNavigate.java index d042cda575f..b3f9c31368a 100644 --- a/src/main/java/com/minecolonies/coremod/entity/pathfinding/PathNavigate.java +++ b/src/main/java/com/minecolonies/coremod/entity/pathfinding/PathNavigate.java @@ -265,7 +265,7 @@ public void onUpdateNavigation() if (pEx.isOnLadder()) { - final Vec3d vec3 = this.getPath().getPosition(this.ourEntity); + Vec3d vec3 = this.getPath().getPosition(this.ourEntity); if (vec3.squareDistanceTo(ourEntity.posX, vec3.y, ourEntity.posZ) < Math.random() * 0.1) { @@ -275,16 +275,16 @@ public void onUpdateNavigation() { // Any of these values is climbing, so adjust our direction of travel towards the ladder case NORTH: - vec3.add(0, 0, 1); + vec3 = vec3.add(0, 0, 1); break; case SOUTH: - vec3.add(0, 0, -1); + vec3 = vec3.add(0, 0, -1); break; case WEST: - vec3.add(1, 0, 0); + vec3 = vec3.add(1, 0, 0); break; case EAST: - vec3.add(-1, 0, 0); + vec3 = vec3.add(-1, 0, 0); break; // Any other value is going down, so lets not move at all default: