From ca9b5f33e6c7481270e10cde26c7d77981f7af60 Mon Sep 17 00:00:00 2001 From: N1nn1 <64173061+N1nn1@users.noreply.github.com> Date: Wed, 8 May 2024 02:45:46 +0200 Subject: [PATCH] Way better boids --- .../java/com/ninni/spawn/entity/Herring.java | 64 ++++++++++++++++++- .../entity/ai/goal/BoidFishSchoolingGoal.java | 20 +++--- .../entity/ai/goal/HeightBoundsGoal.java | 15 ++--- ...itSpeedAndLookInVelocityDirectionGoal.java | 17 ++--- .../ai/goal/OrganizeBoidSchoolingGoal.java | 10 ++- .../spawn/entity/common/BoidFishEntity.java | 30 +++++---- .../spawn/registry/SpawnCreativeModeTab.java | 2 +- 7 files changed, 101 insertions(+), 57 deletions(-) diff --git a/src/main/java/com/ninni/spawn/entity/Herring.java b/src/main/java/com/ninni/spawn/entity/Herring.java index d8aa594..3bc6473 100644 --- a/src/main/java/com/ninni/spawn/entity/Herring.java +++ b/src/main/java/com/ninni/spawn/entity/Herring.java @@ -3,30 +3,88 @@ import com.ninni.spawn.entity.common.BoidFishEntity; import com.ninni.spawn.registry.SpawnItems; import com.ninni.spawn.registry.SpawnSoundEvents; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.syncher.EntityDataAccessor; +import net.minecraft.network.syncher.EntityDataSerializers; +import net.minecraft.network.syncher.SynchedEntityData; import net.minecraft.sounds.SoundEvent; import net.minecraft.sounds.SoundEvents; +import net.minecraft.world.DifficultyInstance; import net.minecraft.world.damagesource.DamageSource; import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.Mob; +import net.minecraft.world.entity.MobSpawnType; +import net.minecraft.world.entity.SpawnGroupData; import net.minecraft.world.entity.ai.attributes.AttributeSupplier; import net.minecraft.world.entity.ai.attributes.Attributes; -import net.minecraft.world.entity.ai.control.SmoothSwimmingLookControl; -import net.minecraft.world.entity.ai.control.SmoothSwimmingMoveControl; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.Level; +import net.minecraft.world.level.ServerLevelAccessor; +import org.jetbrains.annotations.Nullable; public class Herring extends BoidFishEntity { + private static final EntityDataAccessor SCHOOL_SIZE = SynchedEntityData.defineId(Herring.class, EntityDataSerializers.INT); public Herring(EntityType entityType, Level level) { super(entityType, level); } + @Override + public @Nullable SpawnGroupData finalizeSpawn(ServerLevelAccessor serverLevelAccessor, DifficultyInstance difficultyInstance, MobSpawnType mobSpawnType, @Nullable SpawnGroupData spawnGroupData, @Nullable CompoundTag compoundTag) { + if (mobSpawnType != MobSpawnType.BUCKET) { + this.setSchoolSize(random.nextInt(15) + 5); + } + return super.finalizeSpawn(serverLevelAccessor, difficultyInstance, mobSpawnType, spawnGroupData, compoundTag); + } + public static AttributeSupplier.Builder createAttributes() { return Mob.createMobAttributes().add(Attributes.MAX_HEALTH, 2.0); } + @Override + public void tick() { + super.tick(); + if (this.getMaxSchoolSize() != this.getSchoolSize()) this.SetMaxSchoolSize(this.getSchoolSize()); + } + + protected void defineSynchedData() { + super.defineSynchedData(); + this.entityData.define(SCHOOL_SIZE, 0); + } + + public void addAdditionalSaveData(CompoundTag compoundTag) { + super.addAdditionalSaveData(compoundTag); + compoundTag.putInt("SchoolSize", this.getSchoolSize()); + } + + public void readAdditionalSaveData(CompoundTag compoundTag) { + super.readAdditionalSaveData(compoundTag); + this.setSchoolSize(compoundTag.getInt("SchoolSize")); + } + + public void saveToBucketTag(ItemStack itemStack) { + super.saveToBucketTag(itemStack); + CompoundTag compoundTag = itemStack.getOrCreateTag(); + compoundTag.putInt("SchoolSize", this.getSchoolSize()); + } + + @Override + public void loadFromBucketTag(CompoundTag compoundTag) { + super.loadFromBucketTag(compoundTag); + if (compoundTag.contains("SchoolSize")) this.setSchoolSize(compoundTag.getInt("SchoolSize")); + } + + public void setSchoolSize(int i) { + this.entityData.set(SCHOOL_SIZE, i); + } + + public int getSchoolSize() { + return this.entityData.get(SCHOOL_SIZE); + } + + @Override public int getMaxSchoolSize() { - return 15; + return this.getSchoolSize(); } @Override diff --git a/src/main/java/com/ninni/spawn/entity/ai/goal/BoidFishSchoolingGoal.java b/src/main/java/com/ninni/spawn/entity/ai/goal/BoidFishSchoolingGoal.java index 91ce710..63d77c3 100644 --- a/src/main/java/com/ninni/spawn/entity/ai/goal/BoidFishSchoolingGoal.java +++ b/src/main/java/com/ninni/spawn/entity/ai/goal/BoidFishSchoolingGoal.java @@ -17,6 +17,7 @@ public class BoidFishSchoolingGoal extends Goal { public final float alignmentInfluence; public final float cohesionInfluence; private final BoidFishEntity mob; + private int timer = 0; public BoidFishSchoolingGoal(BoidFishEntity mob, float separationInfluence, float separationRange, float alignmentInfluence, float cohesionInfluence) { this.mob = mob; @@ -28,12 +29,15 @@ public BoidFishSchoolingGoal(BoidFishEntity mob, float separationInfluence, floa @Override public boolean canUse() { - return mob.isInWaterOrBubble() && (mob.isFollower() || mob.hasFollowers()); + return mob.isInWaterOrBubble() && (mob.isFollower() || mob.hasFollowers()) && mob.cantFollowTimer == 0; } public void tick() { - mob.addDeltaMovement(separation()); + if (mob.horizontalCollision) timer++; + if (timer > 60) mob.cantFollowTimer = 60; + mob.addDeltaMovement(random()); + mob.addDeltaMovement(separation()); mob.addDeltaMovement(cohesion()); mob.addDeltaMovement(alignment()); } @@ -41,20 +45,14 @@ public void tick() { public Vec3 random() { var velocity = mob.getDeltaMovement(); - if (Mth.abs((float) velocity.x) < 0.1 && Mth.abs((float) velocity.z) < 0.1) - return new Vec3(randomSign() * 0.2, 0, randomSign() * 0.2); + if (Mth.abs((float) velocity.x) < 0.1 && Mth.abs((float) velocity.z) < 0.1 && mob.hasFollowers()) + return new Vec3(randomSign() * 0.4, 0, randomSign() * 0.4); return Vec3.ZERO; } public int randomSign() { - var isNegative = mob.getRandom().nextBoolean(); - - if (isNegative) { - return -1; - } - - return 1; + return mob.getRandom().nextBoolean() ? -1 : 1; } public Vec3 separation() { diff --git a/src/main/java/com/ninni/spawn/entity/ai/goal/HeightBoundsGoal.java b/src/main/java/com/ninni/spawn/entity/ai/goal/HeightBoundsGoal.java index e7d4ce1..7fff6e2 100644 --- a/src/main/java/com/ninni/spawn/entity/ai/goal/HeightBoundsGoal.java +++ b/src/main/java/com/ninni/spawn/entity/ai/goal/HeightBoundsGoal.java @@ -31,20 +31,13 @@ public void tick() { } public Vec3 bounds() { - var amount = 0.1; + var yAmount = 0.1; var dY = Mth.abs((float) mob.getDeltaMovement().y); - if (dY > amount) { - amount = dY; - } + if (dY > yAmount) yAmount = dY; - - - if (this.isTooHigh()) { - return new Vec3(0, -amount, 0); - } - if (this.isTooLow()) - return new Vec3(0, amount, 0); + if (this.isTooHigh()) return new Vec3(0, -yAmount, 0); + if (this.isTooLow()) return new Vec3(0, yAmount, 0); return Vec3.ZERO; } diff --git a/src/main/java/com/ninni/spawn/entity/ai/goal/LimitSpeedAndLookInVelocityDirectionGoal.java b/src/main/java/com/ninni/spawn/entity/ai/goal/LimitSpeedAndLookInVelocityDirectionGoal.java index 235da21..58eddaf 100644 --- a/src/main/java/com/ninni/spawn/entity/ai/goal/LimitSpeedAndLookInVelocityDirectionGoal.java +++ b/src/main/java/com/ninni/spawn/entity/ai/goal/LimitSpeedAndLookInVelocityDirectionGoal.java @@ -11,13 +11,11 @@ public class LimitSpeedAndLookInVelocityDirectionGoal extends Goal { private final BoidFishEntity mob; - private final float minSpeed; - private final float maxSpeed; + private final float speed; - public LimitSpeedAndLookInVelocityDirectionGoal(BoidFishEntity mob, float minSpeed, float maxSpeed) { + public LimitSpeedAndLookInVelocityDirectionGoal(BoidFishEntity mob, float speed) { this.mob = mob; - this.minSpeed = minSpeed; - this.maxSpeed = maxSpeed; + this.speed = speed; } @Override @@ -27,14 +25,7 @@ public boolean canUse() { @Override public void tick() { - var velocity = mob.getDeltaMovement(); - var speed = velocity.length(); - - if (speed < minSpeed) - velocity = velocity.normalize().scale(minSpeed); - if (speed > maxSpeed) - velocity = velocity.normalize().scale(maxSpeed); - + var velocity = mob.getDeltaMovement().normalize().scale(0.2).scale(speed); mob.setDeltaMovement(velocity); mob.lookAt(EntityAnchorArgument.Anchor.EYES, mob.position().add(velocity.scale(3))); } diff --git a/src/main/java/com/ninni/spawn/entity/ai/goal/OrganizeBoidSchoolingGoal.java b/src/main/java/com/ninni/spawn/entity/ai/goal/OrganizeBoidSchoolingGoal.java index d46225f..8f7d94e 100644 --- a/src/main/java/com/ninni/spawn/entity/ai/goal/OrganizeBoidSchoolingGoal.java +++ b/src/main/java/com/ninni/spawn/entity/ai/goal/OrganizeBoidSchoolingGoal.java @@ -24,12 +24,10 @@ protected int nextStartTick(BoidFishEntity abstractSchoolingFish) { @Override public boolean canUse() { - if (this.fish.hasFollowers()) { - return false; - } - if (this.fish.isFollower()) { - return true; - } + if (this.fish.hasFollowers()) return false; + + if (this.fish.isFollower()) return true; + if (this.nextStartTick > 0) { --this.nextStartTick; return false; diff --git a/src/main/java/com/ninni/spawn/entity/common/BoidFishEntity.java b/src/main/java/com/ninni/spawn/entity/common/BoidFishEntity.java index 5152342..f474d3f 100644 --- a/src/main/java/com/ninni/spawn/entity/common/BoidFishEntity.java +++ b/src/main/java/com/ninni/spawn/entity/common/BoidFishEntity.java @@ -25,6 +25,8 @@ public abstract class BoidFishEntity extends AbstractFish { @Nullable public BoidFishEntity leader; public List ownSchool = new ArrayList<>(); + private int maxSchoolSize; + public int cantFollowTimer; public BoidFishEntity(EntityType entityType, Level level) { super(entityType, level); @@ -35,17 +37,15 @@ protected void registerGoals() { this.goalSelector.addGoal(4, new FishSwimGoal(this)); this.goalSelector.addGoal(5, new BoidFishSchoolingGoal(this, 0.2f, 0.4f, 8 / 20f, 1 / 20f)); this.goalSelector.addGoal(3, new HeightBoundsGoal(this)); - this.goalSelector.addGoal(2, new LimitSpeedAndLookInVelocityDirectionGoal(this, 0.3f, 0.4f)); + this.goalSelector.addGoal(2, new LimitSpeedAndLookInVelocityDirectionGoal(this, 0.65f)); this.goalSelector.addGoal(5, new OrganizeBoidSchoolingGoal(this)); } - @Override - public int getMaxSpawnClusterSize() { - return this.getMaxSchoolSize(); - } - public int getMaxSchoolSize() { - return super.getMaxSpawnClusterSize(); + return maxSchoolSize; + } + public void SetMaxSchoolSize(int i) { + this.maxSchoolSize = i; } public boolean isFollower() { @@ -53,8 +53,10 @@ public boolean isFollower() { } public void startFollowing(BoidFishEntity abstractSchoolingFish) { - this.leader = abstractSchoolingFish; - abstractSchoolingFish.addToOwnSchoolFollower(this); + if (this.cantFollowTimer == 0) { + this.leader = abstractSchoolingFish; + abstractSchoolingFish.addToOwnSchoolFollower(this); + } } public void stopFollowing() { @@ -70,7 +72,7 @@ protected SoundEvent getSwimSound() { } private void addToOwnSchoolFollower(BoidFishEntity entity) { - this.ownSchool.add(entity); + if (entity.cantFollowTimer == 0) this.ownSchool.add(entity); } private void removeFollowerFromOwnSchool(BoidFishEntity entity) { @@ -78,12 +80,16 @@ private void removeFollowerFromOwnSchool(BoidFishEntity entity) { } public boolean canBeFollowed() { - return this.hasFollowers() && this.ownSchool.size() < this.getMaxSchoolSize(); + return this.hasFollowers() && this.ownSchool.size() < this.getMaxSchoolSize() && this.cantFollowTimer == 0; } @Override public void tick() { super.tick(); + if (this.cantFollowTimer > 0) { + this.cantFollowTimer--; + this.stopFollowing(); + } } public boolean hasFollowers() { @@ -95,7 +101,7 @@ public void addFollowers(Stream stream) { } public boolean inRangeOfLeader() { - return this.distanceToSqr(this.leader) <= 121.0; + return this.distanceToSqr(this.leader) <= 200.0; } @Override diff --git a/src/main/java/com/ninni/spawn/registry/SpawnCreativeModeTab.java b/src/main/java/com/ninni/spawn/registry/SpawnCreativeModeTab.java index 18fc1be..52a44ec 100644 --- a/src/main/java/com/ninni/spawn/registry/SpawnCreativeModeTab.java +++ b/src/main/java/com/ninni/spawn/registry/SpawnCreativeModeTab.java @@ -99,7 +99,7 @@ public class SpawnCreativeModeTab { ItemGroupEvents.modifyEntriesEvent(CreativeModeTabs.SPAWN_EGGS).register(entries -> { entries.addAfter(Items.ALLAY_SPAWN_EGG, ANGLER_FISH_SPAWN_EGG, ANT_SPAWN_EGG); - entries.addAfter(Items.CHICKEN, CLAM_SPAWN_EGG); + entries.addAfter(Items.CHICKEN_SPAWN_EGG, CLAM_SPAWN_EGG); entries.addAfter(Items.OCELOT_SPAWN_EGG, OCTOPUS_SPAWN_EGG); entries.addAfter(Items.SALMON_SPAWN_EGG, SEA_COW_SPAWN_EGG, SEAHORSE_SPAWN_EGG, SEA_LION_SPAWN_EGG); entries.addAfter(Items.SLIME_SPAWN_EGG, SNAIL_SPAWN_EGG);