mirror of
https://github.com/Sollace/Unicopia.git
synced 2024-11-27 23:27:59 +01:00
Added a proper summoning method to spawn sombra
This commit is contained in:
parent
0320976d47
commit
e08820b921
8 changed files with 303 additions and 12 deletions
|
@ -77,7 +77,7 @@ public interface TreeType {
|
||||||
* Recursively locates the base of the tree.
|
* Recursively locates the base of the tree.
|
||||||
*/
|
*/
|
||||||
default Optional<BlockPos> findBase(World w, BlockPos pos) {
|
default Optional<BlockPos> findBase(World w, BlockPos pos) {
|
||||||
return findBase(new HashSet<BlockPos>(), w, new BlockPos.Mutable(pos.getX(), pos.getY(), pos.getZ()));
|
return findBase(new HashSet<>(), w, new BlockPos.Mutable(pos.getX(), pos.getY(), pos.getZ()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Optional<BlockPos> findBase(Set<BlockPos> done, World w, BlockPos.Mutable pos) {
|
private Optional<BlockPos> findBase(Set<BlockPos> done, World w, BlockPos.Mutable pos) {
|
||||||
|
@ -91,7 +91,7 @@ public interface TreeType {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isWide()) {
|
if (isWide()) {
|
||||||
PosHelper.all(pos.toImmutable(), p -> findBase(done, w, new BlockPos.Mutable(p.getX(), p.getY(), p.getZ()))
|
PosHelper.all(pos.toImmutable(), p -> findBase(done, w, p.mutableCopy())
|
||||||
.filter(a -> a.getY() < pos.getY())
|
.filter(a -> a.getY() < pos.getY())
|
||||||
.ifPresent(pos::set), PosHelper.HORIZONTAL);
|
.ifPresent(pos::set), PosHelper.HORIZONTAL);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,11 @@
|
||||||
package com.minelittlepony.unicopia.entity;
|
package com.minelittlepony.unicopia.entity;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
import com.minelittlepony.unicopia.USounds;
|
import com.minelittlepony.unicopia.USounds;
|
||||||
import com.minelittlepony.unicopia.entity.damage.UDamageSources;
|
import com.minelittlepony.unicopia.entity.damage.UDamageSources;
|
||||||
import com.minelittlepony.unicopia.item.UItems;
|
import com.minelittlepony.unicopia.item.UItems;
|
||||||
|
import com.minelittlepony.unicopia.server.world.Altar;
|
||||||
|
|
||||||
import net.minecraft.entity.Entity;
|
import net.minecraft.entity.Entity;
|
||||||
import net.minecraft.entity.EntityType;
|
import net.minecraft.entity.EntityType;
|
||||||
|
@ -36,6 +39,8 @@ public class FloatingArtefactEntity extends Entity implements UDamageSources {
|
||||||
private float spinChange;
|
private float spinChange;
|
||||||
private float spinChangeProgress;
|
private float spinChangeProgress;
|
||||||
|
|
||||||
|
private Optional<Altar> altar = Optional.empty();
|
||||||
|
|
||||||
public FloatingArtefactEntity(EntityType<?> entityType, World world) {
|
public FloatingArtefactEntity(EntityType<?> entityType, World world) {
|
||||||
super(entityType, world);
|
super(entityType, world);
|
||||||
|
|
||||||
|
@ -49,6 +54,10 @@ public class FloatingArtefactEntity extends Entity implements UDamageSources {
|
||||||
dataTracker.startTracking(SPIN, 1F);
|
dataTracker.startTracking(SPIN, 1F);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setAltar(Altar altar) {
|
||||||
|
this.altar = Optional.of(altar);
|
||||||
|
}
|
||||||
|
|
||||||
public ItemStack getStack() {
|
public ItemStack getStack() {
|
||||||
return dataTracker.get(ITEM);
|
return dataTracker.get(ITEM);
|
||||||
}
|
}
|
||||||
|
@ -144,6 +153,7 @@ public class FloatingArtefactEntity extends Entity implements UDamageSources {
|
||||||
setStack(ItemStack.fromNbt(compound.getCompound("Item")));
|
setStack(ItemStack.fromNbt(compound.getCompound("Item")));
|
||||||
setState(State.valueOf(compound.getInt("State")));
|
setState(State.valueOf(compound.getInt("State")));
|
||||||
setSpin(compound.getFloat("spin"));
|
setSpin(compound.getFloat("spin"));
|
||||||
|
altar = Altar.SERIALIZER.readOptional("altar", compound);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -154,6 +164,7 @@ public class FloatingArtefactEntity extends Entity implements UDamageSources {
|
||||||
}
|
}
|
||||||
compound.putInt("State", getState().ordinal());
|
compound.putInt("State", getState().ordinal());
|
||||||
compound.putFloat("spin", getSpin());
|
compound.putFloat("spin", getSpin());
|
||||||
|
Altar.SERIALIZER.writeOptional("altar", compound, altar);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -170,14 +181,22 @@ public class FloatingArtefactEntity extends Entity implements UDamageSources {
|
||||||
|
|
||||||
ItemStack stack = getStack();
|
ItemStack stack = getStack();
|
||||||
|
|
||||||
if (!(stack.getItem() instanceof Artifact) || ((Artifact)stack.getItem()).onArtifactDestroyed(this) != ActionResult.SUCCESS) {
|
if (altar.isEmpty()) {
|
||||||
dropStack(stack);
|
if (!(stack.getItem() instanceof Artifact) || ((Artifact)stack.getItem()).onArtifactDestroyed(this) != ActionResult.SUCCESS) {
|
||||||
|
dropStack(stack);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void remove(RemovalReason reason) {
|
||||||
|
super.remove(reason);
|
||||||
|
altar.ifPresent(altar -> altar.tearDown(this, getWorld()));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canHit() {
|
public boolean canHit() {
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
package com.minelittlepony.unicopia.entity;
|
package com.minelittlepony.unicopia.entity;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
import com.minelittlepony.unicopia.EquinePredicates;
|
import com.minelittlepony.unicopia.EquinePredicates;
|
||||||
import com.minelittlepony.unicopia.USounds;
|
import com.minelittlepony.unicopia.USounds;
|
||||||
import com.minelittlepony.unicopia.UTags;
|
import com.minelittlepony.unicopia.UTags;
|
||||||
|
@ -9,9 +11,11 @@ import com.minelittlepony.unicopia.entity.player.MeteorlogicalUtil;
|
||||||
import com.minelittlepony.unicopia.item.UItems;
|
import com.minelittlepony.unicopia.item.UItems;
|
||||||
import com.minelittlepony.unicopia.network.Channel;
|
import com.minelittlepony.unicopia.network.Channel;
|
||||||
import com.minelittlepony.unicopia.network.MsgSpellbookStateChanged;
|
import com.minelittlepony.unicopia.network.MsgSpellbookStateChanged;
|
||||||
|
import com.minelittlepony.unicopia.server.world.Altar;
|
||||||
|
|
||||||
import net.fabricmc.fabric.api.screenhandler.v1.ExtendedScreenHandlerFactory;
|
import net.fabricmc.fabric.api.screenhandler.v1.ExtendedScreenHandlerFactory;
|
||||||
import net.fabricmc.fabric.api.util.TriState;
|
import net.fabricmc.fabric.api.util.TriState;
|
||||||
|
import net.minecraft.block.Blocks;
|
||||||
import net.minecraft.entity.Entity;
|
import net.minecraft.entity.Entity;
|
||||||
import net.minecraft.entity.EntityType;
|
import net.minecraft.entity.EntityType;
|
||||||
import net.minecraft.entity.damage.DamageSource;
|
import net.minecraft.entity.damage.DamageSource;
|
||||||
|
@ -27,6 +31,7 @@ import net.minecraft.network.PacketByteBuf;
|
||||||
import net.minecraft.particle.ParticleTypes;
|
import net.minecraft.particle.ParticleTypes;
|
||||||
import net.minecraft.screen.*;
|
import net.minecraft.screen.*;
|
||||||
import net.minecraft.server.network.ServerPlayerEntity;
|
import net.minecraft.server.network.ServerPlayerEntity;
|
||||||
|
import net.minecraft.server.world.ServerWorld;
|
||||||
import net.minecraft.sound.BlockSoundGroup;
|
import net.minecraft.sound.BlockSoundGroup;
|
||||||
import net.minecraft.sound.SoundCategory;
|
import net.minecraft.sound.SoundCategory;
|
||||||
import net.minecraft.text.Text;
|
import net.minecraft.text.Text;
|
||||||
|
@ -47,6 +52,8 @@ public class SpellbookEntity extends MobEntity {
|
||||||
|
|
||||||
private final SpellbookState state = new SpellbookState();
|
private final SpellbookState state = new SpellbookState();
|
||||||
|
|
||||||
|
private Optional<Altar> altar = Optional.empty();
|
||||||
|
|
||||||
public SpellbookEntity(EntityType<SpellbookEntity> type, World world) {
|
public SpellbookEntity(EntityType<SpellbookEntity> type, World world) {
|
||||||
super(type, world);
|
super(type, world);
|
||||||
setPersistent();
|
setPersistent();
|
||||||
|
@ -92,6 +99,10 @@ public class SpellbookEntity extends MobEntity {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setAltar(Altar altar) {
|
||||||
|
this.altar = Optional.of(altar);
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isAltered() {
|
public boolean isAltered() {
|
||||||
return dataTracker.get(ALTERED);
|
return dataTracker.get(ALTERED);
|
||||||
}
|
}
|
||||||
|
@ -177,6 +188,49 @@ public class SpellbookEntity extends MobEntity {
|
||||||
clearForcedState();
|
clearForcedState();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean shouldBeSleeping() {
|
private boolean shouldBeSleeping() {
|
||||||
|
@ -199,6 +253,12 @@ public class SpellbookEntity extends MobEntity {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void remove(RemovalReason reason) {
|
||||||
|
super.remove(reason);
|
||||||
|
altar.ifPresent(altar -> altar.tearDown(this, getWorld()));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ActionResult interactAt(PlayerEntity player, Vec3d vec, Hand hand) {
|
public ActionResult interactAt(PlayerEntity player, Vec3d vec, Hand hand) {
|
||||||
if (player.isSneaking()) {
|
if (player.isSneaking()) {
|
||||||
|
@ -251,6 +311,7 @@ public class SpellbookEntity extends MobEntity {
|
||||||
setAltered(compound.getBoolean("altered"));
|
setAltered(compound.getBoolean("altered"));
|
||||||
setForcedState(compound.contains("locked") ? TriState.of(compound.getBoolean("locked")) : TriState.DEFAULT);
|
setForcedState(compound.contains("locked") ? TriState.of(compound.getBoolean("locked")) : TriState.DEFAULT);
|
||||||
state.fromNBT(compound.getCompound("spellbookState"));
|
state.fromNBT(compound.getCompound("spellbookState"));
|
||||||
|
altar = Altar.SERIALIZER.readOptional("altar", compound);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -264,5 +325,6 @@ public class SpellbookEntity extends MobEntity {
|
||||||
compound.putBoolean("locked", t);
|
compound.putBoolean("locked", t);
|
||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
|
Altar.SERIALIZER.writeOptional("altar", compound, altar);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@ import com.minelittlepony.unicopia.entity.player.*;
|
||||||
import com.minelittlepony.unicopia.particle.FollowingParticleEffect;
|
import com.minelittlepony.unicopia.particle.FollowingParticleEffect;
|
||||||
import com.minelittlepony.unicopia.particle.ParticleUtils;
|
import com.minelittlepony.unicopia.particle.ParticleUtils;
|
||||||
import com.minelittlepony.unicopia.particle.UParticles;
|
import com.minelittlepony.unicopia.particle.UParticles;
|
||||||
|
import com.minelittlepony.unicopia.server.world.Altar;
|
||||||
import com.minelittlepony.unicopia.trinkets.TrinketsDelegate;
|
import com.minelittlepony.unicopia.trinkets.TrinketsDelegate;
|
||||||
import com.minelittlepony.unicopia.util.VecHelper;
|
import com.minelittlepony.unicopia.util.VecHelper;
|
||||||
|
|
||||||
|
@ -21,6 +22,7 @@ import it.unimi.dsi.fastutil.floats.Float2ObjectFunction;
|
||||||
import net.fabricmc.api.EnvType;
|
import net.fabricmc.api.EnvType;
|
||||||
import net.fabricmc.api.Environment;
|
import net.fabricmc.api.Environment;
|
||||||
import net.fabricmc.fabric.api.item.v1.FabricItemSettings;
|
import net.fabricmc.fabric.api.item.v1.FabricItemSettings;
|
||||||
|
import net.minecraft.block.Blocks;
|
||||||
import net.minecraft.client.MinecraftClient;
|
import net.minecraft.client.MinecraftClient;
|
||||||
import net.minecraft.client.item.TooltipContext;
|
import net.minecraft.client.item.TooltipContext;
|
||||||
import net.minecraft.enchantment.EnchantmentHelper;
|
import net.minecraft.enchantment.EnchantmentHelper;
|
||||||
|
@ -46,6 +48,7 @@ import net.minecraft.util.math.Vec3d;
|
||||||
import net.minecraft.util.math.random.Random;
|
import net.minecraft.util.math.random.Random;
|
||||||
import net.minecraft.world.LocalDifficulty;
|
import net.minecraft.world.LocalDifficulty;
|
||||||
import net.minecraft.world.World;
|
import net.minecraft.world.World;
|
||||||
|
import net.minecraft.world.World.ExplosionSourceType;
|
||||||
|
|
||||||
public class AlicornAmuletItem extends AmuletItem implements ItemTracker.Trackable, ItemImpl.ClingyItem, ItemImpl.GroundTickCallback {
|
public class AlicornAmuletItem extends AmuletItem implements ItemTracker.Trackable, ItemImpl.ClingyItem, ItemImpl.GroundTickCallback {
|
||||||
private static final UUID EFFECT_UUID = UUID.fromString("c0a870f5-99ef-4716-a23e-f320ee834b26");
|
private static final UUID EFFECT_UUID = UUID.fromString("c0a870f5-99ef-4716-a23e-f320ee834b26");
|
||||||
|
@ -186,6 +189,23 @@ public class AlicornAmuletItem extends AmuletItem implements ItemTracker.Trackab
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (entity instanceof PlayerEntity) {
|
||||||
|
if (entity.isOnFire() && world.getBlockState(entity.getBlockPos().up()).isOf(Blocks.SOUL_FIRE)) {
|
||||||
|
if (Altar.of(entity.getBlockPos().up()).isValid(world)) {
|
||||||
|
if (living.asEntity().getHealth() < 2) {
|
||||||
|
entity.setFireTicks(0);
|
||||||
|
world.removeBlock(entity.getBlockPos().up(), false);
|
||||||
|
stack.decrement(1);
|
||||||
|
world.createExplosion(null, entity.getX(), entity.getY(), entity.getZ(), 0, ExplosionSourceType.NONE);
|
||||||
|
world.playSound(null, entity.getBlockPos(), USounds.ENTITY_SOMBRA_LAUGH, SoundCategory.AMBIENT, 10, 1);
|
||||||
|
|
||||||
|
SombraEntity.startEncounter(world, entity.getBlockPos());
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
final long attachedTicks = living.getArmour().getTicks(this);
|
final long attachedTicks = living.getArmour().getTicks(this);
|
||||||
final long daysAttached = attachedTicks / ItemTracker.DAYS;
|
final long daysAttached = attachedTicks / ItemTracker.DAYS;
|
||||||
final boolean fullSecond = attachedTicks % ItemTracker.SECONDS == 0;
|
final boolean fullSecond = attachedTicks % ItemTracker.SECONDS == 0;
|
||||||
|
@ -257,11 +277,6 @@ public class AlicornAmuletItem extends AmuletItem implements ItemTracker.Trackab
|
||||||
player.getHungerManager().addExhaustion(90F);
|
player.getHungerManager().addExhaustion(90F);
|
||||||
float healthDrop = MathHelper.clamp(player.getMaxHealth() - player.getHealth(), 2, 5);
|
float healthDrop = MathHelper.clamp(player.getMaxHealth() - player.getHealth(), 2, 5);
|
||||||
player.damage(pony.damageOf(UDamageTypes.ALICORN_AMULET), healthDrop);
|
player.damage(pony.damageOf(UDamageTypes.ALICORN_AMULET), healthDrop);
|
||||||
|
|
||||||
if (player.getHealth() < 2) {
|
|
||||||
stack.decrement(1);
|
|
||||||
SombraEntity.startEncounter(player.getWorld(), player.getBlockPos());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -4,6 +4,7 @@ import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import com.minelittlepony.unicopia.entity.SpellbookEntity;
|
import com.minelittlepony.unicopia.entity.SpellbookEntity;
|
||||||
import com.minelittlepony.unicopia.entity.UEntities;
|
import com.minelittlepony.unicopia.entity.UEntities;
|
||||||
|
import com.minelittlepony.unicopia.server.world.Altar;
|
||||||
import com.minelittlepony.unicopia.util.Dispensable;
|
import com.minelittlepony.unicopia.util.Dispensable;
|
||||||
|
|
||||||
import net.minecraft.block.DispenserBlock;
|
import net.minecraft.block.DispenserBlock;
|
||||||
|
@ -71,6 +72,11 @@ public class SpellbookItem extends BookItem implements Dispensable {
|
||||||
}
|
}
|
||||||
|
|
||||||
world.spawnEntity(book);
|
world.spawnEntity(book);
|
||||||
|
|
||||||
|
Altar.locateAltar(world, book.getBlockPos()).ifPresent(altar -> {
|
||||||
|
book.setAltar(altar);
|
||||||
|
altar.generateDecorations(world);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -37,7 +37,9 @@ public interface ParticleUtils {
|
||||||
|
|
||||||
static void spawnParticle(World world, ParticleEffect effect, double x, double y, double z, double vX, double vY, double vZ) {
|
static void spawnParticle(World world, ParticleEffect effect, double x, double y, double z, double vX, double vY, double vZ) {
|
||||||
if (world instanceof ServerWorld sw) {
|
if (world instanceof ServerWorld sw) {
|
||||||
sw.spawnParticles(effect, x, y, z, 1, vX, vY, vZ, 0);
|
Vec3d vel = new Vec3d(vX, vY, vZ);
|
||||||
|
|
||||||
|
sw.spawnParticles(effect, x, y, z, 1, vX, vY, vZ, vel.length());
|
||||||
} else {
|
} else {
|
||||||
world.addParticle(effect, x, y, z, vX, vY, vZ);
|
world.addParticle(effect, x, y, z, vX, vY, vZ);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,183 @@
|
||||||
|
package com.minelittlepony.unicopia.server.world;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import com.minelittlepony.unicopia.entity.FloatingArtefactEntity;
|
||||||
|
import com.minelittlepony.unicopia.entity.SpellbookEntity;
|
||||||
|
import com.minelittlepony.unicopia.entity.UEntities;
|
||||||
|
import com.minelittlepony.unicopia.item.UItems;
|
||||||
|
import com.minelittlepony.unicopia.util.NbtSerialisable;
|
||||||
|
import com.minelittlepony.unicopia.util.PosHelper;
|
||||||
|
|
||||||
|
import net.minecraft.block.Block;
|
||||||
|
import net.minecraft.block.BlockState;
|
||||||
|
import net.minecraft.block.Blocks;
|
||||||
|
import net.minecraft.entity.Entity;
|
||||||
|
import net.minecraft.nbt.NbtCompound;
|
||||||
|
import net.minecraft.nbt.NbtElement;
|
||||||
|
import net.minecraft.predicate.entity.EntityPredicates;
|
||||||
|
import net.minecraft.registry.tag.BlockTags;
|
||||||
|
import net.minecraft.util.BlockRotation;
|
||||||
|
import net.minecraft.util.math.BlockPos;
|
||||||
|
import net.minecraft.util.math.Box;
|
||||||
|
import net.minecraft.util.math.Direction;
|
||||||
|
import net.minecraft.util.math.Vec3i;
|
||||||
|
import net.minecraft.world.World;
|
||||||
|
|
||||||
|
public record Altar(BlockPos origin, Set<BlockPos> pillars) {
|
||||||
|
private static final Direction[] HORIZONTALS = { Direction.SOUTH, Direction.WEST, Direction.NORTH, Direction.EAST };
|
||||||
|
private static final Predicate<Entity> IS_PARTICIPANT = EntityPredicates.VALID_ENTITY.and(e -> e instanceof FloatingArtefactEntity || e instanceof SpellbookEntity);
|
||||||
|
public static final NbtSerialisable.Serializer<Altar> SERIALIZER = NbtSerialisable.Serializer.of(nbt -> {
|
||||||
|
return new Altar(
|
||||||
|
NbtSerialisable.BLOCK_POS.read(nbt.getCompound("origin")),
|
||||||
|
new HashSet<>(NbtSerialisable.BLOCK_POS.readAll(nbt.getList("pillars", NbtElement.COMPOUND_TYPE)).toList())
|
||||||
|
);
|
||||||
|
}, altar -> {
|
||||||
|
NbtCompound compound = new NbtCompound();
|
||||||
|
compound.put("origin", NbtSerialisable.BLOCK_POS.write(altar.origin));
|
||||||
|
compound.put("pillars", NbtSerialisable.BLOCK_POS.writeAll(altar.pillars));
|
||||||
|
return compound;
|
||||||
|
});
|
||||||
|
|
||||||
|
private static final int INNER_RADIUS = 4;
|
||||||
|
private static final int PILLAR_OFFSET_FROM_CENTER = 2;
|
||||||
|
|
||||||
|
private static final BlockPos PILLAR_A = new BlockPos(INNER_RADIUS, 0, PILLAR_OFFSET_FROM_CENTER);
|
||||||
|
private static final BlockPos PILLAR_B = new BlockPos(INNER_RADIUS, 0, -PILLAR_OFFSET_FROM_CENTER);
|
||||||
|
|
||||||
|
private static final List<BlockPos> PILLAR_OFFSETS = Arrays.stream(BlockRotation.values()).flatMap(rotation -> {
|
||||||
|
return Stream.of(PILLAR_A.rotate(rotation), PILLAR_B.rotate(rotation));
|
||||||
|
}).toList();
|
||||||
|
|
||||||
|
public static Optional<Altar> locateAltar(World world, BlockPos startingPoint) {
|
||||||
|
|
||||||
|
BlockPos.Mutable mutable = startingPoint.mutableCopy();
|
||||||
|
|
||||||
|
if (!world.isSkyVisible(mutable)) {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
mutable.move(Direction.DOWN);
|
||||||
|
if (!world.getBlockState(mutable).isOf(Blocks.LODESTONE)) {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
mutable.set(startingPoint);
|
||||||
|
mutable.move(Direction.DOWN);
|
||||||
|
mutable.move(Direction.fromHorizontal(i), 2);
|
||||||
|
|
||||||
|
if (world.getBlockState(mutable).isOf(Blocks.SOUL_SAND)) {
|
||||||
|
if (checkSlab(world, mutable)) {
|
||||||
|
mutable.move(Direction.UP);
|
||||||
|
BlockState fireState = world.getBlockState(mutable);
|
||||||
|
if (!(fireState.isAir() || fireState.isIn(BlockTags.FIRE))) {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
BlockPos firePos = mutable.toImmutable();
|
||||||
|
mutable.move(Direction.DOWN);
|
||||||
|
|
||||||
|
final Set<BlockPos> pillars = new HashSet<>();
|
||||||
|
|
||||||
|
if (checkPillarPair(world, mutable, BlockRotation.NONE, pillars::add)
|
||||||
|
&& checkPillarPair(world, mutable, BlockRotation.CLOCKWISE_90, pillars::add)
|
||||||
|
&& checkPillarPair(world, mutable, BlockRotation.COUNTERCLOCKWISE_90, pillars::add)
|
||||||
|
&& checkPillarPair(world, mutable, BlockRotation.CLOCKWISE_180, pillars::add)) {
|
||||||
|
|
||||||
|
return Optional.of(new Altar(firePos, pillars));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Altar of(BlockPos center) {
|
||||||
|
return new Altar(center, new HashSet<>(PILLAR_OFFSETS.stream().map(p -> p.add(center).up()).toList()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean checkSlab(World world, BlockPos pos) {
|
||||||
|
return !PosHelper.any(pos, p -> !isObsidian(world, p), HORIZONTALS);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean checkPillarPair(World world, BlockPos.Mutable center, BlockRotation rotation, Consumer<BlockPos> pillarPosCollector) {
|
||||||
|
return checkPillar(world, center, PILLAR_A.rotate(rotation), pillarPosCollector)
|
||||||
|
&& checkPillar(world, center, PILLAR_B.rotate(rotation), pillarPosCollector);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean checkPillar(World world, BlockPos.Mutable pos, Vec3i pillarPos, Consumer<BlockPos> pillarPosCollector) {
|
||||||
|
int x = pos.getX();
|
||||||
|
int y = pos.getY();
|
||||||
|
int z = pos.getZ();
|
||||||
|
|
||||||
|
if (isObsidian(world, pos.move(pillarPos))
|
||||||
|
&& isObsidian(world, pos.move(Direction.UP))
|
||||||
|
&& isObsidian(world, pos.move(Direction.UP))) {
|
||||||
|
pillarPosCollector.accept(pos.toImmutable());
|
||||||
|
pos.set(x, y, z);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isObsidian(World world, BlockPos pos) {
|
||||||
|
BlockState state = world.getBlockState(pos);
|
||||||
|
return state.isOf(Blocks.OBSIDIAN) || state.isOf(Blocks.CRYING_OBSIDIAN);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean checkState(World world, BlockPos pos, Block block) {
|
||||||
|
return world.getBlockState(pos).isOf(block);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void generateDecorations(World world) {
|
||||||
|
world.setBlockState(origin, Blocks.SOUL_FIRE.getDefaultState(), Block.FORCE_STATE | Block.NOTIFY_ALL);
|
||||||
|
pillars.forEach(pillar -> {
|
||||||
|
/*
|
||||||
|
if (world.random.nextInt(3) == 0) {
|
||||||
|
world.setBlockState(pillar, Blocks.CRYING_OBSIDIAN.getDefaultState());
|
||||||
|
} else if (world.random.nextInt(3) == 0) {
|
||||||
|
world.setBlockState(pillar.down(), Blocks.CRYING_OBSIDIAN.getDefaultState());
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
FloatingArtefactEntity artefact = UEntities.FLOATING_ARTEFACT.create(world);
|
||||||
|
artefact.setStack(UItems.ALICORN_BADGE.getDefaultStack());
|
||||||
|
artefact.setPosition(pillar.up().toCenterPos());
|
||||||
|
artefact.setInvulnerable(true);
|
||||||
|
artefact.setAltar(this);
|
||||||
|
artefact.addSpin(2, 9000);
|
||||||
|
removeExisting(null, world, pillar);
|
||||||
|
world.spawnEntity(artefact);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void tearDown(@Nullable Entity except, World world) {
|
||||||
|
if (!(except instanceof SpellbookEntity)) {
|
||||||
|
world.getOtherEntities(except, new Box(origin).expand(3), IS_PARTICIPANT).forEach(Entity::kill);
|
||||||
|
}
|
||||||
|
pillars.forEach(pillar -> removeExisting(except, world, pillar));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeExisting(@Nullable Entity except, World world, BlockPos pillar) {
|
||||||
|
world.getOtherEntities(except, new Box(pillar.up()), IS_PARTICIPANT).forEach(Entity::kill);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isValid(World world) {
|
||||||
|
return checkState(world, origin, Blocks.SOUL_FIRE)
|
||||||
|
&& checkState(world, origin.down(), Blocks.SOUL_SAND)
|
||||||
|
&& checkSlab(world, origin.down())
|
||||||
|
&& pillars.stream().allMatch(pillar -> isObsidian(world, pillar) && isObsidian(world, pillar.down()) && isObsidian(world, pillar.down(2)));
|
||||||
|
}
|
||||||
|
}
|
|
@ -37,14 +37,18 @@ public interface PosHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
static void all(BlockPos origin, Consumer<BlockPos> consumer, Direction... directions) {
|
static void all(BlockPos origin, Consumer<BlockPos> consumer, Direction... directions) {
|
||||||
|
BlockPos.Mutable mutable = origin.mutableCopy();
|
||||||
for (Direction facing : directions) {
|
for (Direction facing : directions) {
|
||||||
consumer.accept(origin.offset(facing));
|
mutable.set(origin);
|
||||||
|
consumer.accept(mutable.move(facing));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static boolean any(BlockPos origin, Predicate<BlockPos> consumer, Direction... directions) {
|
static boolean any(BlockPos origin, Predicate<BlockPos> consumer, Direction... directions) {
|
||||||
|
BlockPos.Mutable mutable = origin.mutableCopy();
|
||||||
for (Direction facing : directions) {
|
for (Direction facing : directions) {
|
||||||
if (consumer.test(origin.offset(facing))) {
|
mutable.set(origin);
|
||||||
|
if (consumer.test(mutable.move(facing))) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue