mirror of
https://github.com/Sollace/Unicopia.git
synced 2025-03-03 16:51:28 +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;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Random;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import com.minelittlepony.unicopia.util.NbtSerialisable;
|
||||
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.entity.Entity;
|
||||
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.mob.AmbientEntity;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.nbt.NbtCompound;
|
||||
import net.minecraft.predicate.entity.EntityPredicates;
|
||||
import net.minecraft.sound.SoundEvent;
|
||||
import net.minecraft.sound.SoundEvents;
|
||||
import net.minecraft.tag.BlockTags;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
|
@ -28,13 +36,18 @@ import net.minecraft.world.World;
|
|||
import net.minecraft.world.WorldAccess;
|
||||
|
||||
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<Integer> VARIANT = DataTracker.registerData(ButterflyEntity.class, TrackedDataHandlerRegistry.INTEGER);
|
||||
|
||||
@Nullable
|
||||
private BlockPos hoveringPosition;
|
||||
private Optional<BlockPos> hoveringPosition = Optional.empty();
|
||||
private Optional<BlockPos> flowerPosition = Optional.empty();
|
||||
|
||||
private final Map<BlockPos, Long> visited = new HashMap<>();
|
||||
|
||||
private int ticksResting;
|
||||
private int breedingCooldown;
|
||||
|
||||
public ButterflyEntity(EntityType<ButterflyEntity> type, World world) {
|
||||
super(type, world);
|
||||
|
@ -57,6 +70,16 @@ public class ButterflyEntity extends AmbientEntity {
|
|||
return SoundEvents.ENTITY_BAT_HURT;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Entity.MoveEffect getMoveEffect() {
|
||||
return Entity.MoveEffect.EVENTS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canAvoidTraps() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
protected SoundEvent getDeathSound() {
|
||||
|
@ -101,7 +124,8 @@ public class ButterflyEntity extends AmbientEntity {
|
|||
public void setResting(boolean resting) {
|
||||
getDataTracker().set(RESTING, resting);
|
||||
if (!resting) {
|
||||
hoveringPosition = null;
|
||||
hoveringPosition = Optional.empty();
|
||||
flowerPosition = Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -139,67 +163,130 @@ public class ButterflyEntity extends AmbientEntity {
|
|||
public void tickMovement() {
|
||||
super.tickMovement();
|
||||
|
||||
if (breedingCooldown > 0) {
|
||||
breedingCooldown--;
|
||||
}
|
||||
|
||||
BlockPos below = new BlockPos(getPos().add(0, -0.5, 0));
|
||||
|
||||
visited.entrySet().removeIf(e -> e.getValue() < age - 500);
|
||||
|
||||
if (isResting()) {
|
||||
if (flowerPosition.isPresent() && breed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (world.getBlockState(below).isAir()
|
||||
|| !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)) {
|
||||
setResting(false);
|
||||
}
|
||||
} else {
|
||||
ticksResting = 0;
|
||||
|
||||
// invalidate the hovering position
|
||||
if (hoveringPosition != null && (!world.isAir(hoveringPosition) || hoveringPosition.getY() < 1)) {
|
||||
hoveringPosition = null;
|
||||
}
|
||||
updateFlowerPosition().map(flower -> {
|
||||
if (flower.isWithinDistance(getPos(), 1)) {
|
||||
setResting(true);
|
||||
visited.put(flower, (long)age);
|
||||
if (breedingCooldown <= 0) {
|
||||
breedingCooldown = MAX_BREEDING_COOLDOWN / 10;
|
||||
}
|
||||
}
|
||||
|
||||
BlockPos pos = getBlockPos();
|
||||
|
||||
// select a new hovering position
|
||||
if (hoveringPosition == null || random.nextInt(30) == 0 || hoveringPosition.getSquaredDistance(pos) < 4) {
|
||||
hoveringPosition = pos.add(
|
||||
random.nextInt(7) - random.nextInt(7),
|
||||
random.nextInt(6) - 2,
|
||||
random.nextInt(7) - random.nextInt(7)
|
||||
);
|
||||
}
|
||||
|
||||
// hover casually towards the chosen position
|
||||
Vec3d motion = Vec3d.ofCenter(hoveringPosition, 0.1).subtract(getPos());
|
||||
Vec3d vel = getVelocity();
|
||||
|
||||
addVelocity(
|
||||
(Math.signum(motion.getX()) * 0.5 - vel.x) * 0.1,
|
||||
(Math.signum(motion.getY()) * 0.7 - vel.y) * 0.1,
|
||||
(Math.signum(motion.getZ()) * 0.5 - vel.z) * 0.1
|
||||
);
|
||||
|
||||
float direction = (float)(MathHelper.atan2(vel.z, vel.x) * (180 / Math.PI)) - 90;
|
||||
|
||||
forwardSpeed = 0.5F;
|
||||
headYaw += MathHelper.wrapDegrees(direction - headYaw);
|
||||
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) {
|
||||
for (Entity i : world.getOtherEntities(this, getBoundingBox().expand(20))) {
|
||||
if (i.getType() == getType()) {
|
||||
return;
|
||||
}
|
||||
if (!world.getOtherEntities(this, getBoundingBox().expand(20), i -> i.getType() == getType()).isEmpty()) {
|
||||
breed();
|
||||
}
|
||||
|
||||
ButterflyEntity copy = (ButterflyEntity)getType().create(world);
|
||||
copy.copyPositionAndRotation(this);
|
||||
world.spawnEntity(copy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
return hoveringPosition = hoveringPosition.filter(p -> world.isAir(p)
|
||||
&& p.getY() >= 1
|
||||
&& random.nextInt(30) != 0
|
||||
&& p.getSquaredDistance(pos) >= 4).or(() -> {
|
||||
return Optional.of(pos.add(
|
||||
random.nextInt(7) - random.nextInt(7),
|
||||
random.nextInt(6) - 2,
|
||||
random.nextInt(7) - random.nextInt(7)
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
private Optional<BlockPos> updateFlowerPosition() {
|
||||
|
||||
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();
|
||||
|
||||
addVelocity(
|
||||
(Math.signum(motion.getX()) * 0.5 - vel.x) * 0.1,
|
||||
(Math.signum(motion.getY()) * 0.7 - vel.y) * 0.1,
|
||||
(Math.signum(motion.getZ()) * 0.5 - vel.z) * 0.1
|
||||
);
|
||||
|
||||
float direction = (float)(MathHelper.atan2(vel.z, vel.x) * (180 / Math.PI)) - 90;
|
||||
|
||||
forwardSpeed = 0.5F;
|
||||
headYaw += MathHelper.wrapDegrees(direction - headYaw);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldRender(double distance) {
|
||||
double d = 64 * getRenderDistanceMultiplier();
|
||||
|
@ -225,6 +312,36 @@ public class ButterflyEntity extends AmbientEntity {
|
|||
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 {
|
||||
BUTTERFLY,
|
||||
YELLOW,
|
||||
|
|
|
@ -35,7 +35,6 @@ import net.minecraft.item.ItemStack;
|
|||
import net.minecraft.nbt.NbtCompound;
|
||||
import net.minecraft.particle.ParticleTypes;
|
||||
import net.minecraft.sound.SoundCategory;
|
||||
import net.minecraft.sound.SoundEvent;
|
||||
import net.minecraft.sound.SoundEvents;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
package com.minelittlepony.unicopia.util;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import net.minecraft.nbt.NbtCompound;
|
||||
import net.minecraft.nbt.NbtDouble;
|
||||
import net.minecraft.nbt.NbtHelper;
|
||||
import net.minecraft.nbt.NbtList;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
|
||||
public interface NbtSerialisable {
|
||||
|
@ -41,4 +45,12 @@ public interface NbtSerialisable {
|
|||
static Vec3d readVector(NbtList list) {
|
||||
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