Unicopia/src/main/java/com/minelittlepony/unicopia/entity/SpellbookEntity.java

331 lines
12 KiB
Java
Raw Normal View History

2021-11-09 17:29:55 +01:00
package com.minelittlepony.unicopia.entity;
import java.util.Optional;
2021-11-09 17:29:55 +01:00
import com.minelittlepony.unicopia.EquinePredicates;
import com.minelittlepony.unicopia.USounds;
import com.minelittlepony.unicopia.UTags;
2021-11-12 23:05:42 +01:00
import com.minelittlepony.unicopia.container.SpellbookScreenHandler;
import com.minelittlepony.unicopia.container.SpellbookState;
import com.minelittlepony.unicopia.entity.player.MeteorlogicalUtil;
2021-11-09 17:29:55 +01:00
import com.minelittlepony.unicopia.item.UItems;
import com.minelittlepony.unicopia.network.Channel;
import com.minelittlepony.unicopia.network.MsgSpellbookStateChanged;
import com.minelittlepony.unicopia.server.world.Altar;
2021-11-09 17:29:55 +01:00
import net.fabricmc.fabric.api.screenhandler.v1.ExtendedScreenHandlerFactory;
2021-11-09 20:42:01 +01:00
import net.fabricmc.fabric.api.util.TriState;
import net.minecraft.block.Blocks;
2021-11-09 17:29:55 +01:00
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.damage.DamageSource;
import net.minecraft.entity.data.DataTracker;
import net.minecraft.entity.data.TrackedData;
import net.minecraft.entity.data.TrackedDataHandlerRegistry;
import net.minecraft.entity.mob.MobEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.player.PlayerInventory;
2021-11-09 17:29:55 +01:00
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.network.PacketByteBuf;
2021-11-09 17:29:55 +01:00
import net.minecraft.particle.ParticleTypes;
import net.minecraft.screen.*;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.server.world.ServerWorld;
2021-11-09 17:29:55 +01:00
import net.minecraft.sound.BlockSoundGroup;
import net.minecraft.sound.SoundCategory;
import net.minecraft.text.Text;
2021-11-09 17:29:55 +01:00
import net.minecraft.util.ActionResult;
import net.minecraft.util.Hand;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.GameRules;
import net.minecraft.world.World;
public class SpellbookEntity extends MobEntity {
2021-11-09 20:42:01 +01:00
private static final TrackedData<Byte> LOCKED = DataTracker.registerData(SpellbookEntity.class, TrackedDataHandlerRegistry.BYTE);
2021-11-09 17:29:55 +01:00
private static final TrackedData<Boolean> ALTERED = DataTracker.registerData(SpellbookEntity.class, TrackedDataHandlerRegistry.BOOLEAN);
2021-11-09 20:42:01 +01:00
private static final int TICKS_TO_SLEEP = 600;
2021-11-09 20:42:01 +01:00
private int activeTicks = TICKS_TO_SLEEP;
private boolean prevDaytime;
2021-11-09 17:29:55 +01:00
private final SpellbookState state = new SpellbookState();
private Optional<Altar> altar = Optional.empty();
2021-11-09 17:29:55 +01:00
public SpellbookEntity(EntityType<SpellbookEntity> type, World world) {
super(type, world);
setPersistent();
2021-11-09 20:42:01 +01:00
setAltered(world.random.nextInt(3) == 0);
if (!world.isClient) {
state.setSynchronizer(state -> {
getWorld().getPlayers().forEach(player -> {
if (player instanceof ServerPlayerEntity recipient
&& player.currentScreenHandler instanceof SpellbookScreenHandler book
&& getUuid().equals(book.entityId)) {
2023-06-03 13:40:54 +02:00
Channel.SERVER_SPELLBOOK_UPDATE.sendToPlayer(new MsgSpellbookStateChanged<>(book.syncId, state), recipient);
}
});
});
}
2021-11-09 17:29:55 +01:00
}
@Override
protected void initDataTracker() {
super.initDataTracker();
2021-11-09 20:42:01 +01:00
dataTracker.startTracking(LOCKED, (byte)1);
2021-11-09 17:29:55 +01:00
dataTracker.startTracking(ALTERED, false);
}
public SpellbookState getSpellbookState() {
return state;
}
2021-11-09 17:29:55 +01:00
@Override
public ItemStack getPickBlockStack() {
ItemStack stack = UItems.SPELLBOOK.getDefaultStack();
stack.getOrCreateNbt().put("spellbookState", state.toNBT());
return stack;
2021-11-09 17:29:55 +01:00
}
@Override
public boolean isPushable() {
return false;
}
@Override
public boolean doesRenderOnFire() {
return false;
}
public void setAltar(Altar altar) {
this.altar = Optional.of(altar);
}
2021-11-09 20:42:01 +01:00
public boolean isAltered() {
2021-11-09 17:29:55 +01:00
return dataTracker.get(ALTERED);
}
2021-11-09 20:42:01 +01:00
public void setAltered(boolean altered) {
dataTracker.set(ALTERED, altered);
}
protected void setForcedState(TriState state) {
dataTracker.set(LOCKED, (byte)state.ordinal());
2021-11-09 17:29:55 +01:00
}
public void clearForcedState() {
setForcedState(TriState.DEFAULT);
activeTicks = 0;
2021-11-09 20:42:01 +01:00
}
private TriState getForcedState() {
if (activeTicks <= 0) {
setForcedState(TriState.DEFAULT);
2021-11-09 20:42:01 +01:00
}
return TriState.values()[Math.abs(dataTracker.get(LOCKED)) % 3];
2021-11-09 17:29:55 +01:00
}
public boolean isOpen() {
return getForcedState().orElse(!shouldBeSleeping());
2021-11-09 17:29:55 +01:00
}
public void keepAwake() {
activeTicks = 100;
2021-11-09 17:29:55 +01:00
}
@Override
public void tick() {
boolean open = isOpen();
2021-11-09 17:29:55 +01:00
super.tick();
2021-11-09 20:42:01 +01:00
if (open && isTouchingWater()) {
addVelocity(0, 0.01, 0);
keepAwake();
}
if (activeTicks > 0) {
activeTicks--;
}
if (getWorld().isClient && open) {
2021-11-09 17:29:55 +01:00
for (int offX = -2; offX <= 1; ++offX) {
for (int offZ = -2; offZ <= 1; ++offZ) {
if (offX > -1 && offX < 1 && offZ == -1) {
offZ = 1;
}
if (random.nextInt(320) == 0) {
for (int offY = 0; offY <= 1; ++offY) {
2023-06-03 13:40:54 +02:00
getWorld().addParticle(ParticleTypes.ENCHANT,
2021-11-09 17:29:55 +01:00
getX(), getY(), getZ(),
offX/2F + random.nextFloat(),
offY/2F - random.nextFloat() + 0.5f,
offZ/2F + random.nextFloat()
);
}
}
}
}
}
getWorld().getOtherEntities(this, getBoundingBox().expand(2), EquinePredicates.PLAYER_UNICORN.and(e -> e instanceof PlayerEntity)).stream().findFirst().ifPresent(player -> {
keepAwake();
if (open) {
Vec3d diff = player.getPos().subtract(getPos());
double yaw = Math.atan2(diff.z, diff.x) * 180D / Math.PI - 90;
2021-11-09 17:29:55 +01:00
setHeadYaw((float)yaw);
setBodyYaw((float)yaw);
2021-11-09 20:42:01 +01:00
}
});
2021-11-09 17:29:55 +01:00
boolean daytime = MeteorlogicalUtil.getSkyAngle(getWorld()) < 1;
if (daytime != prevDaytime) {
prevDaytime = daytime;
if (daytime != getForcedState().orElse(daytime)) {
clearForcedState();
2021-11-09 17:29:55 +01:00
}
}
if (!getWorld().isClient && age % 15 == 0) {
altar.ifPresent(altar -> {
if (!altar.isValid(getWorld())) {
altar.tearDown(null, getWorld());
return;
}
altar.pillars().forEach(pillar -> {
Vec3d center = pillar.toCenterPos().add(
random.nextTriangular(0.5, 0.2),
random.nextTriangular(0.5, 0.2),
random.nextTriangular(0.5, 0.2)
);
((ServerWorld)getWorld()).spawnParticles(
ParticleTypes.SOUL_FIRE_FLAME,
center.x - 0.5, center.y + 0.5, center.z - 0.5,
0,
0.5, 0.5, 0.5, 0);
if (random.nextInt(12) != 0) {
return;
}
Vec3d vel = center.subtract(this.altar.get().origin().toCenterPos()).normalize();
((ServerWorld)getWorld()).spawnParticles(
ParticleTypes.SOUL_FIRE_FLAME,
center.x - 0.5, center.y + 0.5, center.z - 0.5,
0,
vel.x, vel.y, vel.z, -0.2);
if (random.nextInt(2000) == 0) {
if (getWorld().getBlockState(pillar).isOf(Blocks.CRYING_OBSIDIAN)) {
pillar = pillar.down();
}
getWorld().setBlockState(pillar, Blocks.CRYING_OBSIDIAN.getDefaultState());
}
});
});
}
2021-11-09 17:29:55 +01:00
}
private boolean shouldBeSleeping() {
return MeteorlogicalUtil.getSkyAngle(getWorld()) > 1 && activeTicks <= 0;
}
2021-11-09 17:29:55 +01:00
@Override
public boolean damage(DamageSource source, float amount) {
2023-06-03 13:40:54 +02:00
if (!getWorld().isClient) {
2021-11-09 17:29:55 +01:00
remove(Entity.RemovalReason.KILLED);
BlockSoundGroup sound = BlockSoundGroup.WOOD;
2023-06-03 13:40:54 +02:00
getWorld().playSound(getX(), getY(), getZ(), sound.getBreakSound(), SoundCategory.BLOCKS, sound.getVolume(), sound.getPitch(), true);
2021-11-09 17:29:55 +01:00
2023-06-03 13:40:54 +02:00
if (getWorld().getGameRules().getBoolean(GameRules.DO_TILE_DROPS)) {
dropStack(getPickBlockStack(), 1);
2021-11-09 17:29:55 +01:00
}
}
return false;
}
@Override
public void remove(RemovalReason reason) {
super.remove(reason);
altar.ifPresent(altar -> altar.tearDown(this, getWorld()));
}
2021-11-09 17:29:55 +01:00
@Override
public ActionResult interactAt(PlayerEntity player, Vec3d vec, Hand hand) {
if (player.isSneaking()) {
setForcedState(TriState.of(!isOpen()));
keepAwake();
player.playSound(USounds.Vanilla.ITEM_BOOK_PAGE_TURN, 2, 1);
2021-11-09 17:29:55 +01:00
return ActionResult.SUCCESS;
}
2022-09-21 22:58:29 +02:00
if (isOpen()) {
keepAwake();
player.openHandledScreen(new ExtendedScreenHandlerFactory() {
@Override
public Text getDisplayName() {
return SpellbookEntity.this.getDisplayName();
}
@Override
public ScreenHandler createMenu(int syncId, PlayerInventory inv, PlayerEntity player) {
2023-06-03 13:40:54 +02:00
return new SpellbookScreenHandler(syncId, inv, ScreenHandlerContext.create(getWorld(), getBlockPos()), state, getUuid());
}
@Override
public void writeScreenOpeningData(ServerPlayerEntity player, PacketByteBuf buf) {
state.toPacket(buf);
}
});
player.playSound(USounds.Vanilla.ITEM_BOOK_PAGE_TURN, 2, 1);
2021-11-09 17:29:55 +01:00
return ActionResult.SUCCESS;
}
return ActionResult.PASS;
}
@Override
public boolean isImmuneToExplosion() {
return true;
}
@Override
public boolean isInvulnerableTo(DamageSource damageSource) {
return super.isInvulnerableTo(damageSource) || damageSource.isIn(UTags.SPELLBOOK_IMMUNE_TO);
}
2021-11-09 17:29:55 +01:00
@Override
public void readCustomDataFromNbt(NbtCompound compound) {
super.readCustomDataFromNbt(compound);
prevDaytime = compound.getBoolean("prevDaytime");
activeTicks = compound.getInt("activeTicks");
2021-11-09 20:42:01 +01:00
setAltered(compound.getBoolean("altered"));
setForcedState(compound.contains("locked") ? TriState.of(compound.getBoolean("locked")) : TriState.DEFAULT);
state.fromNBT(compound.getCompound("spellbookState"));
altar = Altar.SERIALIZER.readOptional("altar", compound);
2021-11-09 17:29:55 +01:00
}
@Override
public void writeCustomDataToNbt(NbtCompound compound) {
super.writeCustomDataToNbt(compound);
compound.putInt("activeTicks", activeTicks);
compound.putBoolean("prevDaytime", prevDaytime);
2021-11-09 20:42:01 +01:00
compound.putBoolean("altered", isAltered());
compound.put("spellbookState", state.toNBT());
getForcedState().map(t -> {
compound.putBoolean("locked", t);
return null;
});
Altar.SERIALIZER.writeOptional("altar", compound, altar);
2021-11-09 17:29:55 +01:00
}
}