mirror of
https://github.com/Sollace/Unicopia.git
synced 2025-03-04 01:01:29 +01:00
Improved butterfly ai, made it so they will visit flowers to breed
This commit is contained in:
parent
56643db555
commit
cd97867678
3 changed files with 170 additions and 42 deletions
|
@ -1,9 +1,15 @@
|
||||||
package com.minelittlepony.unicopia.entity;
|
package com.minelittlepony.unicopia.entity;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import com.minelittlepony.unicopia.util.NbtSerialisable;
|
||||||
|
|
||||||
import net.minecraft.block.BlockState;
|
import net.minecraft.block.BlockState;
|
||||||
import net.minecraft.entity.Entity;
|
import net.minecraft.entity.Entity;
|
||||||
import net.minecraft.entity.EntityPose;
|
import net.minecraft.entity.EntityPose;
|
||||||
|
@ -17,9 +23,11 @@ import net.minecraft.entity.data.TrackedData;
|
||||||
import net.minecraft.entity.data.TrackedDataHandlerRegistry;
|
import net.minecraft.entity.data.TrackedDataHandlerRegistry;
|
||||||
import net.minecraft.entity.mob.AmbientEntity;
|
import net.minecraft.entity.mob.AmbientEntity;
|
||||||
import net.minecraft.entity.player.PlayerEntity;
|
import net.minecraft.entity.player.PlayerEntity;
|
||||||
|
import net.minecraft.nbt.NbtCompound;
|
||||||
import net.minecraft.predicate.entity.EntityPredicates;
|
import net.minecraft.predicate.entity.EntityPredicates;
|
||||||
import net.minecraft.sound.SoundEvent;
|
import net.minecraft.sound.SoundEvent;
|
||||||
import net.minecraft.sound.SoundEvents;
|
import net.minecraft.sound.SoundEvents;
|
||||||
|
import net.minecraft.tag.BlockTags;
|
||||||
import net.minecraft.util.Identifier;
|
import net.minecraft.util.Identifier;
|
||||||
import net.minecraft.util.math.BlockPos;
|
import net.minecraft.util.math.BlockPos;
|
||||||
import net.minecraft.util.math.MathHelper;
|
import net.minecraft.util.math.MathHelper;
|
||||||
|
@ -28,13 +36,18 @@ import net.minecraft.world.World;
|
||||||
import net.minecraft.world.WorldAccess;
|
import net.minecraft.world.WorldAccess;
|
||||||
|
|
||||||
public class ButterflyEntity extends AmbientEntity {
|
public class ButterflyEntity extends AmbientEntity {
|
||||||
|
private static final int MAX_BREEDING_COOLDOWN = 300;
|
||||||
|
|
||||||
private static final TrackedData<Boolean> RESTING = DataTracker.registerData(ButterflyEntity.class, TrackedDataHandlerRegistry.BOOLEAN);
|
private static final TrackedData<Boolean> RESTING = DataTracker.registerData(ButterflyEntity.class, TrackedDataHandlerRegistry.BOOLEAN);
|
||||||
private static final TrackedData<Integer> VARIANT = DataTracker.registerData(ButterflyEntity.class, TrackedDataHandlerRegistry.INTEGER);
|
private static final TrackedData<Integer> VARIANT = DataTracker.registerData(ButterflyEntity.class, TrackedDataHandlerRegistry.INTEGER);
|
||||||
|
|
||||||
@Nullable
|
private Optional<BlockPos> hoveringPosition = Optional.empty();
|
||||||
private BlockPos hoveringPosition;
|
private Optional<BlockPos> flowerPosition = Optional.empty();
|
||||||
|
|
||||||
|
private final Map<BlockPos, Long> visited = new HashMap<>();
|
||||||
|
|
||||||
private int ticksResting;
|
private int ticksResting;
|
||||||
|
private int breedingCooldown;
|
||||||
|
|
||||||
public ButterflyEntity(EntityType<ButterflyEntity> type, World world) {
|
public ButterflyEntity(EntityType<ButterflyEntity> type, World world) {
|
||||||
super(type, world);
|
super(type, world);
|
||||||
|
@ -57,6 +70,16 @@ public class ButterflyEntity extends AmbientEntity {
|
||||||
return SoundEvents.ENTITY_BAT_HURT;
|
return SoundEvents.ENTITY_BAT_HURT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Entity.MoveEffect getMoveEffect() {
|
||||||
|
return Entity.MoveEffect.EVENTS;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canAvoidTraps() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
protected SoundEvent getDeathSound() {
|
protected SoundEvent getDeathSound() {
|
||||||
|
@ -101,7 +124,8 @@ public class ButterflyEntity extends AmbientEntity {
|
||||||
public void setResting(boolean resting) {
|
public void setResting(boolean resting) {
|
||||||
getDataTracker().set(RESTING, resting);
|
getDataTracker().set(RESTING, resting);
|
||||||
if (!resting) {
|
if (!resting) {
|
||||||
hoveringPosition = null;
|
hoveringPosition = Optional.empty();
|
||||||
|
flowerPosition = Optional.empty();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -139,36 +163,116 @@ public class ButterflyEntity extends AmbientEntity {
|
||||||
public void tickMovement() {
|
public void tickMovement() {
|
||||||
super.tickMovement();
|
super.tickMovement();
|
||||||
|
|
||||||
|
if (breedingCooldown > 0) {
|
||||||
|
breedingCooldown--;
|
||||||
|
}
|
||||||
|
|
||||||
BlockPos below = new BlockPos(getPos().add(0, -0.5, 0));
|
BlockPos below = new BlockPos(getPos().add(0, -0.5, 0));
|
||||||
|
|
||||||
|
visited.entrySet().removeIf(e -> e.getValue() < age - 500);
|
||||||
|
|
||||||
if (isResting()) {
|
if (isResting()) {
|
||||||
|
if (flowerPosition.isPresent() && breed()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (world.getBlockState(below).isAir()
|
if (world.getBlockState(below).isAir()
|
||||||
|| !world.getOtherEntities(this, getBoundingBox().expand(7), this::isAggressor).isEmpty()
|
|| !world.getOtherEntities(this, getBoundingBox().expand(7), this::isAggressor).isEmpty()
|
||||||
|| (ticksResting++ > 40 && world.random.nextInt(500) == 0)
|
|| (ticksResting++ > 40 || world.random.nextInt(500) == 0)
|
||||||
|| world.hasRain(below)) {
|
|| world.hasRain(below)) {
|
||||||
setResting(false);
|
setResting(false);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ticksResting = 0;
|
ticksResting = 0;
|
||||||
|
|
||||||
// invalidate the hovering position
|
updateFlowerPosition().map(flower -> {
|
||||||
if (hoveringPosition != null && (!world.isAir(hoveringPosition) || hoveringPosition.getY() < 1)) {
|
if (flower.isWithinDistance(getPos(), 1)) {
|
||||||
hoveringPosition = null;
|
setResting(true);
|
||||||
|
visited.put(flower, (long)age);
|
||||||
|
if (breedingCooldown <= 0) {
|
||||||
|
breedingCooldown = MAX_BREEDING_COOLDOWN / 10;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return flower;
|
||||||
|
}).or(this::findNextHoverPosition).ifPresent(this::moveTowards);
|
||||||
|
|
||||||
|
if (random.nextInt(100) == 0 && world.getBlockState(below).isOpaque()) {
|
||||||
|
setResting(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!world.isClient && age % 20 == 0 && world.random.nextInt(200) == 0) {
|
||||||
|
if (!world.getOtherEntities(this, getBoundingBox().expand(20), i -> i.getType() == getType()).isEmpty()) {
|
||||||
|
breed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean breed() {
|
||||||
|
|
||||||
|
int others = 0;
|
||||||
|
for (Entity i : world.getOtherEntities(this, getBoundingBox().expand(20))) {
|
||||||
|
if (i.getType() == getType() && others++ > 3) {
|
||||||
|
setResting(false);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (age < 20 || breedingCooldown > 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
breedingCooldown = MAX_BREEDING_COOLDOWN;
|
||||||
|
|
||||||
|
ButterflyEntity copy = (ButterflyEntity)getType().create(world);
|
||||||
|
copy.copyPositionAndRotation(this);
|
||||||
|
world.spawnEntity(copy);
|
||||||
|
setResting(false);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Optional<BlockPos> findNextHoverPosition() {
|
||||||
|
// invalidate the hovering position
|
||||||
BlockPos pos = getBlockPos();
|
BlockPos pos = getBlockPos();
|
||||||
|
|
||||||
// select a new hovering position
|
return hoveringPosition = hoveringPosition.filter(p -> world.isAir(p)
|
||||||
if (hoveringPosition == null || random.nextInt(30) == 0 || hoveringPosition.getSquaredDistance(pos) < 4) {
|
&& p.getY() >= 1
|
||||||
hoveringPosition = pos.add(
|
&& random.nextInt(30) != 0
|
||||||
|
&& p.getSquaredDistance(pos) >= 4).or(() -> {
|
||||||
|
return Optional.of(pos.add(
|
||||||
random.nextInt(7) - random.nextInt(7),
|
random.nextInt(7) - random.nextInt(7),
|
||||||
random.nextInt(6) - 2,
|
random.nextInt(6) - 2,
|
||||||
random.nextInt(7) - random.nextInt(7)
|
random.nextInt(7) - random.nextInt(7)
|
||||||
);
|
));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// hover casually towards the chosen position
|
private Optional<BlockPos> updateFlowerPosition() {
|
||||||
Vec3d motion = Vec3d.ofCenter(hoveringPosition, 0.1).subtract(getPos());
|
|
||||||
|
if (flowerPosition.isPresent()) {
|
||||||
|
return flowerPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (age % 50 == 0) {
|
||||||
|
flowerPosition = findFlower(state -> {
|
||||||
|
return state.isIn(BlockTags.FLOWERS);
|
||||||
|
});
|
||||||
|
flowerPosition.ifPresent(p -> {
|
||||||
|
visited.put(p, (long)age - 900);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Optional<BlockPos> findFlower(Predicate<BlockState> predicate) {
|
||||||
|
return BlockPos.streamOutwards(this.getBlockPos(), 10, 10, 10).filter(p -> {
|
||||||
|
return !visited.containsKey(p) && predicate.test(world.getBlockState(p));
|
||||||
|
}).findFirst();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void moveTowards(BlockPos pos) {
|
||||||
|
Vec3d motion = Vec3d.ofCenter(pos, 0.1).subtract(getPos());
|
||||||
Vec3d vel = getVelocity();
|
Vec3d vel = getVelocity();
|
||||||
|
|
||||||
addVelocity(
|
addVelocity(
|
||||||
|
@ -181,23 +285,6 @@ public class ButterflyEntity extends AmbientEntity {
|
||||||
|
|
||||||
forwardSpeed = 0.5F;
|
forwardSpeed = 0.5F;
|
||||||
headYaw += MathHelper.wrapDegrees(direction - headYaw);
|
headYaw += MathHelper.wrapDegrees(direction - headYaw);
|
||||||
|
|
||||||
if (random.nextInt(100) == 0 && world.getBlockState(below).isOpaque()) {
|
|
||||||
setResting(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!world.isClient && age % 20 == 0 && world.random.nextInt(200) == 0) {
|
|
||||||
for (Entity i : world.getOtherEntities(this, getBoundingBox().expand(20))) {
|
|
||||||
if (i.getType() == getType()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ButterflyEntity copy = (ButterflyEntity)getType().create(world);
|
|
||||||
copy.copyPositionAndRotation(this);
|
|
||||||
world.spawnEntity(copy);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -225,6 +312,36 @@ public class ButterflyEntity extends AmbientEntity {
|
||||||
return getHeight() / 2;
|
return getHeight() / 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeCustomDataToNbt(NbtCompound nbt) {
|
||||||
|
super.writeCustomDataToNbt(nbt);
|
||||||
|
nbt.putInt("ticksResting", ticksResting);
|
||||||
|
nbt.putInt("breedingCooldown", breedingCooldown);
|
||||||
|
NbtSerialisable.writeBlockPos("hoveringPosition", hoveringPosition, nbt);
|
||||||
|
NbtSerialisable.writeBlockPos("flowerPosition", flowerPosition, nbt);
|
||||||
|
NbtCompound visited = new NbtCompound();
|
||||||
|
this.visited.forEach((pos, time) -> {
|
||||||
|
visited.putLong(String.valueOf(pos.asLong()), time);
|
||||||
|
});
|
||||||
|
nbt.put("visited", visited);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void readCustomDataFromNbt(NbtCompound nbt) {
|
||||||
|
super.readCustomDataFromNbt(nbt);
|
||||||
|
ticksResting = nbt.getInt("ticksResting");
|
||||||
|
breedingCooldown = nbt.getInt("breedingCooldown");
|
||||||
|
hoveringPosition = NbtSerialisable.readBlockPos("hoveringPosition", nbt);
|
||||||
|
flowerPosition = NbtSerialisable.readBlockPos("flowerPosition", nbt);
|
||||||
|
NbtCompound visited = nbt.getCompound("visited");
|
||||||
|
this.visited.clear();
|
||||||
|
visited.getKeys().forEach(key -> {
|
||||||
|
try {
|
||||||
|
this.visited.put(BlockPos.fromLong(Long.valueOf(key)), visited.getLong(key));
|
||||||
|
} catch (NumberFormatException ignore) {}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public enum Variant {
|
public enum Variant {
|
||||||
BUTTERFLY,
|
BUTTERFLY,
|
||||||
YELLOW,
|
YELLOW,
|
||||||
|
|
|
@ -35,7 +35,6 @@ import net.minecraft.item.ItemStack;
|
||||||
import net.minecraft.nbt.NbtCompound;
|
import net.minecraft.nbt.NbtCompound;
|
||||||
import net.minecraft.particle.ParticleTypes;
|
import net.minecraft.particle.ParticleTypes;
|
||||||
import net.minecraft.sound.SoundCategory;
|
import net.minecraft.sound.SoundCategory;
|
||||||
import net.minecraft.sound.SoundEvent;
|
|
||||||
import net.minecraft.sound.SoundEvents;
|
import net.minecraft.sound.SoundEvents;
|
||||||
import net.minecraft.util.math.BlockPos;
|
import net.minecraft.util.math.BlockPos;
|
||||||
import net.minecraft.util.math.MathHelper;
|
import net.minecraft.util.math.MathHelper;
|
||||||
|
|
|
@ -1,8 +1,12 @@
|
||||||
package com.minelittlepony.unicopia.util;
|
package com.minelittlepony.unicopia.util;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
import net.minecraft.nbt.NbtCompound;
|
import net.minecraft.nbt.NbtCompound;
|
||||||
import net.minecraft.nbt.NbtDouble;
|
import net.minecraft.nbt.NbtDouble;
|
||||||
|
import net.minecraft.nbt.NbtHelper;
|
||||||
import net.minecraft.nbt.NbtList;
|
import net.minecraft.nbt.NbtList;
|
||||||
|
import net.minecraft.util.math.BlockPos;
|
||||||
import net.minecraft.util.math.Vec3d;
|
import net.minecraft.util.math.Vec3d;
|
||||||
|
|
||||||
public interface NbtSerialisable {
|
public interface NbtSerialisable {
|
||||||
|
@ -41,4 +45,12 @@ public interface NbtSerialisable {
|
||||||
static Vec3d readVector(NbtList list) {
|
static Vec3d readVector(NbtList list) {
|
||||||
return new Vec3d(list.getDouble(0), list.getDouble(1), list.getDouble(2));
|
return new Vec3d(list.getDouble(0), list.getDouble(1), list.getDouble(2));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void writeBlockPos(String name, Optional<BlockPos> pos, NbtCompound nbt) {
|
||||||
|
pos.map(NbtHelper::fromBlockPos).ifPresent(p -> nbt.put("hoveringPosition", p));
|
||||||
|
}
|
||||||
|
|
||||||
|
static Optional<BlockPos> readBlockPos(String name, NbtCompound nbt) {
|
||||||
|
return nbt.contains(name) ? Optional.ofNullable(NbtHelper.toBlockPos(nbt.getCompound(name))) : Optional.empty();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue