Skip to content

Commit

Permalink
feat(flesh-mound): make mound shapes persist across unloaded chunks a…
Browse files Browse the repository at this point in the history
…nd store mound shape seed in primordial cradle block/item
  • Loading branch information
Elenterius committed Nov 29, 2023
1 parent bdfa12f commit 32c2efd
Show file tree
Hide file tree
Showing 28 changed files with 1,125 additions and 204 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ protected static LootTable.Builder createPrimordialCradleTable(Block block) {
return LootTable.lootTable().withPool(applyExplosionCondition(block, LootPool.lootPool().setRolls(ConstantValue.exactly(1))
.add(LootItem.lootTableItem(block)
.apply(CopyNbtFunction.copyData(ContextNbtProvider.BLOCK_ENTITY).copy("PrimalEnergy", "BlockEntityTag.PrimalEnergy"))
.apply(CopyNbtFunction.copyData(ContextNbtProvider.BLOCK_ENTITY).copy("ProcGenValues", "BlockEntityTag.ProcGenValues"))
.apply(CopyNbtFunction.copyData(ContextNbtProvider.BLOCK_ENTITY).copy("SacrificeHandler", "BlockEntityTag.SacrificeHandler"))
)));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@
import com.github.elenterius.biomancy.network.ModNetworkHandler;
import com.github.elenterius.biomancy.tribute.Tribute;
import com.github.elenterius.biomancy.util.SoundUtil;
import com.github.elenterius.biomancy.util.shape.Shape;
import com.github.elenterius.biomancy.world.PrimordialEcosystem;
import com.github.elenterius.biomancy.world.ShapeManager;
import com.github.elenterius.biomancy.world.ShapeTicket;
import com.github.elenterius.biomancy.world.RegionManager;
import com.github.elenterius.biomancy.world.mound.MoundGenerator;
import com.github.elenterius.biomancy.world.mound.MoundShape;
import com.google.common.math.IntMath;
Expand Down Expand Up @@ -46,20 +46,21 @@

public class PrimordialCradleBlockEntity extends SimpleSyncedBlockEntity implements PrimalEnergyHandler, IAnimatable, ISyncableAnimation {

public static final int DURATION_TICKS = 20 * 4;
public static final String SACRIFICE_SYNC_KEY = "SyncSacrificeHandler";
public static final String SACRIFICE_KEY = "SacrificeHandler";
public static final String PRIMAL_ENERGY_KEY = "PrimalEnergy";
public static final String PROC_GEN_VALUES_KEY = "ProcGenValues";

public static final int DURATION_TICKS = 20 * 4;

protected static final AnimationBuilder IDLE_ANIM = new AnimationBuilder().addAnimation("cradle.idle");
protected static final AnimationBuilder SPIKE_ANIM = new AnimationBuilder().addAnimation("cradle.spike");
private final AnimationFactory animationFactory = GeckoLibUtil.createFactory(this);
private final SacrificeHandler sacrificeHandler = new SacrificeHandler();
private boolean playAttackAnimation = false;
private long ticks;
private int primalEnergy;

@Nullable
private ShapeTicket shapeTicket;
private @Nullable MoundShape.ProcGenValues procGenValues;

public PrimordialCradleBlockEntity(BlockPos pos, BlockState state) {
super(ModBlockEntities.PRIMORDIAL_CRADLE.get(), pos, state);
Expand All @@ -78,24 +79,25 @@ public static void serverTick(Level level, BlockPos pos, BlockState state, Primo
@Override
public void onLoad() {
super.onLoad();
if (level != null && !level.isClientSide) {
if (shapeTicket == null) {
long seed = Mth.getSeed(worldPosition); //TODO: make unique per cradle
MoundShape moundShape = MoundGenerator.constructShape(level, worldPosition, seed);
shapeTicket = ShapeManager.addShapeTicket(level, moundShape);
}
else {
shapeTicket.validate();
if (level instanceof ServerLevel serverLevel && !serverLevel.isClientSide) {
Shape shape = RegionManager.getOrCreateShapeRegion(serverLevel, worldPosition, () -> {
if (procGenValues != null) {
return MoundGenerator.constructShape(worldPosition, procGenValues);
}
return MoundGenerator.constructShape(level, worldPosition, level.random.nextLong());
});
if (shape instanceof MoundShape moundShape) {
procGenValues = moundShape.getProcGenValues();
}
}
}

@Override
public void setRemoved() {
super.setRemoved();
if (shapeTicket != null) {
shapeTicket.invalidate();
if (level instanceof ServerLevel serverLevel && !serverLevel.isClientSide) {
RegionManager.remove(serverLevel, worldPosition);
}
super.setRemoved();
}

/**
Expand Down Expand Up @@ -331,6 +333,12 @@ protected void saveAdditional(CompoundTag tag) {
super.saveAdditional(tag);
tag.put(SACRIFICE_KEY, sacrificeHandler.serializeNBT());
tag.putInt(PRIMAL_ENERGY_KEY, primalEnergy);

if (procGenValues != null) {
CompoundTag tagProcGen = new CompoundTag();
procGenValues.writeTo(tagProcGen);
tag.put(PROC_GEN_VALUES_KEY, tagProcGen);
}
}

@Override
Expand All @@ -349,6 +357,10 @@ else if (tag.contains(SACRIFICE_SYNC_KEY)) {
}

primalEnergy = tag.getInt(PRIMAL_ENERGY_KEY);

if (tag.contains(PROC_GEN_VALUES_KEY)) {
procGenValues = MoundShape.ProcGenValues.readFrom(tag.getCompound(PROC_GEN_VALUES_KEY));
}
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
import com.github.elenterius.biomancy.util.random.CellularNoise;
import com.github.elenterius.biomancy.util.shape.Shape;
import com.github.elenterius.biomancy.world.PrimordialEcosystem;
import com.github.elenterius.biomancy.world.ShapeManager;
import com.github.elenterius.biomancy.world.RegionManager;
import com.github.elenterius.biomancy.world.mound.MoundChamber;
import com.github.elenterius.biomancy.world.mound.MoundShape;
import net.minecraft.core.BlockPos;
Expand Down Expand Up @@ -132,19 +132,20 @@ else if (PrimordialEcosystem.FULL_FLESH_BLOCKS.contains(stateRelative.getBlock()
return destroyBlockAndConvertIntoEnergy(level, posRelative, energyHandler, 30); //TODO: this might interfere with future room content generation
}

BlockPos posBelow2 = pos.relative(axisDirection, 2);
BlockState stateBelow2 = level.getBlockState(posBelow2);
BlockPos posRelative2 = pos.relative(axisDirection, 2);
BlockState stateRelative2 = level.getBlockState(posRelative2);
boolean isFleshBlock = PrimordialEcosystem.FULL_FLESH_BLOCKS.contains(stateRelative2.getBlock());

if (axisDirection == Direction.UP && PrimordialEcosystem.FULL_FLESH_BLOCKS.contains(stateBelow2.getBlock()) && LevelUtil.getMaxBrightness(level, pos) < 5) {
if (isFleshBlock && axisDirection == Direction.UP && LevelUtil.getMaxBrightness(level, pos) < 5) {
return level.setBlock(posRelative, ModBlocks.BLOOMLIGHT.get().defaultBlockState(), Block.UPDATE_CLIENTS);
}

posRelative = posBelow2;
stateRelative = stateBelow2;
if (PrimordialEcosystem.isReplaceable(stateRelative)) {
if (PrimordialEcosystem.isReplaceable(stateRelative2) && stateRelative2.isCollisionShapeFullBlock(level, posRelative2)) {
BlockState replacementState = level.random.nextFloat() < nearBoundingCenterPct ? ModBlocks.PRIMAL_FLESH.get().defaultBlockState() : ModBlocks.MALIGNANT_FLESH.get().defaultBlockState();
return level.setBlock(posRelative, replacementState, Block.UPDATE_CLIENTS);
return level.setBlock(posRelative2, replacementState, Block.UPDATE_CLIENTS);
}

//TODO: place membranes as "doors"
}
}

Expand Down Expand Up @@ -492,7 +493,7 @@ public void tick(BlockState state, ServerLevel level, BlockPos pos, RandomSource
MoundChamber chamber = null;
float nearBoundingCenterPct = 0;

if (ShapeManager.getShape(level, pos) instanceof MoundShape moundShape) {
if (RegionManager.getClosestShape(level, pos) instanceof MoundShape moundShape) {
BlockPos origin = moundShape.getOrigin();
BlockEntity existingBlockEntity = level.getExistingBlockEntity(origin);
if (existingBlockEntity instanceof PrimalEnergyHandler peh) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.github.elenterius.biomancy.util;

import com.github.elenterius.biomancy.util.serialization.IntegerSerializable;

public class Bit32Set implements IntegerSerializable {
private int bits;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.github.elenterius.biomancy.util;
package com.github.elenterius.biomancy.util.serialization;

public interface IntegerSerializable {
int serializeToInteger();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.github.elenterius.biomancy.util.serialization;

public interface NBTSerializable<T> {
NBTSerializer<T> getNBTSerializer();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.github.elenterius.biomancy.util.serialization;

import net.minecraft.nbt.CompoundTag;

public interface NBTSerializer<T> {
String id();

CompoundTag serializeNBT(T t);

T deserializeNBT(CompoundTag tag);

@FunctionalInterface
interface Factory<T> {
NBTSerializer<T> create(String id);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package com.github.elenterius.biomancy.util.serialization;

import com.github.elenterius.biomancy.util.shape.OctantEllipsoidShape;
import com.github.elenterius.biomancy.util.shape.Shape;
import com.github.elenterius.biomancy.util.shape.SphereShape;
import com.github.elenterius.biomancy.world.Region;
import com.github.elenterius.biomancy.world.ShapeRegion;
import com.github.elenterius.biomancy.world.mound.MoundShape;
import org.jetbrains.annotations.Nullable;

import java.util.HashMap;
import java.util.Map;

public final class NBTSerializers {
private static final Map<String, NBTSerializer<?>> SERIALIZERS = new HashMap<>();
public static final NBTSerializer<Shape> SPHERE_SERIALIZER = registerShape("sphere", SphereShape.Serializer::new);
public static final NBTSerializer<Shape> OCTANT_ELLIPSOID_SERIALIZER = registerShape("octant_ellipsoid", OctantEllipsoidShape.Serializer::new);
public static final NBTSerializer<Shape> MOUND_SERIALIZER = registerShape("mound", MoundShape.Serializer::new);
public static final NBTSerializer<Region> SHAPE_REGION_SERIALIZER = registerRegion("shape_region", ShapeRegion.Serializer::new);

private NBTSerializers() {}

public static <T> NBTSerializer<T> register(String id, NBTSerializer.Factory<T> factory) {
NBTSerializer<T> serializer = factory.create(id);
SERIALIZERS.put(id, serializer);
return serializer;
}

public static <T extends Shape> NBTSerializer<Shape> registerShape(String id, NBTSerializer.Factory<T> factory) {
NBTSerializer<T> serializer = factory.create(id);
SERIALIZERS.put(id, serializer);
//noinspection unchecked
return (NBTSerializer<Shape>) serializer;
}

public static <T extends Region> NBTSerializer<Region> registerRegion(String id, NBTSerializer.Factory<T> factory) {
NBTSerializer<T> serializer = factory.create(id);
SERIALIZERS.put(id, serializer);
//noinspection unchecked
return (NBTSerializer<Region>) serializer;
}

public static @Nullable NBTSerializer<?> get(String id) {
return SERIALIZERS.get(id);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
@ParametersAreNonnullByDefault
@MethodsReturnNonnullByDefault
package com.github.elenterius.biomancy.util.serialization;

import net.minecraft.MethodsReturnNonnullByDefault;

import javax.annotation.ParametersAreNonnullByDefault;
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package com.github.elenterius.biomancy.util.shape;

import com.github.elenterius.biomancy.util.serialization.NBTSerializer;
import com.github.elenterius.biomancy.util.serialization.NBTSerializers;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;

Expand Down Expand Up @@ -70,4 +73,44 @@ public double getRadius() {
return radius;
}

@Override
public NBTSerializer<Shape> getNBTSerializer() {
return NBTSerializers.OCTANT_ELLIPSOID_SERIALIZER;
}

public record Serializer(String id) implements NBTSerializer<OctantEllipsoidShape> {

@Override
public CompoundTag serializeNBT(OctantEllipsoidShape shape) {
CompoundTag tag = new CompoundTag();

tag.putDouble("X", shape.origin.x);
tag.putDouble("Y", shape.origin.y);
tag.putDouble("Z", shape.origin.z);

tag.putFloat("A+", shape.aPos);
tag.putFloat("B+", shape.bPos);
tag.putFloat("C+", shape.cPos);
tag.putFloat("A-", shape.aNeg);
tag.putFloat("B-", shape.bNeg);
tag.putFloat("C-", shape.cNeg);
return tag;
}

@Override
public OctantEllipsoidShape deserializeNBT(CompoundTag tag) {
double x = tag.getDouble("X");
double y = tag.getDouble("Y");
double z = tag.getDouble("Z");

float aPos = tag.getFloat("A+");
float bPos = tag.getFloat("B+");
float cPos = tag.getFloat("C+");
float aNeg = tag.getFloat("A-");
float bNeg = tag.getFloat("B-");
float cNeg = tag.getFloat("C-");

return new OctantEllipsoidShape(x, y, z, aPos, bPos, cPos, aNeg, bNeg, cNeg);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package com.github.elenterius.biomancy.util.shape;

import com.github.elenterius.biomancy.util.serialization.NBTSerializable;
import com.github.elenterius.biomancy.util.serialization.NBTSerializer;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;

public interface Shape {
public interface Shape extends NBTSerializable<Shape> {

boolean contains(double x, double y, double z);

Expand All @@ -16,4 +19,45 @@ public interface Shape {
interface Sphere extends Shape {
double getRadius();
}

Shape EMPTY = new Shape() {
static final Serializer SERIALIZER = new Serializer("empty");

@Override
public boolean contains(double x, double y, double z) {
return false;
}

@Override
public Vec3 getCenter() {
return Vec3.ZERO;
}

@Override
public double distanceToSqr(double x, double y, double z) {
return Double.MAX_VALUE;
}

@Override
public AABB getAABB() {
return AABB.unitCubeFromLowerCorner(Vec3.ZERO);
}

@Override
public NBTSerializer<Shape> getNBTSerializer() {
return SERIALIZER;
}

public record Serializer(String id) implements NBTSerializer<Shape> {
@Override
public CompoundTag serializeNBT(Shape shape) {
return new CompoundTag();
}

@Override
public Shape deserializeNBT(CompoundTag tag) {
return Shape.EMPTY;
}
}
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,16 @@
import java.util.HashSet;
import java.util.Set;

/**
* spatial hash map based on a 16x16x16 grid
*
* @param <T>
*/
public class ShapeMap<T extends Shape> {
public class ShapeHierarchy<T extends Shape> {

/**
* spatial hash map based on a 16x16x16 cell grid
*/
protected final Long2ObjectMap<Set<T>> sections = new Long2ObjectOpenHashMap<>();

protected final AABB aabb;

public ShapeMap(Iterable<T> shapes) {
public ShapeHierarchy(Iterable<T> shapes) {
double aabbMinX = Double.MAX_VALUE;
double aabbMinY = Double.MAX_VALUE;
double aabbMinZ = Double.MAX_VALUE;
Expand Down
Loading

0 comments on commit 32c2efd

Please sign in to comment.