added 1 billion bugs
No files matched your search
@@ -90,7 +90,7 @@ public final class AntarchySettings {
|
||||
private static ResourceKey<Level> rainbowAntNonInfinityFallbackDimension = dimensionKey("antarchy:elythia");
|
||||
private static double krakenHealth = 1500.0D;
|
||||
private static double krakenAttackDamage = 45.0D;
|
||||
private static double octopusBombHealth = 250.0D;
|
||||
private static double octopusBombHealth = 100.0D;
|
||||
private static double octopusBombAttackDamage = 16.0D;
|
||||
private static double brutalflyHealth = 400.0D;
|
||||
private static double brutalflySwipeDamage = 15.0D;
|
||||
|
||||
@@ -148,6 +148,10 @@ public final class AntarchyObjects {
|
||||
@SuppressWarnings("unchecked")
|
||||
public static Supplier<Block> NYXITE_SPIKE = (Supplier<Block>) UNBOUND;
|
||||
@SuppressWarnings("unchecked")
|
||||
public static Supplier<Block> CHITEN_BLOCK = (Supplier<Block>) UNBOUND;
|
||||
@SuppressWarnings("unchecked")
|
||||
public static Supplier<Block> CHITEN_SPIKE = (Supplier<Block>) UNBOUND;
|
||||
@SuppressWarnings("unchecked")
|
||||
public static Supplier<Block> POTENT_NYXITE = (Supplier<Block>) UNBOUND;
|
||||
@SuppressWarnings("unchecked")
|
||||
public static Supplier<Block> ANTIMETAL = (Supplier<Block>) UNBOUND;
|
||||
@@ -276,6 +280,8 @@ public final class AntarchyObjects {
|
||||
Supplier<Block> triffidGooBlock,
|
||||
Supplier<Block> paleNyxite,
|
||||
Supplier<Block> nyxiteSpike,
|
||||
Supplier<Block> chitenBlock,
|
||||
Supplier<Block> chitenSpike,
|
||||
Supplier<Block> potentNyxite,
|
||||
Supplier<Block> antimetal,
|
||||
Supplier<Block> polishedAntimetal,
|
||||
@@ -364,6 +370,8 @@ public final class AntarchyObjects {
|
||||
TRIFFID_GOO_BLOCK = triffidGooBlock;
|
||||
PALE_NYXITE = paleNyxite;
|
||||
NYXITE_SPIKE = nyxiteSpike;
|
||||
CHITEN_BLOCK = chitenBlock;
|
||||
CHITEN_SPIKE = chitenSpike;
|
||||
POTENT_NYXITE = potentNyxite;
|
||||
ANTIMETAL = antimetal;
|
||||
POLISHED_ANTIMETAL = polishedAntimetal;
|
||||
|
||||
@@ -76,6 +76,7 @@ public final class AntarchyTags {
|
||||
public static final TagKey<Item> DUCT_TAPE_BLACKLIST = TagKey.create(Registries.ITEM, id("duct_tape_blacklist"));
|
||||
public static final TagKey<Item> BROWN_ANT_ACTIVATION_ITEMS = TagKey.create(Registries.ITEM, id("brown_ant_activation_items"));
|
||||
public static final TagKey<Item> BROWN_ANT_BREEDING_FOODS = TagKey.create(Registries.ITEM, id("brown_ant_breeding_foods"));
|
||||
public static final TagKey<Item> MULTISHOT_ENCHANTABLE = TagKey.create(Registries.ITEM, id("multishot_enchantable"));
|
||||
public static final TagKey<Item> RED_ANT_ACTIVATION_ITEMS = TagKey.create(Registries.ITEM, id("red_ant_activation_items"));
|
||||
public static final TagKey<Item> RED_ANT_BREEDING_FOODS = TagKey.create(Registries.ITEM, id("red_ant_breeding_foods"));
|
||||
public static final TagKey<Item> RAINBOW_ANT_ACTIVATION_ITEMS = TagKey.create(Registries.ITEM, id("rainbow_ant_activation_items"));
|
||||
@@ -95,6 +96,7 @@ public final class AntarchyTags {
|
||||
public static final class Biomes {
|
||||
public static final TagKey<Biome> MANTIS_SPAWN_BIOMES = TagKey.create(Registries.BIOME, id("mantis_spawn_biomes"));
|
||||
public static final TagKey<Biome> MANTIS_OVERWORLD_SPAWN_BIOMES = TagKey.create(Registries.BIOME, id("mantis_overworld_spawn_biomes"));
|
||||
public static final TagKey<Biome> ELYTHIA_FIREFLY_PARTICLE_BIOMES = TagKey.create(Registries.BIOME, id("elythia_firefly_particle_biomes"));
|
||||
|
||||
private Biomes() {
|
||||
}
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
package com.craisinlord.antarchy.content.block;
|
||||
|
||||
import com.craisinlord.antarchy.Antarchy;
|
||||
import com.mojang.serialization.MapCodec;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.world.level.Level;
|
||||
@@ -9,9 +15,14 @@ import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.BonemealableBlock;
|
||||
import net.minecraft.world.level.block.state.BlockBehaviour;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.levelgen.feature.ConfiguredFeature;
|
||||
|
||||
public class AmberMossBlock extends Block implements BonemealableBlock {
|
||||
public static final MapCodec<AmberMossBlock> CODEC = Block.simpleCodec(AmberMossBlock::new);
|
||||
private static final ResourceKey<ConfiguredFeature<?, ?>> AMBER_MOSS_PATCH_BONEMEAL = ResourceKey.create(
|
||||
Registries.CONFIGURED_FEATURE,
|
||||
ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "amber_moss_patch_bonemeal")
|
||||
);
|
||||
|
||||
public AmberMossBlock(BlockBehaviour.Properties properties) {
|
||||
super(properties);
|
||||
@@ -24,15 +35,19 @@ public class AmberMossBlock extends Block implements BonemealableBlock {
|
||||
|
||||
@Override
|
||||
public boolean isValidBonemealTarget(LevelReader level, BlockPos pos, BlockState state) {
|
||||
return false;
|
||||
return level.getBlockState(pos.above()).isAir();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBonemealSuccess(Level level, RandomSource random, BlockPos pos, BlockState state) {
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void performBonemeal(net.minecraft.server.level.ServerLevel level, RandomSource random, BlockPos pos, BlockState state) {
|
||||
public void performBonemeal(ServerLevel level, RandomSource random, BlockPos pos, BlockState state) {
|
||||
Holder.Reference<ConfiguredFeature<?, ?>> feature = level.registryAccess()
|
||||
.lookupOrThrow(Registries.CONFIGURED_FEATURE)
|
||||
.getOrThrow(AMBER_MOSS_PATCH_BONEMEAL);
|
||||
feature.value().place(level, level.getChunkSource().getGenerator(), random, pos);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
package com.craisinlord.antarchy.content.block;
|
||||
|
||||
import com.craisinlord.antarchy.content.AntarchyObjects;
|
||||
import com.mojang.serialization.MapCodec;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.world.item.context.BlockPlaceContext;
|
||||
import net.minecraft.world.level.LevelAccessor;
|
||||
import net.minecraft.world.level.LevelReader;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.PointedDripstoneBlock;
|
||||
import net.minecraft.world.level.block.state.BlockBehaviour;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.block.state.properties.DripstoneThickness;
|
||||
import net.minecraft.world.level.material.Fluids;
|
||||
|
||||
public class ChitenSpikeBlock extends PointedDripstoneBlock {
|
||||
@SuppressWarnings("rawtypes")
|
||||
public static final MapCodec<PointedDripstoneBlock> CODEC = (MapCodec) Block.simpleCodec(ChitenSpikeBlock::new);
|
||||
|
||||
public ChitenSpikeBlock(BlockBehaviour.Properties properties) {
|
||||
super(properties);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MapCodec<PointedDripstoneBlock> codec() {
|
||||
return CODEC;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockState getStateForPlacement(BlockPlaceContext context) {
|
||||
LevelAccessor level = context.getLevel();
|
||||
BlockPos pos = context.getClickedPos();
|
||||
Direction direction = calculateTipDirection(level, pos, context.getNearestLookingVerticalDirection().getOpposite());
|
||||
if (direction == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
boolean mergeTips = !context.isSecondaryUseActive();
|
||||
DripstoneThickness thickness = calculateDripstoneThickness(level, pos, direction, mergeTips);
|
||||
if (thickness == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return this.defaultBlockState()
|
||||
.setValue(TIP_DIRECTION, direction)
|
||||
.setValue(THICKNESS, thickness)
|
||||
.setValue(WATERLOGGED, level.getFluidState(pos).getType() == Fluids.WATER);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BlockState updateShape(
|
||||
BlockState state,
|
||||
Direction direction,
|
||||
BlockState neighborState,
|
||||
LevelAccessor level,
|
||||
BlockPos pos,
|
||||
BlockPos neighborPos
|
||||
) {
|
||||
if (state.getValue(WATERLOGGED)) {
|
||||
level.scheduleTick(pos, Fluids.WATER, Fluids.WATER.getTickDelay(level));
|
||||
}
|
||||
|
||||
if (direction != Direction.UP && direction != Direction.DOWN) {
|
||||
return state;
|
||||
}
|
||||
|
||||
Direction tipDirection = state.getValue(TIP_DIRECTION);
|
||||
if (tipDirection == Direction.DOWN && level.getBlockTicks().hasScheduledTick(pos, this)) {
|
||||
return state;
|
||||
}
|
||||
|
||||
if (direction == tipDirection.getOpposite() && !this.canSurvive(state, level, pos)) {
|
||||
if (tipDirection == Direction.DOWN) {
|
||||
level.scheduleTick(pos, this, 2);
|
||||
} else {
|
||||
level.scheduleTick(pos, this, 1);
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
boolean mergeTips = state.getValue(THICKNESS) == DripstoneThickness.TIP_MERGE;
|
||||
return state.setValue(THICKNESS, calculateDripstoneThickness(level, pos, tipDirection, mergeTips));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canSurvive(BlockState state, LevelReader level, BlockPos pos) {
|
||||
return isValidPointedDripstonePlacement(level, pos, state.getValue(TIP_DIRECTION));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void tick(BlockState state, ServerLevel level, BlockPos pos, RandomSource random) {
|
||||
if (this.canSurvive(state, level, pos)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (state.getValue(TIP_DIRECTION) == Direction.DOWN) {
|
||||
BlockPos.MutableBlockPos root = pos.mutable();
|
||||
while (isChitenSpikeWithDirection(level.getBlockState(root.above()), Direction.DOWN)) {
|
||||
root.move(Direction.UP);
|
||||
}
|
||||
|
||||
BlockPos.MutableBlockPos cursor = root.mutable();
|
||||
while (isChitenSpikeWithDirection(level.getBlockState(cursor), Direction.DOWN)) {
|
||||
BlockState fallingState = level.getBlockState(cursor);
|
||||
if (isChitenSpikeWithDirection(fallingState, Direction.DOWN)) {
|
||||
net.minecraft.world.entity.item.FallingBlockEntity.fall(level, cursor, fallingState);
|
||||
}
|
||||
cursor.move(Direction.DOWN);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
BlockPos.MutableBlockPos cursor = pos.mutable();
|
||||
while (isChitenSpikeWithDirection(level.getBlockState(cursor), Direction.UP)) {
|
||||
BlockState spikeState = level.getBlockState(cursor);
|
||||
dropResources(spikeState, level, cursor);
|
||||
level.removeBlock(cursor, false);
|
||||
cursor.move(Direction.UP);
|
||||
}
|
||||
}
|
||||
|
||||
private static Direction calculateTipDirection(LevelReader level, BlockPos pos, Direction preferredDirection) {
|
||||
if (isValidPointedDripstonePlacement(level, pos, preferredDirection)) {
|
||||
return preferredDirection;
|
||||
}
|
||||
|
||||
Direction opposite = preferredDirection.getOpposite();
|
||||
return isValidPointedDripstonePlacement(level, pos, opposite) ? opposite : null;
|
||||
}
|
||||
|
||||
private static DripstoneThickness calculateDripstoneThickness(LevelReader level, BlockPos pos, Direction direction, boolean mergeTips) {
|
||||
Direction opposite = direction.getOpposite();
|
||||
BlockState forwardState = level.getBlockState(pos.relative(direction));
|
||||
if (isChitenSpikeWithDirection(forwardState, opposite)) {
|
||||
return !mergeTips && forwardState.getValue(THICKNESS) != DripstoneThickness.TIP_MERGE
|
||||
? DripstoneThickness.TIP
|
||||
: DripstoneThickness.TIP_MERGE;
|
||||
}
|
||||
|
||||
if (!isChitenSpikeWithDirection(forwardState, direction)) {
|
||||
return DripstoneThickness.TIP;
|
||||
}
|
||||
|
||||
DripstoneThickness forwardThickness = forwardState.getValue(THICKNESS);
|
||||
if (forwardThickness != DripstoneThickness.TIP && forwardThickness != DripstoneThickness.TIP_MERGE) {
|
||||
BlockState backState = level.getBlockState(pos.relative(opposite));
|
||||
return !isChitenSpikeWithDirection(backState, direction) ? DripstoneThickness.BASE : DripstoneThickness.MIDDLE;
|
||||
}
|
||||
|
||||
return DripstoneThickness.FRUSTUM;
|
||||
}
|
||||
|
||||
private static boolean isValidPointedDripstonePlacement(LevelReader level, BlockPos pos, Direction direction) {
|
||||
BlockPos supportPos = pos.relative(direction.getOpposite());
|
||||
BlockState supportState = level.getBlockState(supportPos);
|
||||
return supportState.isFaceSturdy(level, supportPos, direction) || isChitenSpikeWithDirection(supportState, direction);
|
||||
}
|
||||
|
||||
private static boolean isChitenSpikeWithDirection(BlockState state, Direction direction) {
|
||||
return state.is(AntarchyObjects.CHITEN_SPIKE.get()) && state.getValue(TIP_DIRECTION) == direction;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package com.craisinlord.antarchy.content.client.particle;
|
||||
|
||||
import net.minecraft.client.multiplayer.ClientLevel;
|
||||
import net.minecraft.client.particle.Particle;
|
||||
import net.minecraft.client.particle.ParticleProvider;
|
||||
import net.minecraft.client.particle.ParticleRenderType;
|
||||
import net.minecraft.client.particle.SpriteSet;
|
||||
import net.minecraft.client.particle.TextureSheetParticle;
|
||||
import net.minecraft.core.particles.SimpleParticleType;
|
||||
|
||||
public final class OrangeAshParticle extends TextureSheetParticle {
|
||||
private final SpriteSet sprites;
|
||||
|
||||
private OrangeAshParticle(ClientLevel level, double x, double y, double z, double xd, double yd, double zd, SpriteSet sprites) {
|
||||
super(level, x, y, z, xd, yd, zd);
|
||||
this.sprites = sprites;
|
||||
this.friction = 0.98F;
|
||||
this.gravity = 0.0025F;
|
||||
this.speedUpWhenYMotionIsBlocked = false;
|
||||
this.hasPhysics = false;
|
||||
this.xd = xd * 0.08D + (this.random.nextDouble() - 0.5D) * 0.01D;
|
||||
this.yd = yd * 0.02D - 0.01D - this.random.nextDouble() * 0.01D;
|
||||
this.zd = zd * 0.08D + (this.random.nextDouble() - 0.5D) * 0.01D;
|
||||
this.quadSize *= 0.75F + this.random.nextFloat() * 0.5F;
|
||||
this.lifetime = 40 + this.random.nextInt(28);
|
||||
this.setColor(0.96F, 0.46F, 0.12F);
|
||||
this.setAlpha(0.72F);
|
||||
this.pickSprite(sprites);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
super.tick();
|
||||
this.pickSprite(this.sprites);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ParticleRenderType getRenderType() {
|
||||
return ParticleRenderType.PARTICLE_SHEET_OPAQUE;
|
||||
}
|
||||
|
||||
public static final class Provider implements ParticleProvider<SimpleParticleType> {
|
||||
private final SpriteSet sprites;
|
||||
|
||||
public Provider(SpriteSet sprites) {
|
||||
this.sprites = sprites;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Particle createParticle(SimpleParticleType type, ClientLevel level, double x, double y, double z, double xd, double yd, double zd) {
|
||||
return new OrangeAshParticle(level, x, y, z, xd, yd, zd, this.sprites);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
package com.craisinlord.antarchy.content.client.renderer;
|
||||
|
||||
import com.craisinlord.antarchy.Antarchy;
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
import com.mojang.blaze3d.vertex.BufferBuilder;
|
||||
import com.mojang.blaze3d.vertex.BufferUploader;
|
||||
import com.mojang.blaze3d.vertex.DefaultVertexFormat;
|
||||
import com.mojang.blaze3d.vertex.MeshData;
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import com.mojang.blaze3d.vertex.Tesselator;
|
||||
import com.mojang.blaze3d.vertex.VertexBuffer;
|
||||
import com.mojang.blaze3d.vertex.VertexFormat;
|
||||
import java.util.Random;
|
||||
import net.minecraft.client.Camera;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.multiplayer.ClientLevel;
|
||||
import net.minecraft.client.renderer.GameRenderer;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.level.Level;
|
||||
|
||||
public final class CavarynSkyRenderer {
|
||||
public static final ResourceKey<Level> CAVARYN = ResourceKey.create(
|
||||
Registries.DIMENSION,
|
||||
ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "cavaryn")
|
||||
);
|
||||
|
||||
@org.jetbrains.annotations.Nullable
|
||||
private static VertexBuffer orangeStarBuffer;
|
||||
|
||||
private CavarynSkyRenderer() {
|
||||
}
|
||||
|
||||
public static boolean shouldRender(ClientLevel level) {
|
||||
return level.dimension().equals(CAVARYN);
|
||||
}
|
||||
|
||||
public static void render(
|
||||
Minecraft minecraft,
|
||||
ClientLevel level,
|
||||
PoseStack poseStack,
|
||||
org.joml.Matrix4f projectionMatrix,
|
||||
float partialTick,
|
||||
Camera camera,
|
||||
boolean isFoggy,
|
||||
Runnable setupFog,
|
||||
VertexBuffer skyBuffer,
|
||||
VertexBuffer darkBuffer
|
||||
) {
|
||||
setupFog.run();
|
||||
if (isFoggy || minecraft.gameRenderer.getMainCamera() != camera) {
|
||||
return;
|
||||
}
|
||||
|
||||
float red = 0.015F;
|
||||
float green = 0.085F;
|
||||
float blue = 0.035F;
|
||||
|
||||
RenderSystem.depthMask(false);
|
||||
RenderSystem.setShaderColor(red, green, blue, 1.0F);
|
||||
skyBuffer.bind();
|
||||
skyBuffer.drawWithShader(poseStack.last().pose(), projectionMatrix, GameRenderer.getPositionShader());
|
||||
VertexBuffer.unbind();
|
||||
RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F);
|
||||
|
||||
float starBrightness = 0.8F;
|
||||
if (orangeStarBuffer == null) {
|
||||
orangeStarBuffer = buildOrangeStarBuffer();
|
||||
}
|
||||
|
||||
RenderSystem.enableBlend();
|
||||
RenderSystem.defaultBlendFunc();
|
||||
RenderSystem.setShaderColor(starBrightness, starBrightness, starBrightness, starBrightness);
|
||||
orangeStarBuffer.bind();
|
||||
orangeStarBuffer.drawWithShader(poseStack.last().pose(), projectionMatrix, GameRenderer.getPositionColorShader());
|
||||
VertexBuffer.unbind();
|
||||
RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F);
|
||||
RenderSystem.disableBlend();
|
||||
|
||||
if (camera.getEntity() != null) {
|
||||
double horizon = camera.getEntity().getEyePosition(partialTick).y - level.getLevelData().getHorizonHeight(level);
|
||||
if (horizon < 0.0D) {
|
||||
poseStack.pushPose();
|
||||
poseStack.translate(0.0F, 12.0F, 0.0F);
|
||||
darkBuffer.bind();
|
||||
darkBuffer.drawWithShader(poseStack.last().pose(), projectionMatrix, GameRenderer.getPositionShader());
|
||||
VertexBuffer.unbind();
|
||||
poseStack.popPose();
|
||||
}
|
||||
}
|
||||
|
||||
RenderSystem.depthMask(true);
|
||||
}
|
||||
|
||||
private static VertexBuffer buildOrangeStarBuffer() {
|
||||
Random random = new Random(548721L);
|
||||
BufferBuilder builder = Tesselator.getInstance().begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_COLOR);
|
||||
|
||||
for (int i = 0; i < 1800; i++) {
|
||||
double x = random.nextFloat() * 2.0F - 1.0F;
|
||||
double y = random.nextFloat() * 2.0F - 1.0F;
|
||||
double z = random.nextFloat() * 2.0F - 1.0F;
|
||||
double size = 0.12F + random.nextFloat() * 0.12F;
|
||||
double lenSq = x * x + y * y + z * z;
|
||||
if (lenSq < 1.0D && lenSq > 0.01D) {
|
||||
double len = 1.0D / Math.sqrt(lenSq);
|
||||
x *= len;
|
||||
y *= len;
|
||||
z *= len;
|
||||
double sx = x * 100.0D;
|
||||
double sy = y * 100.0D;
|
||||
double sz = z * 100.0D;
|
||||
double yaw = Math.atan2(x, z);
|
||||
double sinYaw = Math.sin(yaw);
|
||||
double cosYaw = Math.cos(yaw);
|
||||
double pitch = Math.atan2(Math.sqrt(x * x + z * z), y);
|
||||
double sinPitch = Math.sin(pitch);
|
||||
double cosPitch = Math.cos(pitch);
|
||||
double rot = random.nextDouble() * Math.PI * 2.0D;
|
||||
double sinRot = Math.sin(rot);
|
||||
double cosRot = Math.cos(rot);
|
||||
|
||||
float[] color = pickStarColor(random);
|
||||
|
||||
for (int j = 0; j < 4; j++) {
|
||||
double u = (j & 2) - 1;
|
||||
double v = (j + 1 & 2) - 1;
|
||||
double px = u * cosRot - v * sinRot;
|
||||
double py = v * cosRot + u * sinRot;
|
||||
double qx = px * sinPitch;
|
||||
double qy = -px * cosPitch;
|
||||
double rx = qy * sinYaw - py * cosYaw;
|
||||
double ry = py * sinYaw + qy * cosYaw;
|
||||
builder.addVertex((float) (sx + rx * size), (float) (sy + qx * size), (float) (sz + ry * size))
|
||||
.setColor(color[0], color[1], color[2], 1.0F);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MeshData mesh = builder.buildOrThrow();
|
||||
VertexBuffer buffer = new VertexBuffer(VertexBuffer.Usage.STATIC);
|
||||
buffer.bind();
|
||||
buffer.upload(mesh);
|
||||
VertexBuffer.unbind();
|
||||
return buffer;
|
||||
}
|
||||
|
||||
private static float[] pickStarColor(Random random) {
|
||||
float roll = random.nextFloat();
|
||||
if (roll < 0.70F) return new float[]{1.00F, 0.55F, 0.14F};
|
||||
if (roll < 0.90F) return new float[]{1.00F, 0.70F, 0.24F};
|
||||
return new float[]{1.00F, 0.84F, 0.40F};
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,23 @@
|
||||
package com.craisinlord.antarchy.content.effect;
|
||||
|
||||
import com.craisinlord.antarchy.content.StinkyBehavior;
|
||||
import net.minecraft.world.effect.MobEffect;
|
||||
import net.minecraft.world.effect.MobEffectCategory;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
|
||||
public final class StinkyMobEffect extends MobEffect {
|
||||
public StinkyMobEffect() {
|
||||
super(MobEffectCategory.HARMFUL, 0x66773A);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean applyEffectTick(LivingEntity entity, int amplifier) {
|
||||
StinkyBehavior.tickStinkyTrail(entity);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldApplyEffectTickThisTick(int duration, int amplifier) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,16 @@
|
||||
package com.craisinlord.antarchy.content.entity;
|
||||
|
||||
import com.craisinlord.antarchy.config.AntarchySettings;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.world.Difficulty;
|
||||
import net.minecraft.network.syncher.EntityDataAccessor;
|
||||
import net.minecraft.network.syncher.EntityDataSerializers;
|
||||
import net.minecraft.network.syncher.SynchedEntityData;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.world.entity.Mob;
|
||||
import net.minecraft.world.entity.MobSpawnType;
|
||||
import net.minecraft.world.entity.ai.attributes.AttributeSupplier;
|
||||
import net.minecraft.world.entity.ai.attributes.Attributes;
|
||||
import net.minecraft.world.entity.ai.goal.FloatGoal;
|
||||
@@ -18,6 +23,9 @@ import net.minecraft.world.entity.ai.goal.target.NearestAttackableTargetGoal;
|
||||
import net.minecraft.world.entity.monster.Monster;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.ServerLevelAccessor;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import software.bernie.geckolib.animatable.GeoEntity;
|
||||
import software.bernie.geckolib.animatable.instance.AnimatableInstanceCache;
|
||||
import software.bernie.geckolib.animation.AnimatableManager;
|
||||
@@ -62,6 +70,25 @@ public class CreepingHorrorEntity extends Monster implements GeoEntity {
|
||||
.add(Attributes.FOLLOW_RANGE, 16.0D);
|
||||
}
|
||||
|
||||
public static boolean canSpawn(EntityType<CreepingHorrorEntity> entityType, ServerLevelAccessor level, MobSpawnType spawnReason, BlockPos pos, RandomSource random) {
|
||||
if (spawnReason == MobSpawnType.SPAWN_EGG || spawnReason == MobSpawnType.SPAWNER || spawnReason == MobSpawnType.COMMAND) {
|
||||
return true;
|
||||
}
|
||||
|
||||
BlockPos belowPos = pos.below();
|
||||
BlockState belowState = level.getBlockState(belowPos);
|
||||
boolean validSupport = !belowState.is(Blocks.BEDROCK)
|
||||
&& belowState.blocksMotion()
|
||||
&& belowState.isFaceSturdy(level, belowPos, Direction.UP)
|
||||
&& belowState.isCollisionShapeFullBlock(level, belowPos);
|
||||
|
||||
return level.getDifficulty() != Difficulty.PEACEFUL
|
||||
&& validSupport
|
||||
&& level.isEmptyBlock(pos)
|
||||
&& level.isEmptyBlock(pos.above())
|
||||
&& Monster.checkMonsterSpawnRules(entityType, level, spawnReason, pos, random);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void registerGoals() {
|
||||
this.goalSelector.addGoal(0, new FloatGoal(this));
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
package com.craisinlord.antarchy.content.entity;
|
||||
|
||||
import com.craisinlord.antarchy.config.AntarchySettings;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.world.Difficulty;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.world.entity.Mob;
|
||||
import net.minecraft.world.entity.MobSpawnType;
|
||||
import net.minecraft.world.entity.ai.attributes.AttributeSupplier;
|
||||
import net.minecraft.world.entity.ai.attributes.Attributes;
|
||||
import net.minecraft.world.entity.ai.control.FlyingMoveControl;
|
||||
@@ -18,6 +23,9 @@ import net.minecraft.world.entity.ai.navigation.PathNavigation;
|
||||
import net.minecraft.world.entity.monster.Monster;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.ServerLevelAccessor;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import software.bernie.geckolib.animatable.GeoEntity;
|
||||
import software.bernie.geckolib.animatable.instance.AnimatableInstanceCache;
|
||||
import software.bernie.geckolib.animation.AnimatableManager;
|
||||
@@ -52,6 +60,25 @@ public class LurkingTerrorEntity extends Monster implements GeoEntity {
|
||||
.add(Attributes.FLYING_SPEED, 0.55D);
|
||||
}
|
||||
|
||||
public static boolean canSpawn(EntityType<LurkingTerrorEntity> entityType, ServerLevelAccessor level, MobSpawnType spawnReason, BlockPos pos, RandomSource random) {
|
||||
if (spawnReason == MobSpawnType.SPAWN_EGG || spawnReason == MobSpawnType.SPAWNER || spawnReason == MobSpawnType.COMMAND) {
|
||||
return true;
|
||||
}
|
||||
|
||||
BlockPos belowPos = pos.below();
|
||||
BlockState belowState = level.getBlockState(belowPos);
|
||||
boolean validSupport = !belowState.is(Blocks.BEDROCK)
|
||||
&& belowState.blocksMotion()
|
||||
&& belowState.isFaceSturdy(level, belowPos, Direction.UP)
|
||||
&& belowState.isCollisionShapeFullBlock(level, belowPos);
|
||||
|
||||
return level.getDifficulty() != Difficulty.PEACEFUL
|
||||
&& validSupport
|
||||
&& level.isEmptyBlock(pos)
|
||||
&& level.isEmptyBlock(pos.above())
|
||||
&& Monster.checkMonsterSpawnRules(entityType, level, spawnReason, pos, random);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PathNavigation createNavigation(Level level) {
|
||||
FlyingPathNavigation nav = new FlyingPathNavigation(this, level);
|
||||
|
||||
@@ -2,8 +2,7 @@ package com.craisinlord.antarchy.content.entity;
|
||||
|
||||
import com.craisinlord.antarchy.content.AntarchySoundEvents;
|
||||
import com.craisinlord.antarchy.content.damage.AntarchyDamageSources;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.tags.FluidTags;
|
||||
import net.minecraft.core.particles.ParticleTypes;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.network.syncher.EntityDataAccessor;
|
||||
import net.minecraft.network.syncher.EntityDataSerializers;
|
||||
@@ -11,7 +10,6 @@ import net.minecraft.network.syncher.SynchedEntityData;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.sounds.SoundEvent;
|
||||
import net.minecraft.core.particles.ParticleTypes;
|
||||
import net.minecraft.util.Mth;
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.world.DifficultyInstance;
|
||||
@@ -24,6 +22,7 @@ import net.minecraft.world.entity.ai.control.SmoothSwimmingMoveControl;
|
||||
import net.minecraft.world.entity.ai.goal.LookAtPlayerGoal;
|
||||
import net.minecraft.world.entity.ai.goal.RandomLookAroundGoal;
|
||||
import net.minecraft.world.entity.ai.goal.target.HurtByTargetGoal;
|
||||
import net.minecraft.world.entity.ai.goal.target.NearestAttackableTargetGoal;
|
||||
import net.minecraft.world.entity.ai.navigation.FlyingPathNavigation;
|
||||
import net.minecraft.world.entity.ai.navigation.PathNavigation;
|
||||
import net.minecraft.world.entity.ai.navigation.WaterBoundPathNavigation;
|
||||
@@ -46,28 +45,21 @@ import java.util.Objects;
|
||||
|
||||
public class OctopusBombEntity extends Monster implements GeoEntity {
|
||||
private static final EntityDataAccessor<Integer> ATTACK_STATE = SynchedEntityData.defineId(OctopusBombEntity.class, EntityDataSerializers.INT);
|
||||
private static final EntityDataAccessor<Boolean> ROARING = SynchedEntityData.defineId(OctopusBombEntity.class, EntityDataSerializers.BOOLEAN);
|
||||
private static final EntityDataAccessor<Integer> ANIMATION_STATE = SynchedEntityData.defineId(OctopusBombEntity.class, EntityDataSerializers.INT);
|
||||
private static final EntityDataAccessor<Boolean> CAN_FLY = SynchedEntityData.defineId(OctopusBombEntity.class, EntityDataSerializers.BOOLEAN);
|
||||
|
||||
private static final String ATTACK_COOLDOWN_KEY = "AttackCooldown";
|
||||
private static final String ACTION_TICKS_KEY = "ActionTicks";
|
||||
private static final String LAST_ATTACK_STATE_KEY = "LastAttackState";
|
||||
private static final String AGGRO_TRIGGERED_KEY = "AggroTriggered";
|
||||
private static final String GRAB_TARGET_KEY = "GrabTarget";
|
||||
private static final String CAN_FLY_KEY = "CanFly";
|
||||
private static final String SPAWNED_BY_KRAKEN_KEY = "SpawnedByKraken";
|
||||
|
||||
private static final int ATTACK_NONE = 0;
|
||||
private static final int ATTACK_GRAB = 1;
|
||||
private static final int ATTACK_SWIPE = 2;
|
||||
|
||||
private static final int ANIM_IDLE = 0;
|
||||
private static final int ANIM_SWIM = 2;
|
||||
private static final int ANIM_SWIPE = 3;
|
||||
private static final int ANIM_GRAB = 4;
|
||||
private static final int ANIM_DEATH = 5;
|
||||
|
||||
private static final int DEATH_ANIM_TICKS = 60;
|
||||
private static final int INK_SPRAY_RANGE = 6;
|
||||
private static final int INK_SPRAY_RANGE = 8;
|
||||
private static final int INK_SPRAY_COOLDOWN = 80;
|
||||
|
||||
private static final RawAnimation IDLE_ANIM = RawAnimation.begin().thenLoop("idle");
|
||||
@@ -77,16 +69,21 @@ public class OctopusBombEntity extends Monster implements GeoEntity {
|
||||
private static final RawAnimation DEATH_ANIM = RawAnimation.begin().thenPlay("Death");
|
||||
|
||||
private final AnimatableInstanceCache geoCache = GeckoLibUtil.createInstanceCache(this);
|
||||
|
||||
// Squid-style wander
|
||||
private float wanderDx, wanderDy, wanderDz;
|
||||
private int wanderRetargetTicks;
|
||||
|
||||
// Combat
|
||||
private int attackCooldown;
|
||||
private int actionTicks;
|
||||
private int lastAttackState;
|
||||
private boolean aggroTriggered;
|
||||
private int grabbedTargetId = -1;
|
||||
private int strafeRetargetTicks;
|
||||
private int inkSprayCooldown;
|
||||
private float orbitDirection = 1.0F;
|
||||
private int inkSprayCooldown;
|
||||
|
||||
// Kraken spawn flag
|
||||
private boolean spawnedByKraken;
|
||||
|
||||
public OctopusBombEntity(EntityType<? extends Monster> entityType, Level level) {
|
||||
@@ -97,7 +94,7 @@ public class OctopusBombEntity extends Monster implements GeoEntity {
|
||||
|
||||
public static AttributeSupplier.Builder createAttributes() {
|
||||
return Mob.createMobAttributes()
|
||||
.add(Attributes.MAX_HEALTH, 250.0D)
|
||||
.add(Attributes.MAX_HEALTH, 100.0D)
|
||||
.add(Attributes.ATTACK_DAMAGE, 16.0D)
|
||||
.add(Attributes.MOVEMENT_SPEED, 0.28D)
|
||||
.add(Attributes.FLYING_SPEED, 0.28D)
|
||||
@@ -105,15 +102,15 @@ public class OctopusBombEntity extends Monster implements GeoEntity {
|
||||
.add(Attributes.KNOCKBACK_RESISTANCE, 0.4D);
|
||||
}
|
||||
|
||||
public static boolean canSpawn(EntityType<OctopusBombEntity> entityType, ServerLevelAccessor level, MobSpawnType spawnReason, BlockPos pos, RandomSource random) {
|
||||
return level.getFluidState(pos).is(FluidTags.WATER) && level.getFluidState(pos.above()).is(FluidTags.WATER);
|
||||
public static boolean canSpawn(EntityType<OctopusBombEntity> entityType, ServerLevelAccessor level, MobSpawnType spawnReason, net.minecraft.core.BlockPos pos, RandomSource random) {
|
||||
return level.getFluidState(pos).is(net.minecraft.tags.FluidTags.WATER)
|
||||
&& level.getFluidState(pos.above()).is(net.minecraft.tags.FluidTags.WATER);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void defineSynchedData(SynchedEntityData.Builder builder) {
|
||||
super.defineSynchedData(builder);
|
||||
builder.define(ATTACK_STATE, ATTACK_NONE);
|
||||
builder.define(ROARING, false);
|
||||
builder.define(ANIMATION_STATE, ANIM_IDLE);
|
||||
builder.define(CAN_FLY, false);
|
||||
}
|
||||
@@ -133,12 +130,26 @@ public class OctopusBombEntity extends Monster implements GeoEntity {
|
||||
|
||||
@Override
|
||||
protected void registerGoals() {
|
||||
this.goalSelector.addGoal(6, new LookAtPlayerGoal(this, Player.class, 20.0F));
|
||||
this.goalSelector.addGoal(7, new RandomLookAroundGoal(this));
|
||||
this.goalSelector.addGoal(5, new LookAtPlayerGoal(this, Player.class, 20.0F));
|
||||
this.goalSelector.addGoal(6, new RandomLookAroundGoal(this));
|
||||
|
||||
HurtByTargetGoal hurtByTargetGoal = new HurtByTargetGoal(this);
|
||||
hurtByTargetGoal.setAlertOthers(MissileSquidEntity.class);
|
||||
this.targetSelector.addGoal(1, hurtByTargetGoal);
|
||||
// Only retaliate when hit — neutral otherwise
|
||||
this.targetSelector.addGoal(1, new HurtByTargetGoal(this));
|
||||
// When spawned by Kraken and flying, target nearest player
|
||||
this.targetSelector.addGoal(2, new KrakenSummonTargetGoal(this));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PathNavigation createNavigation(Level level) {
|
||||
return new WaterBoundPathNavigation(this, level);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpawnGroupData finalizeSpawn(ServerLevelAccessor level, DifficultyInstance difficulty, MobSpawnType spawnReason, @Nullable SpawnGroupData spawnData) {
|
||||
Objects.requireNonNull(this.getAttribute(Attributes.MAX_HEALTH)).setBaseValue(com.craisinlord.antarchy.config.AntarchySettings.octopusBombHealth());
|
||||
this.setHealth((float) com.craisinlord.antarchy.config.AntarchySettings.octopusBombHealth());
|
||||
Objects.requireNonNull(this.getAttribute(Attributes.ATTACK_DAMAGE)).setBaseValue(com.craisinlord.antarchy.config.AntarchySettings.octopusBombAttackDamage());
|
||||
return super.finalizeSpawn(level, difficulty, spawnReason, spawnData);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -162,19 +173,6 @@ public class OctopusBombEntity extends Monster implements GeoEntity {
|
||||
return this.geoCache;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PathNavigation createNavigation(Level level) {
|
||||
return new WaterBoundPathNavigation(this, level);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpawnGroupData finalizeSpawn(ServerLevelAccessor level, DifficultyInstance difficulty, MobSpawnType spawnReason, @Nullable SpawnGroupData spawnData) {
|
||||
Objects.requireNonNull(this.getAttribute(Attributes.MAX_HEALTH)).setBaseValue(com.craisinlord.antarchy.config.AntarchySettings.octopusBombHealth());
|
||||
this.setHealth((float) com.craisinlord.antarchy.config.AntarchySettings.octopusBombHealth());
|
||||
Objects.requireNonNull(this.getAttribute(Attributes.ATTACK_DAMAGE)).setBaseValue(com.craisinlord.antarchy.config.AntarchySettings.octopusBombAttackDamage());
|
||||
return super.finalizeSpawn(level, difficulty, spawnReason, spawnData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void baseTick() {
|
||||
int airSupply = this.getAirSupply();
|
||||
@@ -196,22 +194,17 @@ public class OctopusBombEntity extends Monster implements GeoEntity {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.attackCooldown > 0) {
|
||||
this.attackCooldown--;
|
||||
}
|
||||
if (this.inkSprayCooldown > 0) {
|
||||
this.inkSprayCooldown--;
|
||||
}
|
||||
if (this.attackCooldown > 0) this.attackCooldown--;
|
||||
if (this.inkSprayCooldown > 0) this.inkSprayCooldown--;
|
||||
|
||||
// Ink spray — always active regardless of aggro state
|
||||
this.tickInkSpray();
|
||||
|
||||
LivingEntity target = this.getTarget();
|
||||
|
||||
this.tickDefensiveInkSpray();
|
||||
|
||||
if (target == null || !target.isAlive()) {
|
||||
this.resetCombatState();
|
||||
if (this.isInWater()) {
|
||||
this.tickWanderMovement();
|
||||
}
|
||||
if (this.isInWater()) this.tickWanderMovement();
|
||||
this.updateSwimRotation();
|
||||
this.updateAnimationState();
|
||||
return;
|
||||
@@ -219,32 +212,17 @@ public class OctopusBombEntity extends Monster implements GeoEntity {
|
||||
|
||||
this.getLookControl().setLookAt(target, 25.0F, 20.0F);
|
||||
|
||||
if (!this.aggroTriggered) {
|
||||
this.startAggroRoar();
|
||||
}
|
||||
|
||||
if (this.isRoaring()) {
|
||||
this.tickRoarState(target);
|
||||
this.updateSwimRotation();
|
||||
this.updateAnimationState();
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.getAttackState() != ATTACK_NONE) {
|
||||
this.tickCurrentAttack(target);
|
||||
this.updateSwimRotation();
|
||||
this.updateAnimationState();
|
||||
return;
|
||||
}
|
||||
|
||||
} else {
|
||||
this.tickPursuitMovement(target);
|
||||
|
||||
if (this.attackCooldown <= 0) {
|
||||
int nextAttack = this.chooseNextAttack(target);
|
||||
if (nextAttack != ATTACK_NONE) {
|
||||
this.startAttack(nextAttack);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.updateSwimRotation();
|
||||
this.updateAnimationState();
|
||||
@@ -258,7 +236,6 @@ public class OctopusBombEntity extends Monster implements GeoEntity {
|
||||
this.setDeltaMovement(this.getDeltaMovement().scale(this.isInWater() ? 0.92D : 0.91D));
|
||||
return;
|
||||
}
|
||||
|
||||
super.travel(travelVector);
|
||||
}
|
||||
|
||||
@@ -296,16 +273,12 @@ public class OctopusBombEntity extends Monster implements GeoEntity {
|
||||
@Override
|
||||
public void addAdditionalSaveData(CompoundTag tag) {
|
||||
super.addAdditionalSaveData(tag);
|
||||
tag.putInt(ATTACK_COOLDOWN_KEY, this.attackCooldown);
|
||||
tag.putInt(ACTION_TICKS_KEY, this.actionTicks);
|
||||
tag.putInt(LAST_ATTACK_STATE_KEY, this.lastAttackState);
|
||||
tag.putBoolean(AGGRO_TRIGGERED_KEY, this.aggroTriggered);
|
||||
tag.putBoolean(CAN_FLY_KEY, this.canFly());
|
||||
tag.putBoolean(SPAWNED_BY_KRAKEN_KEY, this.spawnedByKraken);
|
||||
if (this.grabbedTargetId >= 0) {
|
||||
tag.putInt(GRAB_TARGET_KEY, this.grabbedTargetId);
|
||||
}
|
||||
tag.putBoolean("Roaring", this.isRoaring());
|
||||
tag.putInt("AttackCooldown", this.attackCooldown);
|
||||
tag.putInt("ActionTicks", this.actionTicks);
|
||||
tag.putInt("LastAttackState", this.lastAttackState);
|
||||
tag.putBoolean("CanFly", this.canFly());
|
||||
tag.putBoolean("SpawnedByKraken", this.spawnedByKraken);
|
||||
if (this.grabbedTargetId >= 0) tag.putInt("GrabTarget", this.grabbedTargetId);
|
||||
tag.putInt("AttackState", this.getAttackState());
|
||||
tag.putInt("AnimationState", this.getAnimationState());
|
||||
}
|
||||
@@ -313,27 +286,24 @@ public class OctopusBombEntity extends Monster implements GeoEntity {
|
||||
@Override
|
||||
public void readAdditionalSaveData(CompoundTag tag) {
|
||||
super.readAdditionalSaveData(tag);
|
||||
this.attackCooldown = tag.getInt(ATTACK_COOLDOWN_KEY);
|
||||
this.actionTicks = tag.getInt(ACTION_TICKS_KEY);
|
||||
this.lastAttackState = tag.contains(LAST_ATTACK_STATE_KEY) ? tag.getInt(LAST_ATTACK_STATE_KEY) : ATTACK_NONE;
|
||||
this.aggroTriggered = tag.getBoolean(AGGRO_TRIGGERED_KEY);
|
||||
this.setCanFly(tag.getBoolean(CAN_FLY_KEY));
|
||||
this.spawnedByKraken = tag.getBoolean(SPAWNED_BY_KRAKEN_KEY);
|
||||
this.grabbedTargetId = tag.contains(GRAB_TARGET_KEY) ? tag.getInt(GRAB_TARGET_KEY) : -1;
|
||||
this.entityData.set(ROARING, tag.getBoolean("Roaring"));
|
||||
this.attackCooldown = tag.getInt("AttackCooldown");
|
||||
this.actionTicks = tag.getInt("ActionTicks");
|
||||
this.lastAttackState = tag.contains("LastAttackState") ? tag.getInt("LastAttackState") : ATTACK_NONE;
|
||||
this.setCanFly(tag.getBoolean("CanFly"));
|
||||
this.spawnedByKraken = tag.getBoolean("SpawnedByKraken");
|
||||
this.grabbedTargetId = tag.contains("GrabTarget") ? tag.getInt("GrabTarget") : -1;
|
||||
this.entityData.set(ATTACK_STATE, tag.getInt("AttackState"));
|
||||
this.entityData.set(ANIMATION_STATE, tag.contains("AnimationState") ? tag.getInt("AnimationState") : ANIM_IDLE);
|
||||
this.updateAnimationState();
|
||||
}
|
||||
|
||||
public boolean isRoaring() {
|
||||
return this.entityData.get(ROARING);
|
||||
}
|
||||
|
||||
public boolean canFly() {
|
||||
return this.entityData.get(CAN_FLY);
|
||||
}
|
||||
|
||||
public void setCanFly(boolean canFly) {
|
||||
this.entityData.set(CAN_FLY, canFly);
|
||||
}
|
||||
|
||||
public int getAttackState() {
|
||||
return this.entityData.get(ATTACK_STATE);
|
||||
}
|
||||
@@ -342,49 +312,79 @@ public class OctopusBombEntity extends Monster implements GeoEntity {
|
||||
return this.entityData.get(ANIMATION_STATE);
|
||||
}
|
||||
|
||||
private void setRoaring(boolean roaring) {
|
||||
this.entityData.set(ROARING, roaring);
|
||||
private void setAttackState(int state) {
|
||||
this.entityData.set(ATTACK_STATE, state);
|
||||
}
|
||||
|
||||
public void setCanFly(boolean canFly) {
|
||||
this.entityData.set(CAN_FLY, canFly);
|
||||
private void setAnimationState(int state) {
|
||||
this.entityData.set(ANIMATION_STATE, state);
|
||||
}
|
||||
|
||||
public void launchAsProjectile(net.minecraft.world.entity.Entity owner, net.minecraft.world.phys.Vec3 velocity) {
|
||||
public void launchAsProjectile(net.minecraft.world.entity.Entity owner, Vec3 velocity) {
|
||||
this.spawnedByKraken = true;
|
||||
this.setCanFly(true);
|
||||
this.setDeltaMovement(velocity);
|
||||
}
|
||||
|
||||
private void setAttackState(int attackState) {
|
||||
this.entityData.set(ATTACK_STATE, attackState);
|
||||
// -------------------------------------------------------------------------
|
||||
// Squid-style wander
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
private void tickWanderMovement() {
|
||||
if (--this.wanderRetargetTicks <= 0) {
|
||||
this.wanderRetargetTicks = 40 + this.random.nextInt(40);
|
||||
float angle = this.random.nextFloat() * (float) (Math.PI * 2);
|
||||
this.wanderDx = Mth.cos(angle) * 0.1f;
|
||||
this.wanderDy = -0.05f + this.random.nextFloat() * 0.1f;
|
||||
this.wanderDz = Mth.sin(angle) * 0.1f;
|
||||
}
|
||||
this.setDeltaMovement(this.getDeltaMovement().add(this.wanderDx, this.wanderDy, this.wanderDz));
|
||||
}
|
||||
|
||||
private void setAnimationState(int animationState) {
|
||||
this.entityData.set(ANIMATION_STATE, animationState);
|
||||
// -------------------------------------------------------------------------
|
||||
// Ink spray
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
private void tickInkSpray() {
|
||||
if (this.inkSprayCooldown > 0 || !(this.level() instanceof ServerLevel serverLevel)) return;
|
||||
|
||||
Player nearbyPlayer = this.level().getNearestPlayer(this, INK_SPRAY_RANGE);
|
||||
if (nearbyPlayer == null || nearbyPlayer.isCreative() || nearbyPlayer.isSpectator()) return;
|
||||
|
||||
this.inkSprayCooldown = INK_SPRAY_COOLDOWN;
|
||||
this.playSound(AntarchySoundEvents.KRAKEN_HURT.get(), 1.0F, 1.49F);
|
||||
|
||||
double cx = this.getX();
|
||||
double cy = this.getY() + this.getBbHeight() * 0.5D;
|
||||
double cz = this.getZ();
|
||||
serverLevel.sendParticles(ParticleTypes.SQUID_INK, cx, cy, cz, 120, 2.0D, 1.2D, 2.0D, 0.04D);
|
||||
serverLevel.sendParticles(ParticleTypes.SQUID_INK, cx, cy, cz, 60, 3.5D, 1.8D, 3.5D, 0.02D);
|
||||
}
|
||||
|
||||
private void startAggroRoar() {
|
||||
this.aggroTriggered = true;
|
||||
this.actionTicks = 30;
|
||||
this.setRoaring(true);
|
||||
this.playSound(AntarchySoundEvents.KRAKEN_ROAR.get(), 1.4F, 1.07F);
|
||||
this.getNavigation().stop();
|
||||
this.setDeltaMovement(this.getDeltaMovement().scale(0.2D));
|
||||
this.updateAnimationState();
|
||||
// -------------------------------------------------------------------------
|
||||
// Combat
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
private void tickPursuitMovement(LivingEntity target) {
|
||||
if (this.strafeRetargetTicks-- <= 0) {
|
||||
this.strafeRetargetTicks = 10 + this.random.nextInt(12);
|
||||
if (this.random.nextFloat() < 0.4F) this.orbitDirection *= -1.0F;
|
||||
}
|
||||
Vec3 attackPoint = this.createCombatTarget(target);
|
||||
this.getMoveControl().setWantedPosition(attackPoint.x, attackPoint.y, attackPoint.z, 1.1D);
|
||||
}
|
||||
|
||||
private void tickRoarState(LivingEntity target) {
|
||||
this.getNavigation().stop();
|
||||
this.getMoveControl().setWantedPosition(this.getX(), this.getY(), this.getZ(), 0.0D);
|
||||
this.setDeltaMovement(this.getDeltaMovement().scale(0.6D));
|
||||
this.getLookControl().setLookAt(target, 25.0F, 20.0F);
|
||||
|
||||
if (--this.actionTicks <= 0) {
|
||||
this.actionTicks = 0;
|
||||
this.setRoaring(false);
|
||||
this.attackCooldown = 18;
|
||||
}
|
||||
private Vec3 createCombatTarget(LivingEntity target) {
|
||||
double radius = 9.0D;
|
||||
double angle = this.tickCount * 0.16D * this.orbitDirection + this.getId() * 0.11D;
|
||||
double x = target.getX() + Math.cos(angle) * radius;
|
||||
double z = target.getZ() + Math.sin(angle) * radius;
|
||||
double y = Mth.clamp(
|
||||
target.getY() + 1.0D + Math.sin((this.tickCount + this.getId()) * 0.18D),
|
||||
target.getY() - 3.0D,
|
||||
target.getY() + 4.0D
|
||||
);
|
||||
return new Vec3(x, y, z);
|
||||
}
|
||||
|
||||
private void tickCurrentAttack(LivingEntity target) {
|
||||
@@ -402,7 +402,6 @@ public class OctopusBombEntity extends Monster implements GeoEntity {
|
||||
private void startAttack(int attackState) {
|
||||
this.setAttackState(attackState);
|
||||
this.grabbedTargetId = -1;
|
||||
|
||||
if (attackState == ATTACK_GRAB) {
|
||||
this.actionTicks = 55;
|
||||
this.playSound(AntarchySoundEvents.KRAKEN_ATTACK.get(), 1.2F, 1.07F);
|
||||
@@ -417,33 +416,26 @@ public class OctopusBombEntity extends Monster implements GeoEntity {
|
||||
this.grabbedTargetId = target.getId();
|
||||
}
|
||||
|
||||
LivingEntity grabbedTarget = this.getGrabbedTarget();
|
||||
if (grabbedTarget != null && grabbedTarget.isAlive()) {
|
||||
this.holdGrabbedTarget(grabbedTarget);
|
||||
LivingEntity grabbed = this.getGrabbedTarget();
|
||||
if (grabbed != null && grabbed.isAlive()) {
|
||||
this.holdGrabbedTarget(grabbed);
|
||||
if (this.actionTicks <= 45 && this.actionTicks > 10 && this.actionTicks % 10 == 0) {
|
||||
if (this.level() instanceof ServerLevel serverLevel) {
|
||||
grabbedTarget.hurt(AntarchyDamageSources.krakenMauling(serverLevel, this), 4.0F);
|
||||
grabbed.hurt(AntarchyDamageSources.krakenMauling(serverLevel, this), 4.0F);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (--this.actionTicks <= 0) {
|
||||
if (grabbedTarget != null && grabbedTarget.isAlive()) {
|
||||
this.pullDown(grabbedTarget, 2.5D);
|
||||
}
|
||||
if (grabbed != null && grabbed.isAlive()) this.pullDown(grabbed, 2.5D);
|
||||
this.grabbedTargetId = -1;
|
||||
this.finishAttack();
|
||||
}
|
||||
}
|
||||
|
||||
private void tickSwipeAttack() {
|
||||
if (this.actionTicks == 10) {
|
||||
this.performSwipe();
|
||||
}
|
||||
|
||||
if (--this.actionTicks <= 0) {
|
||||
this.finishAttack();
|
||||
}
|
||||
if (this.actionTicks == 10) this.performSwipe();
|
||||
if (--this.actionTicks <= 0) this.finishAttack();
|
||||
}
|
||||
|
||||
private void finishAttack() {
|
||||
@@ -453,70 +445,13 @@ public class OctopusBombEntity extends Monster implements GeoEntity {
|
||||
this.attackCooldown = 18;
|
||||
this.grabbedTargetId = -1;
|
||||
this.lastAttackState = finished;
|
||||
this.updateAnimationState();
|
||||
}
|
||||
|
||||
private void tickPursuitMovement(LivingEntity target) {
|
||||
if (this.strafeRetargetTicks-- <= 0) {
|
||||
this.strafeRetargetTicks = 10 + this.random.nextInt(12);
|
||||
if (this.random.nextFloat() < 0.4F) {
|
||||
this.orbitDirection *= -1.0F;
|
||||
}
|
||||
}
|
||||
|
||||
Vec3 attackPoint = this.createCombatTarget(target);
|
||||
this.getMoveControl().setWantedPosition(attackPoint.x, attackPoint.y, attackPoint.z, 1.1D);
|
||||
}
|
||||
|
||||
private void resetCombatState() {
|
||||
this.setAttackState(ATTACK_NONE);
|
||||
this.setRoaring(false);
|
||||
this.setTarget(null);
|
||||
this.actionTicks = 0;
|
||||
this.grabbedTargetId = -1;
|
||||
this.aggroTriggered = false;
|
||||
this.updateAnimationState();
|
||||
}
|
||||
|
||||
private void handleAirSupply(int previousAirSupply) {
|
||||
if (this.isInWaterOrBubble()) {
|
||||
this.setAirSupply(this.getMaxAirSupply());
|
||||
return;
|
||||
}
|
||||
|
||||
this.setAirSupply(previousAirSupply - 1);
|
||||
if (this.getAirSupply() <= -20) {
|
||||
this.setAirSupply(0);
|
||||
this.hurt(this.damageSources().drown(), 2.0F);
|
||||
}
|
||||
}
|
||||
|
||||
private void tickDefensiveInkSpray() {
|
||||
if (this.inkSprayCooldown > 0 || !(this.level() instanceof ServerLevel serverLevel)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Player nearbyPlayer = this.level().getNearestPlayer(this, INK_SPRAY_RANGE);
|
||||
if (nearbyPlayer == null || nearbyPlayer.isCreative() || nearbyPlayer.isSpectator()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.inkSprayCooldown = INK_SPRAY_COOLDOWN;
|
||||
this.playSound(AntarchySoundEvents.KRAKEN_HURT.get(), 1.0F, 1.49F);
|
||||
|
||||
double centerY = this.getY() + this.getBbHeight() * 0.5D;
|
||||
serverLevel.sendParticles(ParticleTypes.SQUID_INK, this.getX(), centerY, this.getZ(), 55, 1.2D, 0.8D, 1.2D, 0.02D);
|
||||
serverLevel.sendParticles(ParticleTypes.SQUID_INK, this.getX(), centerY, this.getZ(), 35, 2.0D, 1.1D, 2.0D, 0.01D);
|
||||
}
|
||||
|
||||
private int chooseNextAttack(LivingEntity target) {
|
||||
double distSqr = this.distanceToSqr(target);
|
||||
boolean canGrab = distSqr <= 42.25D;
|
||||
boolean canSwipe = distSqr <= 81.0D && this.isTargetInFront(target, -0.15D);
|
||||
|
||||
if (!canGrab && !canSwipe) {
|
||||
return ATTACK_NONE;
|
||||
}
|
||||
if (!canGrab && !canSwipe) return ATTACK_NONE;
|
||||
|
||||
int grabWeight = canGrab ? (this.lastAttackState == ATTACK_GRAB ? 1 : 4) : 0;
|
||||
int swipeWeight = canSwipe ? (this.lastAttackState == ATTACK_SWIPE ? 1 : 5) : 0;
|
||||
@@ -524,19 +459,19 @@ public class OctopusBombEntity extends Monster implements GeoEntity {
|
||||
if (total == 0) return ATTACK_NONE;
|
||||
|
||||
int roll = this.random.nextInt(total);
|
||||
if (roll < grabWeight) return ATTACK_GRAB;
|
||||
return ATTACK_SWIPE;
|
||||
return roll < grabWeight ? ATTACK_GRAB : ATTACK_SWIPE;
|
||||
}
|
||||
|
||||
private void performSwipe() {
|
||||
AABB hitBox = this.getBoundingBox().inflate(7.0D, 3.0D, 7.0D);
|
||||
List<Player> players = this.level().getEntitiesOfClass(Player.class, hitBox, player -> player.isAlive() && this.isTargetInFront(player, -0.15D));
|
||||
List<Player> players = this.level().getEntitiesOfClass(Player.class, hitBox,
|
||||
p -> p.isAlive() && this.isTargetInFront(p, -0.15D));
|
||||
float damage = (float) this.getAttributeValue(Attributes.ATTACK_DAMAGE);
|
||||
for (Player player : players) {
|
||||
if (this.level() instanceof ServerLevel serverLevel) {
|
||||
player.hurt(AntarchyDamageSources.krakenMauling(serverLevel, this), damage);
|
||||
Vec3 direction = player.position().subtract(this.position());
|
||||
Vec3 horizontal = new Vec3(direction.x, 0.0D, direction.z).normalize().scale(1.75D);
|
||||
Vec3 dir = player.position().subtract(this.position());
|
||||
Vec3 horizontal = new Vec3(dir.x, 0.0D, dir.z).normalize().scale(1.75D);
|
||||
player.push(horizontal.x, 0.3D, horizontal.z);
|
||||
player.hurtMarked = true;
|
||||
}
|
||||
@@ -547,45 +482,49 @@ public class OctopusBombEntity extends Monster implements GeoEntity {
|
||||
Vec3 anchor = this.position().add(0.0D, -this.getBbHeight() * 0.4D, 0.0D);
|
||||
target.setDeltaMovement(anchor.subtract(target.position()).scale(0.35D));
|
||||
target.resetFallDistance();
|
||||
if (target instanceof ServerPlayer serverPlayer) {
|
||||
serverPlayer.connection.teleport(anchor.x, anchor.y, anchor.z, target.getYRot(), target.getXRot());
|
||||
if (target instanceof ServerPlayer sp) {
|
||||
sp.connection.teleport(anchor.x, anchor.y, anchor.z, target.getYRot(), target.getXRot());
|
||||
} else {
|
||||
target.moveTo(anchor.x, anchor.y, anchor.z, target.getYRot(), target.getXRot());
|
||||
}
|
||||
target.hurtMarked = true;
|
||||
}
|
||||
|
||||
private void pullDown(LivingEntity target, double downwardStrength) {
|
||||
target.push(0.0D, -downwardStrength, 0.0D);
|
||||
private void pullDown(LivingEntity target, double strength) {
|
||||
target.push(0.0D, -strength, 0.0D);
|
||||
target.hurtMarked = true;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private LivingEntity getGrabbedTarget() {
|
||||
if (this.grabbedTargetId < 0) {
|
||||
return null;
|
||||
}
|
||||
net.minecraft.world.entity.Entity entity = this.level().getEntity(this.grabbedTargetId);
|
||||
return entity instanceof LivingEntity livingEntity ? livingEntity : null;
|
||||
if (this.grabbedTargetId < 0) return null;
|
||||
net.minecraft.world.entity.Entity e = this.level().getEntity(this.grabbedTargetId);
|
||||
return e instanceof LivingEntity le ? le : null;
|
||||
}
|
||||
|
||||
private boolean isTargetInFront(LivingEntity target, double minimumDot) {
|
||||
private boolean isTargetInFront(LivingEntity target, double minDot) {
|
||||
Vec3 forward = this.getViewVector(1.0F);
|
||||
Vec3 toTarget = target.position().subtract(this.position()).normalize();
|
||||
return forward.dot(toTarget) >= minimumDot;
|
||||
return forward.dot(toTarget) >= minDot;
|
||||
}
|
||||
|
||||
private Vec3 createCombatTarget(LivingEntity target) {
|
||||
double radius = 9.0D;
|
||||
double angle = this.tickCount * 0.16D * this.orbitDirection + this.getId() * 0.11D;
|
||||
double x = target.getX() + Math.cos(angle) * radius;
|
||||
double z = target.getZ() + Math.sin(angle) * radius;
|
||||
double y = Mth.clamp(
|
||||
target.getY() + 1.0D + Math.sin((this.tickCount + this.getId()) * 0.18D),
|
||||
target.getY() - 3.0D,
|
||||
target.getY() + 4.0D
|
||||
);
|
||||
return new Vec3(x, y, z);
|
||||
private void resetCombatState() {
|
||||
this.setAttackState(ATTACK_NONE);
|
||||
this.setTarget(null);
|
||||
this.actionTicks = 0;
|
||||
this.grabbedTargetId = -1;
|
||||
}
|
||||
|
||||
private void handleAirSupply(int previousAirSupply) {
|
||||
if (this.isInWaterOrBubble()) {
|
||||
this.setAirSupply(this.getMaxAirSupply());
|
||||
return;
|
||||
}
|
||||
this.setAirSupply(previousAirSupply - 1);
|
||||
if (this.getAirSupply() <= -20) {
|
||||
this.setAirSupply(0);
|
||||
this.hurt(this.damageSources().drown(), 2.0F);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateSwimRotation() {
|
||||
@@ -596,21 +535,41 @@ public class OctopusBombEntity extends Monster implements GeoEntity {
|
||||
this.yBodyRot = this.getYRot();
|
||||
this.yHeadRot = this.getYRot();
|
||||
}
|
||||
|
||||
if (velocity.lengthSqr() > 1.0E-4D) {
|
||||
float targetPitch = (float) (-(Mth.atan2(velocity.y, velocity.horizontalDistance()) * (180.0D / Math.PI)));
|
||||
this.setXRot(Mth.approachDegrees(this.getXRot(), targetPitch, 4.0F));
|
||||
}
|
||||
}
|
||||
|
||||
private void updateAnimationState() {
|
||||
if (this.isDeadOrDying()) {
|
||||
this.setAnimationState(ANIM_DEATH);
|
||||
return;
|
||||
}
|
||||
switch (this.getAttackState()) {
|
||||
case ATTACK_GRAB -> { this.setAnimationState(ANIM_GRAB); return; }
|
||||
case ATTACK_SWIPE -> { this.setAnimationState(ANIM_SWIPE); return; }
|
||||
default -> {}
|
||||
}
|
||||
if (!this.isInWater()) {
|
||||
this.setAnimationState(ANIM_SWIM);
|
||||
return;
|
||||
}
|
||||
Vec3 vel = this.getDeltaMovement();
|
||||
double hSqr = vel.horizontalDistanceSqr();
|
||||
double vSqr = vel.y * vel.y;
|
||||
if (hSqr > 0.03D && hSqr > vSqr * 2.5D) {
|
||||
this.setAnimationState(ANIM_SWIM);
|
||||
return;
|
||||
}
|
||||
this.setAnimationState(ANIM_IDLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void die(DamageSource damageSource) {
|
||||
if (!this.level().isClientSide) {
|
||||
this.setAnimationState(ANIM_DEATH);
|
||||
this.actionTicks = 0;
|
||||
this.attackCooldown = 0;
|
||||
this.setAttackState(ATTACK_NONE);
|
||||
this.setRoaring(false);
|
||||
this.setDeltaMovement(Vec3.ZERO);
|
||||
this.getNavigation().stop();
|
||||
}
|
||||
@@ -620,65 +579,28 @@ public class OctopusBombEntity extends Monster implements GeoEntity {
|
||||
@Override
|
||||
protected void tickDeath() {
|
||||
this.deathTime++;
|
||||
|
||||
if (this.deathTime == 1) {
|
||||
this.setAnimationState(ANIM_DEATH);
|
||||
}
|
||||
|
||||
if (this.deathTime == 1) this.setAnimationState(ANIM_DEATH);
|
||||
if (this.deathTime >= DEATH_ANIM_TICKS) {
|
||||
this.remove(RemovalReason.KILLED);
|
||||
this.dropExperience(this);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateAnimationState() {
|
||||
if (this.isDeadOrDying()) {
|
||||
this.setAnimationState(ANIM_DEATH);
|
||||
return;
|
||||
// -------------------------------------------------------------------------
|
||||
// Inner goal: only target players when spawned by Kraken in fly mode
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
private static final class KrakenSummonTargetGoal extends NearestAttackableTargetGoal<Player> {
|
||||
private final OctopusBombEntity octopus;
|
||||
|
||||
KrakenSummonTargetGoal(OctopusBombEntity octopus) {
|
||||
super(octopus, Player.class, true);
|
||||
this.octopus = octopus;
|
||||
}
|
||||
|
||||
if (this.isRoaring()) {
|
||||
this.setAnimationState(ANIM_IDLE);
|
||||
return;
|
||||
@Override
|
||||
public boolean canUse() {
|
||||
return this.octopus.spawnedByKraken && this.octopus.canFly() && super.canUse();
|
||||
}
|
||||
|
||||
switch (this.getAttackState()) {
|
||||
case ATTACK_GRAB -> {
|
||||
this.setAnimationState(ANIM_GRAB);
|
||||
return;
|
||||
}
|
||||
case ATTACK_SWIPE -> {
|
||||
this.setAnimationState(ANIM_SWIPE);
|
||||
return;
|
||||
}
|
||||
default -> {
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.isInWater()) {
|
||||
this.setAnimationState(ANIM_SWIM);
|
||||
return;
|
||||
}
|
||||
|
||||
Vec3 velocity = this.getDeltaMovement();
|
||||
double horizontalSpeedSqr = velocity.horizontalDistanceSqr();
|
||||
double verticalSpeedSqr = velocity.y * velocity.y;
|
||||
if (horizontalSpeedSqr > 0.03D && horizontalSpeedSqr > verticalSpeedSqr * 2.5D) {
|
||||
this.setAnimationState(ANIM_SWIM);
|
||||
return;
|
||||
}
|
||||
|
||||
this.setAnimationState(ANIM_IDLE);
|
||||
}
|
||||
|
||||
private void tickWanderMovement() {
|
||||
if (--this.wanderRetargetTicks <= 0) {
|
||||
this.wanderRetargetTicks = 40 + this.random.nextInt(40);
|
||||
float angle = this.random.nextFloat() * (float) (Math.PI * 2);
|
||||
this.wanderDx = Mth.cos(angle) * 0.1f;
|
||||
this.wanderDy = -0.05f + this.random.nextFloat() * 0.1f;
|
||||
this.wanderDz = Mth.sin(angle) * 0.1f;
|
||||
}
|
||||
this.setDeltaMovement(this.getDeltaMovement().add(this.wanderDx, this.wanderDy, this.wanderDz));
|
||||
}
|
||||
}
|
||||
@@ -1230,11 +1230,11 @@ public abstract class BaseAntEntity extends Animal implements GeoEntity {
|
||||
}
|
||||
|
||||
private boolean shouldPlayAttackAnimation() {
|
||||
if (!(this instanceof RedAntEntity)) {
|
||||
return false;
|
||||
return this.shouldUseBiteAnimation() && this.getBiteAnimationTicks() > 0;
|
||||
}
|
||||
|
||||
return this.getBiteAnimationTicks() > 0;
|
||||
protected boolean shouldUseBiteAnimation() {
|
||||
return this instanceof RedAntEntity;
|
||||
}
|
||||
|
||||
private int getBiteAnimationTicks() {
|
||||
@@ -1245,6 +1245,10 @@ public abstract class BaseAntEntity extends Animal implements GeoEntity {
|
||||
this.entityData.set(BITE_ANIM_TICKS, Math.max(0, ticks));
|
||||
}
|
||||
|
||||
protected final void triggerBiteAnimation(int ticks) {
|
||||
this.setBiteAnimationTicks(ticks);
|
||||
}
|
||||
|
||||
private void syncAttribute(@Nullable AttributeInstance attributeInstance, double desiredValue, boolean rescaleCurrentHealth) {
|
||||
if (attributeInstance == null || Math.abs(attributeInstance.getBaseValue() - desiredValue) < ATTRIBUTE_EPSILON) {
|
||||
return;
|
||||
|
||||
@@ -17,6 +17,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
import software.bernie.geckolib.animatable.GeoEntity;
|
||||
|
||||
public class TermiteEntity extends BaseAntEntity implements GeoEntity {
|
||||
private static final int WOOD_BITE_ANIMATION_TICKS = 8;
|
||||
private static final int WOOD_SEARCH_INTERVAL_TICKS = 10;
|
||||
private static final int WOOD_REPATH_INTERVAL_TICKS = 6;
|
||||
private static final int WOOD_SEARCH_RADIUS_HORIZONTAL = 12;
|
||||
@@ -88,6 +89,11 @@ public class TermiteEntity extends BaseAntEntity implements GeoEntity {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean shouldUseBiteAnimation() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean handlePriorityForaging() {
|
||||
if (!this.level().getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) {
|
||||
@@ -126,6 +132,7 @@ public class TermiteEntity extends BaseAntEntity implements GeoEntity {
|
||||
}
|
||||
|
||||
SoundType soundType = targetState.getSoundType();
|
||||
this.triggerBiteAnimation(WOOD_BITE_ANIMATION_TICKS);
|
||||
this.level().destroyBlock(targetPos, false, this);
|
||||
this.level().playSound(null, targetPos, soundType.getBreakSound(), this.getSoundSource(), 0.7F, 1.1F);
|
||||
return true;
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.craisinlord.antarchy.content.fluid;
|
||||
|
||||
import com.craisinlord.antarchy.content.AntarchyObjects;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.effect.MobEffectInstance;
|
||||
import net.minecraft.world.effect.MobEffects;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
import net.minecraft.tags.EntityTypeTags;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.LiquidBlock;
|
||||
import net.minecraft.world.level.block.state.BlockBehaviour;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.material.FlowingFluid;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
|
||||
public class BileLiquidBlock extends LiquidBlock {
|
||||
private static final int EFFECT_DURATION_TICKS = 100;
|
||||
private static final Vec3 SLOW_MOVEMENT = new Vec3(0.85D, 0.95D, 0.85D);
|
||||
|
||||
public BileLiquidBlock(FlowingFluid fluid, BlockBehaviour.Properties properties) {
|
||||
super(fluid, properties);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void entityInside(BlockState state, Level level, BlockPos pos, Entity entity) {
|
||||
super.entityInside(state, level, pos, entity);
|
||||
|
||||
if (entity instanceof LivingEntity livingEntity) {
|
||||
if (!level.isClientSide) {
|
||||
livingEntity.addEffect(new MobEffectInstance(AntarchyObjects.STINKY_EFFECT.get(), EFFECT_DURATION_TICKS, 0, false, true, true));
|
||||
livingEntity.addEffect(new MobEffectInstance(MobEffects.WEAKNESS, EFFECT_DURATION_TICKS, 0, false, true, true));
|
||||
}
|
||||
|
||||
if (livingEntity.getType().is(EntityTypeTags.SENSITIVE_TO_BANE_OF_ARTHROPODS)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
entity.makeStuckInBlock(state, SLOW_MOVEMENT);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
package com.craisinlord.antarchy.content.worldgen.cavaryn;
|
||||
|
||||
import com.craisinlord.antarchy.Antarchy;
|
||||
import com.mojang.serialization.Codec;
|
||||
import java.util.Optional;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.util.Mth;
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.world.level.WorldGenLevel;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.block.LiquidBlock;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.levelgen.feature.Feature;
|
||||
import net.minecraft.world.level.levelgen.feature.FeaturePlaceContext;
|
||||
import net.minecraft.world.level.levelgen.feature.configurations.NoneFeatureConfiguration;
|
||||
import net.minecraft.world.level.material.FluidState;
|
||||
|
||||
public final class CavarynBilePoolFeature extends Feature<NoneFeatureConfiguration> {
|
||||
private static final ResourceLocation BILE_ID = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "bile");
|
||||
private static final int SEARCH_RADIUS = 8;
|
||||
private static final int SEARCH_ATTEMPTS = 24;
|
||||
private static final int FLOOR_SCAN_RANGE = 14;
|
||||
|
||||
public CavarynBilePoolFeature(Codec<NoneFeatureConfiguration> codec) {
|
||||
super(codec);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean place(FeaturePlaceContext<NoneFeatureConfiguration> context) {
|
||||
Block bileBlock = getBlock(BILE_ID);
|
||||
if (!(bileBlock instanceof LiquidBlock)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
WorldGenLevel level = context.level();
|
||||
RandomSource random = context.random();
|
||||
BlockPos floorCenter = findPoolFloor(level, context.origin(), random);
|
||||
if (floorCenter == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return placePool(level, floorCenter, bileBlock.defaultBlockState(), random);
|
||||
}
|
||||
|
||||
private static BlockPos findPoolFloor(WorldGenLevel level, BlockPos origin, RandomSource random) {
|
||||
BlockPos.MutableBlockPos mutable = new BlockPos.MutableBlockPos();
|
||||
for (int attempt = 0; attempt < SEARCH_ATTEMPTS; attempt++) {
|
||||
int x = origin.getX() + random.nextInt(SEARCH_RADIUS * 2 + 1) - SEARCH_RADIUS;
|
||||
int z = origin.getZ() + random.nextInt(SEARCH_RADIUS * 2 + 1) - SEARCH_RADIUS;
|
||||
int startY = origin.getY() + random.nextInt(FLOOR_SCAN_RANGE * 2 + 1) - FLOOR_SCAN_RANGE;
|
||||
startY = Math.max(level.getMinBuildHeight() + 3, Math.min(level.getMaxBuildHeight() - 4, startY));
|
||||
|
||||
for (int y = startY; y >= Math.max(level.getMinBuildHeight() + 2, startY - FLOOR_SCAN_RANGE); y--) {
|
||||
mutable.set(x, y, z);
|
||||
BlockState floorState = level.getBlockState(mutable);
|
||||
if (!isValidFloor(level, mutable, floorState)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
BlockState aboveState = level.getBlockState(mutable.above());
|
||||
if (!isOpenBlock(aboveState)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (countSolidNeighbors(level, mutable) >= 3) {
|
||||
return mutable.immutable();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static boolean placePool(WorldGenLevel level, BlockPos center, BlockState bileState, RandomSource random) {
|
||||
boolean placedAny = false;
|
||||
int radius = 2 + random.nextInt(3);
|
||||
BlockPos.MutableBlockPos mutable = new BlockPos.MutableBlockPos();
|
||||
|
||||
for (int dx = -radius; dx <= radius; dx++) {
|
||||
for (int dz = -radius; dz <= radius; dz++) {
|
||||
double normalized = (dx * dx + dz * dz) / (double) Math.max(1, radius * radius);
|
||||
if (normalized > 1.0D + random.nextDouble() * 0.2D) {
|
||||
continue;
|
||||
}
|
||||
|
||||
double density = 1.0D - Math.min(1.0D, normalized);
|
||||
int localDepth = density > 0.45D || random.nextFloat() < 0.35F ? 2 : 1;
|
||||
localDepth = Mth.clamp(localDepth, 1, 2);
|
||||
|
||||
for (int depth = 0; depth < localDepth; depth++) {
|
||||
mutable.set(center.getX() + dx, center.getY() - depth, center.getZ() + dz);
|
||||
BlockState existing = level.getBlockState(mutable);
|
||||
FluidState existingFluid = existing.getFluidState();
|
||||
if (existing.is(Blocks.BEDROCK)) {
|
||||
break;
|
||||
}
|
||||
if (!existing.canBeReplaced() && existingFluid.isEmpty() && !existing.blocksMotion()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
level.setBlock(mutable, bileState, 2);
|
||||
scheduleFluidTick(level, mutable, bileState);
|
||||
placedAny = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return placedAny;
|
||||
}
|
||||
|
||||
private static boolean isValidFloor(WorldGenLevel level, BlockPos pos, BlockState state) {
|
||||
return !state.isAir()
|
||||
&& !state.is(Blocks.BEDROCK)
|
||||
&& state.getFluidState().isEmpty()
|
||||
&& state.blocksMotion()
|
||||
&& state.isFaceSturdy(level, pos, Direction.UP);
|
||||
}
|
||||
|
||||
private static boolean isOpenBlock(BlockState state) {
|
||||
return state.isAir() || state.canBeReplaced() || !state.getFluidState().isEmpty();
|
||||
}
|
||||
|
||||
private static int countSolidNeighbors(WorldGenLevel level, BlockPos pos) {
|
||||
int solidNeighbors = 0;
|
||||
BlockPos.MutableBlockPos mutable = new BlockPos.MutableBlockPos();
|
||||
for (Direction direction : Direction.Plane.HORIZONTAL) {
|
||||
mutable.setWithOffset(pos, direction);
|
||||
if (isValidFloor(level, mutable, level.getBlockState(mutable))) {
|
||||
solidNeighbors++;
|
||||
}
|
||||
}
|
||||
return solidNeighbors;
|
||||
}
|
||||
|
||||
private static void scheduleFluidTick(WorldGenLevel level, BlockPos pos, BlockState state) {
|
||||
FluidState fluidState = state.getFluidState();
|
||||
if (!fluidState.isEmpty()) {
|
||||
level.scheduleTick(pos, fluidState.getType(), fluidState.getType().getTickDelay(level));
|
||||
}
|
||||
}
|
||||
|
||||
private static Block getBlock(ResourceLocation id) {
|
||||
Optional<Block> block = BuiltInRegistries.BLOCK.getOptional(id);
|
||||
return block.orElse(null);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
package com.craisinlord.antarchy.content.worldgen.cavaryn;
|
||||
|
||||
import com.craisinlord.antarchy.Antarchy;
|
||||
import com.mojang.serialization.Codec;
|
||||
import java.util.Optional;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.world.level.WorldGenLevel;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.VineBlock;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.levelgen.feature.Feature;
|
||||
import net.minecraft.world.level.levelgen.feature.FeaturePlaceContext;
|
||||
import net.minecraft.world.level.levelgen.feature.configurations.NoneFeatureConfiguration;
|
||||
|
||||
public final class CavarynCreepvineFeature extends Feature<NoneFeatureConfiguration> {
|
||||
private static final ResourceLocation CREEPVINE_ID = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "creepvine");
|
||||
private static final int SEARCH_RADIUS = 8;
|
||||
private static final int SEARCH_ATTEMPTS = 24;
|
||||
private static final int VERTICAL_SCAN = 12;
|
||||
|
||||
public CavarynCreepvineFeature(Codec<NoneFeatureConfiguration> codec) {
|
||||
super(codec);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean place(FeaturePlaceContext<NoneFeatureConfiguration> context) {
|
||||
Block creepvineBlock = getBlock(CREEPVINE_ID);
|
||||
if (!(creepvineBlock instanceof VineBlock vineBlock)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
WorldGenLevel level = context.level();
|
||||
RandomSource random = context.random();
|
||||
Anchor anchor = findAnchor(level, context.origin(), random);
|
||||
if (anchor == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return placeVineColumn(level, vineBlock, anchor, random);
|
||||
}
|
||||
|
||||
private static Anchor findAnchor(WorldGenLevel level, BlockPos origin, RandomSource random) {
|
||||
BlockPos.MutableBlockPos mutable = new BlockPos.MutableBlockPos();
|
||||
for (int attempt = 0; attempt < SEARCH_ATTEMPTS; attempt++) {
|
||||
int x = origin.getX() + random.nextInt(SEARCH_RADIUS * 2 + 1) - SEARCH_RADIUS;
|
||||
int z = origin.getZ() + random.nextInt(SEARCH_RADIUS * 2 + 1) - SEARCH_RADIUS;
|
||||
int startY = origin.getY() + random.nextInt(VERTICAL_SCAN * 2 + 1) - VERTICAL_SCAN;
|
||||
startY = Math.max(level.getMinBuildHeight() + 3, Math.min(level.getMaxBuildHeight() - 3, startY));
|
||||
|
||||
for (int y = startY; y >= Math.max(level.getMinBuildHeight() + 1, startY - VERTICAL_SCAN); y--) {
|
||||
mutable.set(x, y, z);
|
||||
if (!level.getBlockState(mutable).canBeReplaced()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (Direction direction : Direction.Plane.HORIZONTAL) {
|
||||
BlockPos supportPos = mutable.relative(direction);
|
||||
BlockState supportState = level.getBlockState(supportPos);
|
||||
if (supportState.isFaceSturdy(level, supportPos, direction.getOpposite())) {
|
||||
return new Anchor(mutable.immutable(), direction);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static boolean placeVineColumn(WorldGenLevel level, VineBlock vineBlock, Anchor anchor, RandomSource random) {
|
||||
boolean placedAny = false;
|
||||
int length = 3 + random.nextInt(6);
|
||||
|
||||
for (int offset = 0; offset < length; offset++) {
|
||||
BlockPos vinePos = anchor.pos().below(offset);
|
||||
if (vinePos.getY() <= level.getMinBuildHeight()) {
|
||||
break;
|
||||
}
|
||||
|
||||
BlockState existing = level.getBlockState(vinePos);
|
||||
if (!existing.canBeReplaced() && !existing.is(vineBlock)) {
|
||||
break;
|
||||
}
|
||||
|
||||
BlockState vineState = createVineState(vineBlock, anchor.supportDirection());
|
||||
if (vineState == null || !vineState.canSurvive(level, vinePos)) {
|
||||
break;
|
||||
}
|
||||
|
||||
level.setBlock(vinePos, vineState, 3);
|
||||
placedAny = true;
|
||||
}
|
||||
|
||||
return placedAny;
|
||||
}
|
||||
|
||||
private static BlockState createVineState(VineBlock vineBlock, Direction attachedFace) {
|
||||
BlockState state = vineBlock.defaultBlockState();
|
||||
return switch (attachedFace) {
|
||||
case NORTH -> state.setValue(VineBlock.NORTH, Boolean.TRUE);
|
||||
case SOUTH -> state.setValue(VineBlock.SOUTH, Boolean.TRUE);
|
||||
case EAST -> state.setValue(VineBlock.EAST, Boolean.TRUE);
|
||||
case WEST -> state.setValue(VineBlock.WEST, Boolean.TRUE);
|
||||
default -> null;
|
||||
};
|
||||
}
|
||||
|
||||
private static Block getBlock(ResourceLocation id) {
|
||||
Optional<Block> block = BuiltInRegistries.BLOCK.getOptional(id);
|
||||
return block.orElse(null);
|
||||
}
|
||||
|
||||
private record Anchor(BlockPos pos, Direction supportDirection) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
package com.craisinlord.antarchy.content.worldgen.cavaryn;
|
||||
|
||||
import com.craisinlord.antarchy.content.AntarchyObjects;
|
||||
import com.craisinlord.antarchy.content.block.CreepingHorrorEggBlock;
|
||||
import com.craisinlord.antarchy.content.block.LurkingTerrorEggBlock;
|
||||
import com.mojang.serialization.Codec;
|
||||
import java.util.List;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.world.level.WorldGenLevel;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.levelgen.feature.Feature;
|
||||
import net.minecraft.world.level.levelgen.feature.FeaturePlaceContext;
|
||||
import net.minecraft.world.level.levelgen.feature.configurations.NoneFeatureConfiguration;
|
||||
|
||||
public final class CavarynEggPatchFeature extends Feature<NoneFeatureConfiguration> {
|
||||
private static final int SEARCH_RADIUS = 8;
|
||||
private static final int SEARCH_ATTEMPTS = 20;
|
||||
private static final int FLOOR_SCAN_RANGE = 10;
|
||||
|
||||
public CavarynEggPatchFeature(Codec<NoneFeatureConfiguration> codec) {
|
||||
super(codec);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean place(FeaturePlaceContext<NoneFeatureConfiguration> context) {
|
||||
WorldGenLevel level = context.level();
|
||||
RandomSource random = context.random();
|
||||
BlockPos origin = context.origin();
|
||||
|
||||
BlockPos surfacePos = findSurfacePos(level, origin, random);
|
||||
if (surfacePos == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
EggType eggType = random.nextBoolean() ? EggType.CREEPING_HORROR : EggType.LURKING_TERROR;
|
||||
return placePatch(level, surfacePos, random, eggType);
|
||||
}
|
||||
|
||||
private static BlockPos findSurfacePos(WorldGenLevel level, BlockPos origin, RandomSource random) {
|
||||
BlockPos.MutableBlockPos mutable = new BlockPos.MutableBlockPos();
|
||||
for (int attempt = 0; attempt < SEARCH_ATTEMPTS; attempt++) {
|
||||
int x = origin.getX() + random.nextInt(SEARCH_RADIUS * 2 + 1) - SEARCH_RADIUS;
|
||||
int z = origin.getZ() + random.nextInt(SEARCH_RADIUS * 2 + 1) - SEARCH_RADIUS;
|
||||
int startY = origin.getY() + random.nextInt(FLOOR_SCAN_RANGE * 2 + 1) - FLOOR_SCAN_RANGE;
|
||||
startY = Math.max(level.getMinBuildHeight() + 2, Math.min(level.getMaxBuildHeight() - 3, startY));
|
||||
|
||||
for (int y = startY; y >= Math.max(level.getMinBuildHeight() + 1, startY - FLOOR_SCAN_RANGE); y--) {
|
||||
mutable.set(x, y, z);
|
||||
BlockState floorState = level.getBlockState(mutable);
|
||||
BlockState airState = level.getBlockState(mutable.above());
|
||||
if (floorState.getFluidState().isEmpty()
|
||||
&& floorState.blocksMotion()
|
||||
&& floorState.isFaceSturdy(level, mutable, Direction.UP)
|
||||
&& airState.canBeReplaced()) {
|
||||
return mutable.immutable();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean placePatch(WorldGenLevel level, BlockPos center, RandomSource random, EggType eggType) {
|
||||
int radius = 2 + random.nextInt(3);
|
||||
boolean placedAny = false;
|
||||
List<BlockPos> eggSpots = new java.util.ArrayList<>();
|
||||
|
||||
for (int dx = -radius; dx <= radius; dx++) {
|
||||
for (int dz = -radius; dz <= radius; dz++) {
|
||||
double distance = dx * dx + dz * dz;
|
||||
if (distance > radius * radius + random.nextInt(2)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
BlockPos floorPos = center.offset(dx, 0, dz);
|
||||
BlockState floorState = level.getBlockState(floorPos);
|
||||
BlockPos eggPos = floorPos.above();
|
||||
BlockState aboveState = level.getBlockState(eggPos);
|
||||
if (!isValidEggFloor(level, floorPos, floorState, aboveState)) {
|
||||
continue;
|
||||
}
|
||||
eggSpots.add(eggPos);
|
||||
}
|
||||
}
|
||||
|
||||
if (eggSpots.isEmpty()) {
|
||||
return placedAny;
|
||||
}
|
||||
|
||||
int eggCount = Math.min(eggSpots.size(), 2 + random.nextInt(3));
|
||||
for (int i = 0; i < eggCount; i++) {
|
||||
BlockPos eggPos = eggSpots.get(random.nextInt(eggSpots.size()));
|
||||
if (!level.getBlockState(eggPos).canBeReplaced()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
BlockState eggState = eggType == EggType.CREEPING_HORROR
|
||||
? AntarchyObjects.CREEPING_HORROR_EGGS.get().defaultBlockState()
|
||||
: AntarchyObjects.LURKING_TERROR_EGGS.get().defaultBlockState();
|
||||
eggState = eggState.setValue(eggType == EggType.CREEPING_HORROR ? CreepingHorrorEggBlock.EGGS : LurkingTerrorEggBlock.EGGS, 1 + random.nextInt(4));
|
||||
level.setBlock(eggPos, eggState, 3);
|
||||
placedAny = true;
|
||||
}
|
||||
|
||||
return placedAny;
|
||||
}
|
||||
|
||||
private static boolean isValidEggFloor(WorldGenLevel level, BlockPos floorPos, BlockState floorState, BlockState aboveState) {
|
||||
return !floorState.isAir()
|
||||
&& floorState.getFluidState().isEmpty()
|
||||
&& floorState.blocksMotion()
|
||||
&& floorState.isFaceSturdy(level, floorPos, Direction.UP)
|
||||
&& aboveState.canBeReplaced();
|
||||
}
|
||||
|
||||
private enum EggType {
|
||||
CREEPING_HORROR,
|
||||
LURKING_TERROR
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
package com.craisinlord.antarchy.content.worldgen.cavaryn;
|
||||
|
||||
import com.craisinlord.antarchy.Antarchy;
|
||||
import com.mojang.serialization.Codec;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.world.level.WorldGenLevel;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.MultifaceBlock;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.levelgen.feature.Feature;
|
||||
import net.minecraft.world.level.levelgen.feature.FeaturePlaceContext;
|
||||
import net.minecraft.world.level.levelgen.feature.configurations.NoneFeatureConfiguration;
|
||||
|
||||
public final class CavarynWallAmberMossFeature extends Feature<NoneFeatureConfiguration> {
|
||||
private static final ResourceLocation AMBER_LICHEN_ID = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "amber_lichen");
|
||||
private static final ResourceLocation GLOW_LICHEN_ID = ResourceLocation.withDefaultNamespace("glow_lichen");
|
||||
private static final int SEARCH_RADIUS = 8;
|
||||
private static final int SEARCH_ATTEMPTS = 28;
|
||||
private static final int VERTICAL_SCAN = 10;
|
||||
|
||||
public CavarynWallAmberMossFeature(Codec<NoneFeatureConfiguration> codec) {
|
||||
super(codec);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean place(FeaturePlaceContext<NoneFeatureConfiguration> context) {
|
||||
WorldGenLevel level = context.level();
|
||||
RandomSource random = context.random();
|
||||
Block amberLichenBlock = getBlock(AMBER_LICHEN_ID);
|
||||
Block glowLichenBlock = getBlock(GLOW_LICHEN_ID);
|
||||
if (amberLichenBlock == null || glowLichenBlock == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Anchor anchor = findAnchor(level, context.origin(), random);
|
||||
if (anchor == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
List<BlockPos> placedPatch = placeWallPatch(level, anchor, amberLichenBlock, random);
|
||||
if (placedPatch.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
placeLichenFringe(level, placedPatch, anchor.supportDirection(), amberLichenBlock, glowLichenBlock, random);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static Anchor findAnchor(WorldGenLevel level, BlockPos origin, RandomSource random) {
|
||||
BlockPos.MutableBlockPos mutable = new BlockPos.MutableBlockPos();
|
||||
for (int attempt = 0; attempt < SEARCH_ATTEMPTS; attempt++) {
|
||||
int x = origin.getX() + random.nextInt(SEARCH_RADIUS * 2 + 1) - SEARCH_RADIUS;
|
||||
int z = origin.getZ() + random.nextInt(SEARCH_RADIUS * 2 + 1) - SEARCH_RADIUS;
|
||||
int startY = origin.getY() + random.nextInt(VERTICAL_SCAN * 2 + 1) - VERTICAL_SCAN;
|
||||
startY = Math.max(level.getMinBuildHeight() + 2, Math.min(level.getMaxBuildHeight() - 3, startY));
|
||||
|
||||
for (int y = startY; y >= Math.max(level.getMinBuildHeight() + 1, startY - VERTICAL_SCAN); y--) {
|
||||
mutable.set(x, y, z);
|
||||
if (!level.getBlockState(mutable).canBeReplaced()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (Direction direction : Direction.Plane.HORIZONTAL) {
|
||||
BlockPos supportPos = mutable.relative(direction);
|
||||
BlockState supportState = level.getBlockState(supportPos);
|
||||
if (supportState.isFaceSturdy(level, supportPos, direction.getOpposite())) {
|
||||
return new Anchor(mutable.immutable(), direction);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static List<BlockPos> placeWallPatch(WorldGenLevel level, Anchor anchor, Block amberLichenBlock, RandomSource random) {
|
||||
List<BlockPos> placed = new ArrayList<>();
|
||||
Direction lateral = anchor.supportDirection().getClockWise();
|
||||
int width = 1 + random.nextInt(3);
|
||||
int height = 2 + random.nextInt(4);
|
||||
|
||||
for (int yOffset = 0; yOffset < height; yOffset++) {
|
||||
for (int lateralOffset = -width; lateralOffset <= width; lateralOffset++) {
|
||||
if (Math.abs(lateralOffset) == width && random.nextBoolean()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
BlockPos pos = anchor.pos().above(yOffset).relative(lateral, lateralOffset);
|
||||
if (!canPlaceWallMoss(level, pos, anchor.supportDirection())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
BlockState state = createLichenState(amberLichenBlock, anchor.supportDirection());
|
||||
if (!state.canSurvive(level, pos)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
level.setBlock(pos, state, 3);
|
||||
placed.add(pos.immutable());
|
||||
}
|
||||
}
|
||||
|
||||
return placed;
|
||||
}
|
||||
|
||||
private static void placeLichenFringe(
|
||||
WorldGenLevel level,
|
||||
List<BlockPos> patchPositions,
|
||||
Direction supportDirection,
|
||||
Block amberLichenBlock,
|
||||
Block glowLichenBlock,
|
||||
RandomSource random
|
||||
) {
|
||||
for (BlockPos patchPos : patchPositions) {
|
||||
for (Direction spread : List.of(Direction.UP, Direction.DOWN, supportDirection.getClockWise(), supportDirection.getCounterClockWise())) {
|
||||
if (random.nextFloat() > 0.45F) {
|
||||
continue;
|
||||
}
|
||||
|
||||
BlockPos targetPos = patchPos.relative(spread);
|
||||
if (!level.getBlockState(targetPos).canBeReplaced()) {
|
||||
continue;
|
||||
}
|
||||
if (!hasSupport(level, targetPos, supportDirection)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
BlockState state = createLichenState(random.nextFloat() < 0.65F ? amberLichenBlock : glowLichenBlock, supportDirection);
|
||||
if (!state.canSurvive(level, targetPos)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
level.setBlock(targetPos, state, 3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean canPlaceWallMoss(WorldGenLevel level, BlockPos pos, Direction supportDirection) {
|
||||
return level.getBlockState(pos).canBeReplaced() && hasSupport(level, pos, supportDirection);
|
||||
}
|
||||
|
||||
private static boolean hasSupport(WorldGenLevel level, BlockPos pos, Direction supportDirection) {
|
||||
BlockPos supportPos = pos.relative(supportDirection);
|
||||
return level.getBlockState(supportPos).isFaceSturdy(level, supportPos, supportDirection.getOpposite());
|
||||
}
|
||||
|
||||
private static BlockState createLichenState(Block lichenBlock, Direction supportDirection) {
|
||||
return lichenBlock.defaultBlockState().setValue(MultifaceBlock.getFaceProperty(supportDirection), true);
|
||||
}
|
||||
|
||||
private static Block getBlock(ResourceLocation id) {
|
||||
Optional<Block> block = BuiltInRegistries.BLOCK.getOptional(id);
|
||||
return block.orElse(null);
|
||||
}
|
||||
|
||||
private record Anchor(BlockPos pos, Direction supportDirection) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package com.craisinlord.antarchy.content.worldgen.cavaryn;
|
||||
|
||||
import com.mojang.serialization.Codec;
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder;
|
||||
import net.minecraft.world.level.levelgen.feature.configurations.FeatureConfiguration;
|
||||
|
||||
public record ChitenSpikeConfiguration(
|
||||
int floorToCeilingSearchRange,
|
||||
int radius,
|
||||
int maxColumnHeight,
|
||||
int heightDeviation,
|
||||
float chanceOfColumnAtMaxDistanceFromCenter,
|
||||
int maxDistanceFromCenterAffectingChanceOfColumn,
|
||||
int maxDistanceFromCenterAffectingHeightBias,
|
||||
int maxHeightDifference,
|
||||
float largeVariantChance,
|
||||
int largeVariantExtraRadius,
|
||||
int largeVariantExtraHeight,
|
||||
int largeVariantExtraHeightDeviation,
|
||||
float largeVariantSurfaceSpikeMultiplier
|
||||
) implements FeatureConfiguration {
|
||||
public static final Codec<ChitenSpikeConfiguration> CODEC = RecordCodecBuilder.create(instance -> instance.group(
|
||||
Codec.intRange(1, 128).fieldOf("floor_to_ceiling_search_range").forGetter(ChitenSpikeConfiguration::floorToCeilingSearchRange),
|
||||
Codec.intRange(1, 32).fieldOf("radius").forGetter(ChitenSpikeConfiguration::radius),
|
||||
Codec.intRange(1, 32).fieldOf("max_column_height").forGetter(ChitenSpikeConfiguration::maxColumnHeight),
|
||||
Codec.intRange(1, 16).fieldOf("height_deviation").forGetter(ChitenSpikeConfiguration::heightDeviation),
|
||||
Codec.floatRange(0.0F, 1.0F).fieldOf("chance_of_column_at_max_distance_from_center").forGetter(ChitenSpikeConfiguration::chanceOfColumnAtMaxDistanceFromCenter),
|
||||
Codec.intRange(1, 64).fieldOf("max_distance_from_center_affecting_chance_of_column").forGetter(ChitenSpikeConfiguration::maxDistanceFromCenterAffectingChanceOfColumn),
|
||||
Codec.intRange(1, 64).fieldOf("max_distance_from_center_affecting_height_bias").forGetter(ChitenSpikeConfiguration::maxDistanceFromCenterAffectingHeightBias),
|
||||
Codec.intRange(0, 32).fieldOf("max_height_difference").forGetter(ChitenSpikeConfiguration::maxHeightDifference),
|
||||
Codec.floatRange(0.0F, 1.0F).optionalFieldOf("large_variant_chance", 0.0F).forGetter(ChitenSpikeConfiguration::largeVariantChance),
|
||||
Codec.intRange(0, 24).optionalFieldOf("large_variant_extra_radius", 0).forGetter(ChitenSpikeConfiguration::largeVariantExtraRadius),
|
||||
Codec.intRange(0, 32).optionalFieldOf("large_variant_extra_height", 0).forGetter(ChitenSpikeConfiguration::largeVariantExtraHeight),
|
||||
Codec.intRange(0, 16).optionalFieldOf("large_variant_extra_height_deviation", 0).forGetter(ChitenSpikeConfiguration::largeVariantExtraHeightDeviation),
|
||||
Codec.floatRange(1.0F, 8.0F).optionalFieldOf("large_variant_surface_spike_multiplier", 1.0F).forGetter(ChitenSpikeConfiguration::largeVariantSurfaceSpikeMultiplier)
|
||||
).apply(instance, ChitenSpikeConfiguration::new));
|
||||
}
|
||||
@@ -0,0 +1,253 @@
|
||||
package com.craisinlord.antarchy.content.worldgen.cavaryn;
|
||||
|
||||
import com.craisinlord.antarchy.content.AntarchyObjects;
|
||||
import com.mojang.serialization.Codec;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.OptionalInt;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.util.Mth;
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.world.level.WorldGenLevel;
|
||||
import net.minecraft.world.level.block.PointedDripstoneBlock;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.block.state.properties.DripstoneThickness;
|
||||
import net.minecraft.world.level.levelgen.Column;
|
||||
import net.minecraft.world.level.levelgen.feature.Feature;
|
||||
import net.minecraft.world.level.levelgen.feature.FeaturePlaceContext;
|
||||
|
||||
public final class ChitenSpikeFeature extends Feature<ChitenSpikeConfiguration> {
|
||||
private record ResolvedVariant(int radius, int maxColumnHeight, int heightDeviation, int surfaceSpikeAttempts) {
|
||||
}
|
||||
|
||||
public ChitenSpikeFeature(Codec<ChitenSpikeConfiguration> codec) {
|
||||
super(codec);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean place(FeaturePlaceContext<ChitenSpikeConfiguration> context) {
|
||||
WorldGenLevel level = context.level();
|
||||
BlockPos origin = context.origin();
|
||||
ChitenSpikeConfiguration config = context.config();
|
||||
RandomSource random = context.random();
|
||||
|
||||
if (!level.getBlockState(origin).isAir()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean placedAny = false;
|
||||
List<BlockPos> placedBlocks = new ArrayList<>();
|
||||
ResolvedVariant variant = this.resolveVariant(config, random);
|
||||
int radius = variant.radius();
|
||||
for (int dx = -radius; dx <= radius; dx++) {
|
||||
for (int dz = -radius; dz <= radius; dz++) {
|
||||
double chance = this.getChanceOfColumn(radius, dx, dz, config);
|
||||
if (chance <= 0.0D) {
|
||||
continue;
|
||||
}
|
||||
if (this.placeColumn(level, random, origin.offset(dx, 0, dz), dx, dz, chance, config, variant, placedBlocks)) {
|
||||
placedAny = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.dotSurfaceSpikes(level, random, placedBlocks, config, variant)) {
|
||||
placedAny = true;
|
||||
}
|
||||
return placedAny;
|
||||
}
|
||||
|
||||
private ResolvedVariant resolveVariant(ChitenSpikeConfiguration config, RandomSource random) {
|
||||
if (config.largeVariantChance() > 0.0F && random.nextFloat() < config.largeVariantChance()) {
|
||||
int radius = config.radius() + config.largeVariantExtraRadius();
|
||||
int maxColumnHeight = config.maxColumnHeight() + config.largeVariantExtraHeight();
|
||||
int heightDeviation = config.heightDeviation() + config.largeVariantExtraHeightDeviation();
|
||||
int baseAttempts = Math.max(8, ((radius * 2) + 1) * ((radius * 2) + 1) / 6);
|
||||
int surfaceSpikeAttempts = Math.max(baseAttempts + 4, Math.round(baseAttempts * config.largeVariantSurfaceSpikeMultiplier()));
|
||||
return new ResolvedVariant(radius, maxColumnHeight, heightDeviation, surfaceSpikeAttempts);
|
||||
}
|
||||
|
||||
int radius = config.radius();
|
||||
int baseAttempts = Math.max(8, ((radius * 2) + 1) * ((radius * 2) + 1) / 6);
|
||||
return new ResolvedVariant(radius, config.maxColumnHeight(), config.heightDeviation(), baseAttempts);
|
||||
}
|
||||
|
||||
private boolean placeColumn(
|
||||
WorldGenLevel level,
|
||||
RandomSource random,
|
||||
BlockPos pos,
|
||||
int dx,
|
||||
int dz,
|
||||
double chance,
|
||||
ChitenSpikeConfiguration config,
|
||||
ResolvedVariant variant,
|
||||
List<BlockPos> placedBlocks
|
||||
) {
|
||||
Optional<Column> optional = Column.scan(level, pos, config.floorToCeilingSearchRange(), state -> state.isAir(), state -> !state.isAir());
|
||||
if (optional.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Column column = optional.get();
|
||||
OptionalInt ceiling = column.getCeiling();
|
||||
OptionalInt floor = column.getFloor();
|
||||
int maxHeight = this.getMaxLength(variant, ceiling, floor);
|
||||
int downLength = 0;
|
||||
int upLength = 0;
|
||||
|
||||
if (ceiling.isPresent()) {
|
||||
downLength = this.getSpikeHeight(random, dx, dz, chance, maxHeight, config.maxDistanceFromCenterAffectingHeightBias(), variant.heightDeviation());
|
||||
}
|
||||
if (floor.isPresent()) {
|
||||
if (downLength > 0) {
|
||||
upLength = Mth.clamp(
|
||||
downLength + Mth.randomBetweenInclusive(random, -config.maxHeightDifference(), config.maxHeightDifference()),
|
||||
0,
|
||||
maxHeight
|
||||
);
|
||||
} else {
|
||||
upLength = this.getSpikeHeight(random, dx, dz, chance, maxHeight, config.maxDistanceFromCenterAffectingHeightBias(), variant.heightDeviation());
|
||||
}
|
||||
}
|
||||
|
||||
boolean placedAny = false;
|
||||
if (ceiling.isPresent() && downLength > 0) {
|
||||
placedAny |= this.growChitenColumn(level, pos.atY(ceiling.getAsInt() - 1), Direction.DOWN, downLength, placedBlocks);
|
||||
}
|
||||
if (floor.isPresent() && upLength > 0) {
|
||||
placedAny |= this.growChitenColumn(level, pos.atY(floor.getAsInt() + 1), Direction.UP, upLength, placedBlocks);
|
||||
}
|
||||
return placedAny;
|
||||
}
|
||||
|
||||
private int getMaxLength(ResolvedVariant variant, OptionalInt ceiling, OptionalInt floor) {
|
||||
if (ceiling.isPresent() && floor.isPresent()) {
|
||||
return Math.min(variant.maxColumnHeight(), ceiling.getAsInt() - floor.getAsInt() - 1);
|
||||
}
|
||||
return variant.maxColumnHeight();
|
||||
}
|
||||
|
||||
private boolean growChitenColumn(WorldGenLevel level, BlockPos rootStart, Direction direction, int length, List<BlockPos> placedBlocks) {
|
||||
boolean placedAny = false;
|
||||
for (int offset = 0; offset < length; offset++) {
|
||||
BlockPos spikePos = rootStart.relative(direction, offset);
|
||||
if (spikePos.getY() < level.getMinBuildHeight() || spikePos.getY() >= level.getMaxBuildHeight()) {
|
||||
break;
|
||||
}
|
||||
|
||||
BlockState current = level.getBlockState(spikePos);
|
||||
if (!current.isAir()) {
|
||||
break;
|
||||
}
|
||||
|
||||
BlockState chitenState = AntarchyObjects.CHITEN_BLOCK.get().defaultBlockState();
|
||||
level.setBlock(spikePos, chitenState, 3);
|
||||
placedBlocks.add(spikePos.immutable());
|
||||
placedAny = true;
|
||||
}
|
||||
return placedAny;
|
||||
}
|
||||
|
||||
private boolean dotSurfaceSpikes(
|
||||
WorldGenLevel level,
|
||||
RandomSource random,
|
||||
List<BlockPos> placedBlocks,
|
||||
ChitenSpikeConfiguration config,
|
||||
ResolvedVariant variant
|
||||
) {
|
||||
boolean placedAny = false;
|
||||
if (placedBlocks.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int attempts = Math.max(variant.surfaceSpikeAttempts(), placedBlocks.size() / 6);
|
||||
for (int i = 0; i < attempts; i++) {
|
||||
BlockPos anchor = placedBlocks.get(random.nextInt(placedBlocks.size()));
|
||||
Direction direction = random.nextBoolean() ? Direction.UP : Direction.DOWN;
|
||||
int length = 1 + random.nextInt(Math.max(1, config.maxHeightDifference() + 1));
|
||||
BlockPos start = anchor.relative(direction);
|
||||
if (this.growSurfaceSpike(level, start, direction, length)) {
|
||||
placedAny = true;
|
||||
}
|
||||
}
|
||||
return placedAny;
|
||||
}
|
||||
|
||||
private boolean growSurfaceSpike(WorldGenLevel level, BlockPos startPos, Direction direction, int length) {
|
||||
boolean placedAny = false;
|
||||
for (int offset = 0; offset < length; offset++) {
|
||||
BlockPos spikePos = startPos.relative(direction, offset);
|
||||
if (spikePos.getY() < level.getMinBuildHeight() || spikePos.getY() >= level.getMaxBuildHeight()) {
|
||||
break;
|
||||
}
|
||||
if (!level.getBlockState(spikePos).isAir()) {
|
||||
break;
|
||||
}
|
||||
|
||||
BlockState spikeState = AntarchyObjects.CHITEN_SPIKE.get().defaultBlockState()
|
||||
.setValue(PointedDripstoneBlock.TIP_DIRECTION, direction)
|
||||
.setValue(PointedDripstoneBlock.THICKNESS, getThicknessForRootOffset(offset, length))
|
||||
.setValue(PointedDripstoneBlock.WATERLOGGED, false);
|
||||
if (!spikeState.canSurvive(level, spikePos)) {
|
||||
break;
|
||||
}
|
||||
|
||||
level.setBlock(spikePos, spikeState, 3);
|
||||
placedAny = true;
|
||||
}
|
||||
return placedAny;
|
||||
}
|
||||
|
||||
private static DripstoneThickness getThicknessForRootOffset(int offsetFromRoot, int length) {
|
||||
if (length <= 1 || offsetFromRoot == length - 1) {
|
||||
return DripstoneThickness.TIP;
|
||||
}
|
||||
if (offsetFromRoot == length - 2) {
|
||||
return DripstoneThickness.FRUSTUM;
|
||||
}
|
||||
if (offsetFromRoot == 0) {
|
||||
return DripstoneThickness.BASE;
|
||||
}
|
||||
return DripstoneThickness.MIDDLE;
|
||||
}
|
||||
|
||||
private int getSpikeHeight(
|
||||
RandomSource random,
|
||||
int dx,
|
||||
int dz,
|
||||
double chance,
|
||||
int maxHeight,
|
||||
int maxDistanceFromCenterAffectingHeightBias,
|
||||
int heightDeviation
|
||||
) {
|
||||
if (maxHeight <= 0 || random.nextDouble() > chance) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int distance = Math.abs(dx) + Math.abs(dz);
|
||||
float mean = (float) Mth.clampedMap(
|
||||
(double) distance,
|
||||
0.0D,
|
||||
(double) maxDistanceFromCenterAffectingHeightBias,
|
||||
(double) maxHeight,
|
||||
1.0D
|
||||
);
|
||||
int sampled = Math.round((float) (mean + random.nextGaussian() * Math.max(1, Math.min(maxHeight, heightDeviation))));
|
||||
return Mth.clamp(sampled, 1, maxHeight);
|
||||
}
|
||||
|
||||
private double getChanceOfColumn(int radius, int dx, int dz, ChitenSpikeConfiguration config) {
|
||||
int remainingX = radius - Math.abs(dx);
|
||||
int remainingZ = radius - Math.abs(dz);
|
||||
int edgeDistance = Math.min(remainingX, remainingZ);
|
||||
return Mth.clampedMap(
|
||||
(double) edgeDistance,
|
||||
0.0D,
|
||||
(double) config.maxDistanceFromCenterAffectingChanceOfColumn(),
|
||||
(double) config.chanceOfColumnAtMaxDistanceFromCenter(),
|
||||
1.0D
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
package com.craisinlord.antarchy.content.worldgen.elythia;
|
||||
|
||||
import com.mojang.serialization.Codec;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.world.level.WorldGenLevel;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.block.SeaPickleBlock;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
|
||||
import net.minecraft.world.level.levelgen.Heightmap;
|
||||
import net.minecraft.world.level.levelgen.feature.Feature;
|
||||
import net.minecraft.world.level.levelgen.feature.FeaturePlaceContext;
|
||||
import net.minecraft.world.level.levelgen.feature.configurations.NoneFeatureConfiguration;
|
||||
|
||||
public class CoralSpikeFeature extends Feature<NoneFeatureConfiguration> {
|
||||
|
||||
private record CoralSet(
|
||||
net.minecraft.world.level.block.Block block,
|
||||
net.minecraft.world.level.block.Block coral,
|
||||
net.minecraft.world.level.block.Block wallFan,
|
||||
net.minecraft.world.level.block.Block floorFan
|
||||
) {}
|
||||
|
||||
private static final CoralSet[] CORAL_SETS = {
|
||||
new CoralSet(Blocks.TUBE_CORAL_BLOCK, Blocks.TUBE_CORAL, Blocks.TUBE_CORAL_WALL_FAN, Blocks.TUBE_CORAL_FAN),
|
||||
new CoralSet(Blocks.BRAIN_CORAL_BLOCK, Blocks.BRAIN_CORAL, Blocks.BRAIN_CORAL_WALL_FAN, Blocks.BRAIN_CORAL_FAN),
|
||||
new CoralSet(Blocks.BUBBLE_CORAL_BLOCK, Blocks.BUBBLE_CORAL, Blocks.BUBBLE_CORAL_WALL_FAN, Blocks.BUBBLE_CORAL_FAN),
|
||||
new CoralSet(Blocks.FIRE_CORAL_BLOCK, Blocks.FIRE_CORAL, Blocks.FIRE_CORAL_WALL_FAN, Blocks.FIRE_CORAL_FAN),
|
||||
new CoralSet(Blocks.HORN_CORAL_BLOCK, Blocks.HORN_CORAL, Blocks.HORN_CORAL_WALL_FAN, Blocks.HORN_CORAL_FAN),
|
||||
};
|
||||
|
||||
public CoralSpikeFeature(Codec<NoneFeatureConfiguration> codec) {
|
||||
super(codec);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean place(FeaturePlaceContext<NoneFeatureConfiguration> context) {
|
||||
WorldGenLevel level = context.level();
|
||||
RandomSource random = context.random();
|
||||
BlockPos origin = context.origin();
|
||||
int seaLevel = level.getSeaLevel();
|
||||
|
||||
int spikes = 1 + random.nextInt(3);
|
||||
boolean placed = false;
|
||||
for (int i = 0; i < spikes; i++) {
|
||||
int offX = random.nextInt(11) - 5;
|
||||
int offZ = random.nextInt(11) - 5;
|
||||
placed |= placeSpike(level, random, origin.offset(offX, 0, offZ), seaLevel);
|
||||
}
|
||||
return placed;
|
||||
}
|
||||
|
||||
private boolean placeSpike(WorldGenLevel level, RandomSource random, BlockPos origin, int seaLevel) {
|
||||
int floorY = level.getHeight(Heightmap.Types.OCEAN_FLOOR_WG, origin.getX(), origin.getZ());
|
||||
if (floorY >= seaLevel) return false;
|
||||
|
||||
CoralSet coral = CORAL_SETS[random.nextInt(CORAL_SETS.length)];
|
||||
|
||||
int baseRadius = 2 + random.nextInt(2); // 2–3 wide at base
|
||||
int totalHeight = 20 + random.nextInt(15); // 20–34 blocks tall
|
||||
|
||||
// Curve: lean toward a random horizontal direction, accelerating toward the tip
|
||||
double curveAngle = random.nextDouble() * Math.PI * 2;
|
||||
double curveDX = Math.cos(curveAngle);
|
||||
double curveDZ = Math.sin(curveAngle);
|
||||
double maxCurve = 4.0 + random.nextDouble() * 5.0; // 4–9 block lean at tip
|
||||
|
||||
boolean placed = false;
|
||||
|
||||
for (int dy = 0; dy < totalHeight; dy++) {
|
||||
int blockY = floorY + dy;
|
||||
double progress = (double) dy / totalHeight;
|
||||
|
||||
// Quadratic lean — slow at base, bending hard near tip
|
||||
double curveOffset = maxCurve * progress * progress;
|
||||
double centerX = origin.getX() + curveDX * curveOffset;
|
||||
double centerZ = origin.getZ() + curveDZ * curveOffset;
|
||||
|
||||
// Taper: full width for bottom 25%, then narrows sharply to a point
|
||||
double taperT = Math.max(0.0, (progress - 0.25) / 0.75);
|
||||
int radius = (int) Math.max(0, baseRadius * (1.0 - taperT * taperT * 1.3));
|
||||
|
||||
for (int dx = -(radius + 1); dx <= radius + 1; dx++) {
|
||||
for (int dz = -(radius + 1); dz <= radius + 1; dz++) {
|
||||
double dist = Math.sqrt(
|
||||
(origin.getX() + dx - centerX) * (origin.getX() + dx - centerX)
|
||||
+ (origin.getZ() + dz - centerZ) * (origin.getZ() + dz - centerZ));
|
||||
|
||||
if (dist > radius + 0.5) continue;
|
||||
|
||||
BlockPos pos = new BlockPos(origin.getX() + dx, blockY, origin.getZ() + dz);
|
||||
if (!canReplace(level.getBlockState(pos))) continue;
|
||||
|
||||
level.setBlock(pos, coral.block().defaultBlockState(), 2);
|
||||
placed = true;
|
||||
|
||||
// Coral plants growing off the top of outer-surface blocks
|
||||
if (dist >= radius - 0.6 && radius > 0) {
|
||||
BlockPos above = pos.above();
|
||||
if (canReplace(level.getBlockState(above))) {
|
||||
boolean aboveInWater = level.getBlockState(above).is(Blocks.WATER);
|
||||
if (random.nextFloat() < 0.35f && aboveInWater) {
|
||||
level.setBlock(above,
|
||||
coral.coral().defaultBlockState()
|
||||
.setValue(BlockStateProperties.WATERLOGGED, true),
|
||||
2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Wall fans on the outer surface
|
||||
if (dist >= radius - 0.5) {
|
||||
for (Direction dir : Direction.Plane.HORIZONTAL) {
|
||||
if (random.nextFloat() >= 0.4f) continue;
|
||||
BlockPos fanPos = pos.relative(dir);
|
||||
BlockState existing = level.getBlockState(fanPos);
|
||||
if (!existing.isAir() && !existing.is(Blocks.WATER)) continue;
|
||||
boolean fanInWater = existing.is(Blocks.WATER);
|
||||
level.setBlock(fanPos,
|
||||
coral.wallFan().defaultBlockState()
|
||||
.setValue(BlockStateProperties.HORIZONTAL_FACING, dir)
|
||||
.setValue(BlockStateProperties.WATERLOGGED, fanInWater),
|
||||
2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Floor scatter: coral plants, fans, and sea pickles around the base
|
||||
int decorCount = 10 + random.nextInt(12);
|
||||
for (int i = 0; i < decorCount; i++) {
|
||||
int fx = origin.getX() + random.nextInt(13) - 6;
|
||||
int fz = origin.getZ() + random.nextInt(13) - 6;
|
||||
int fy = level.getHeight(Heightmap.Types.OCEAN_FLOOR_WG, fx, fz);
|
||||
BlockPos decorPos = new BlockPos(fx, fy, fz);
|
||||
|
||||
if (!canReplace(level.getBlockState(decorPos))) continue;
|
||||
BlockState below = level.getBlockState(decorPos.below());
|
||||
if (below.isAir() || !below.isFaceSturdy(level, decorPos.below(), Direction.UP)) continue;
|
||||
|
||||
boolean decorInWater = level.getBlockState(decorPos).is(Blocks.WATER);
|
||||
float roll = random.nextFloat();
|
||||
CoralSet randomCoral = CORAL_SETS[random.nextInt(CORAL_SETS.length)];
|
||||
|
||||
if (roll < 0.35f && decorInWater) {
|
||||
// Coral plant (frond)
|
||||
BlockState coralState = randomCoral.coral().defaultBlockState()
|
||||
.setValue(BlockStateProperties.WATERLOGGED, true);
|
||||
if (coralState.canSurvive(level, decorPos)) {
|
||||
level.setBlock(decorPos, coralState, 2);
|
||||
}
|
||||
} else if (roll < 0.65f) {
|
||||
// Floor fan
|
||||
BlockState fanState = randomCoral.floorFan().defaultBlockState()
|
||||
.setValue(BlockStateProperties.WATERLOGGED, decorInWater);
|
||||
if (fanState.canSurvive(level, decorPos)) {
|
||||
level.setBlock(decorPos, fanState, 2);
|
||||
}
|
||||
} else {
|
||||
// Sea pickle cluster
|
||||
BlockState pickle = Blocks.SEA_PICKLE.defaultBlockState()
|
||||
.setValue(SeaPickleBlock.PICKLES, 1 + random.nextInt(4));
|
||||
if (pickle.canSurvive(level, decorPos)) {
|
||||
level.setBlock(decorPos, pickle, 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return placed;
|
||||
}
|
||||
|
||||
private static boolean canReplace(BlockState state) {
|
||||
return state.isAir() || state.is(Blocks.WATER) || state.canBeReplaced();
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.craisinlord.antarchy.content.worldgen.elythia;
|
||||
|
||||
import com.craisinlord.antarchy.Antarchy;
|
||||
import com.mojang.datafixers.util.Pair;
|
||||
import com.mojang.serialization.Codec;
|
||||
import com.mojang.serialization.MapCodec;
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder;
|
||||
@@ -20,10 +21,18 @@ public class ElythiaBiomeSource extends BiomeSource {
|
||||
Registries.BIOME,
|
||||
ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "moleworm_caves")
|
||||
);
|
||||
private static final ResourceKey<Biome> ELYTHIA_LUSH_CAVES = ResourceKey.create(
|
||||
Registries.BIOME,
|
||||
ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "elythia_lush_caves")
|
||||
);
|
||||
private static final ResourceKey<Biome> ELYTHIA_OCEAN = ResourceKey.create(
|
||||
Registries.BIOME,
|
||||
ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "elythia_ocean")
|
||||
);
|
||||
private static final ResourceKey<Biome> ELYTHIA_CORAL_SPIKES = ResourceKey.create(
|
||||
Registries.BIOME,
|
||||
ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "elythia_coral_spikes")
|
||||
);
|
||||
private static final ResourceKey<Biome> ELYTHIA_BEACH = ResourceKey.create(
|
||||
Registries.BIOME,
|
||||
ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "elythia_beach")
|
||||
@@ -34,7 +43,8 @@ public class ElythiaBiomeSource extends BiomeSource {
|
||||
MultiNoiseBiomeSource.DIRECT_CODEC.forGetter(ElythiaBiomeSource::parameters),
|
||||
Codec.INT.optionalFieldOf("moleworm_caves_max_y", 72).forGetter(ElythiaBiomeSource::molewormCavesMaxY),
|
||||
Codec.INT.optionalFieldOf("surface_biome_sample_y", 160).forGetter(ElythiaBiomeSource::surfaceBiomeSampleY),
|
||||
Codec.INT.optionalFieldOf("ocean_max_y", 85).forGetter(ElythiaBiomeSource::oceanMaxY)
|
||||
Codec.INT.optionalFieldOf("ocean_max_y", 85).forGetter(ElythiaBiomeSource::oceanMaxY),
|
||||
Codec.INT.optionalFieldOf("sea_level", 78).forGetter(ElythiaBiomeSource::seaLevel)
|
||||
).apply(instance, ElythiaBiomeSource::new));
|
||||
|
||||
private final Climate.ParameterList<Holder<Biome>> parameters;
|
||||
@@ -42,36 +52,41 @@ public class ElythiaBiomeSource extends BiomeSource {
|
||||
private final int molewormCavesMaxY;
|
||||
private final int surfaceBiomeSampleY;
|
||||
private final int oceanMaxY;
|
||||
private final int seaLevel;
|
||||
private final int molewormCavesMaxQuartY;
|
||||
private final int surfaceBiomeSampleQuartY;
|
||||
private final int oceanMaxQuartY;
|
||||
private final int seaLevelQuartY;
|
||||
private final Holder<Biome> oceanHolder;
|
||||
private final Holder<Biome> defaultLandHolder;
|
||||
|
||||
public ElythiaBiomeSource(Climate.ParameterList<Holder<Biome>> parameters, int molewormCavesMaxY, int surfaceBiomeSampleY, int oceanMaxY) {
|
||||
public ElythiaBiomeSource(Climate.ParameterList<Holder<Biome>> parameters, int molewormCavesMaxY, int surfaceBiomeSampleY, int oceanMaxY, int seaLevel) {
|
||||
this.parameters = parameters;
|
||||
this.delegate = MultiNoiseBiomeSource.createFromList(parameters);
|
||||
this.molewormCavesMaxY = molewormCavesMaxY;
|
||||
this.surfaceBiomeSampleY = surfaceBiomeSampleY;
|
||||
this.oceanMaxY = oceanMaxY;
|
||||
this.seaLevel = seaLevel;
|
||||
this.molewormCavesMaxQuartY = QuartPos.fromBlock(molewormCavesMaxY);
|
||||
this.surfaceBiomeSampleQuartY = QuartPos.fromBlock(surfaceBiomeSampleY);
|
||||
this.oceanMaxQuartY = QuartPos.fromBlock(oceanMaxY);
|
||||
this.seaLevelQuartY = QuartPos.fromBlock(seaLevel);
|
||||
this.oceanHolder = parameters.values().stream()
|
||||
.map(Pair::getSecond)
|
||||
.filter(h -> h.is(ELYTHIA_OCEAN))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
this.defaultLandHolder = parameters.values().stream()
|
||||
.map(Pair::getSecond)
|
||||
.filter(h -> !isOceanBiome(h) && !h.is(ELYTHIA_BEACH)
|
||||
&& !h.is(MOLEWORM_CAVES) && !h.is(ELYTHIA_LUSH_CAVES))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
private Climate.ParameterList<Holder<Biome>> parameters() {
|
||||
return this.parameters;
|
||||
}
|
||||
|
||||
private int molewormCavesMaxY() {
|
||||
return this.molewormCavesMaxY;
|
||||
}
|
||||
|
||||
private int surfaceBiomeSampleY() {
|
||||
return this.surfaceBiomeSampleY;
|
||||
}
|
||||
|
||||
private int oceanMaxY() {
|
||||
return this.oceanMaxY;
|
||||
}
|
||||
private Climate.ParameterList<Holder<Biome>> parameters() { return this.parameters; }
|
||||
private int molewormCavesMaxY() { return this.molewormCavesMaxY; }
|
||||
private int surfaceBiomeSampleY() { return this.surfaceBiomeSampleY; }
|
||||
private int oceanMaxY() { return this.oceanMaxY; }
|
||||
private int seaLevel() { return this.seaLevel; }
|
||||
|
||||
@Override
|
||||
protected MapCodec<? extends BiomeSource> codec() {
|
||||
@@ -83,24 +98,53 @@ public class ElythiaBiomeSource extends BiomeSource {
|
||||
return this.delegate.possibleBiomes().stream();
|
||||
}
|
||||
|
||||
// Conservative threshold well inside the ocean entry range (-0.85 to -0.92).
|
||||
// At continentalness < -0.80 the terrain is reliably below sea level.
|
||||
// Transition-zone columns (-0.80 to -0.85) are treated as land to avoid
|
||||
// ocean biome appearing over above-sea-level terrain.
|
||||
private static final long OCEAN_CONTINENTALNESS_THRESHOLD = Climate.quantizeCoord(-0.80f);
|
||||
|
||||
@Override
|
||||
public Holder<Biome> getNoiseBiome(int x, int y, int z, Climate.Sampler sampler) {
|
||||
Holder<Biome> biome = this.delegate.getNoiseBiome(x, y, z, sampler);
|
||||
|
||||
// Cap moleworm caves below their max Y
|
||||
// Cap moleworm caves below their max Y — replace with surface biome above it
|
||||
if (biome.is(MOLEWORM_CAVES) && y > this.molewormCavesMaxQuartY) {
|
||||
return resolveSurfaceFallback(x, y, z, sampler, MOLEWORM_CAVES);
|
||||
}
|
||||
|
||||
// Cap ocean/beach biomes above sea level — if terrain noise puts these biomes
|
||||
// too high, replace with whatever surface biome would naturally be there
|
||||
if ((biome.is(ELYTHIA_OCEAN) || biome.is(ELYTHIA_BEACH)) && y > this.oceanMaxQuartY) {
|
||||
return resolveSurfaceFallback(x, y, z, sampler, ELYTHIA_OCEAN, ELYTHIA_BEACH);
|
||||
// Use continentalness (XZ-only noise) to classify the column.
|
||||
// Ocean columns get the ocean biome at every Y so structures, particles,
|
||||
// and ambient effects work correctly above the water surface.
|
||||
Climate.TargetPoint target = sampler.sample(x, this.seaLevelQuartY, z);
|
||||
if (target.continentalness() < OCEAN_CONTINENTALNESS_THRESHOLD) {
|
||||
return this.delegate.getNoiseBiome(x, this.seaLevelQuartY, z, sampler);
|
||||
}
|
||||
|
||||
// Land column below or at sea level: force ocean for any biome not allowed there.
|
||||
// Use <= so quart 19 (blocks 76-79, partly underwater) is covered.
|
||||
if (y <= this.seaLevelQuartY && oceanHolder != null && !allowedBelowSeaLevel(biome)) {
|
||||
return oceanHolder;
|
||||
}
|
||||
|
||||
// Land column: prevent ocean biomes from bleeding in at any Y
|
||||
if (isOceanBiome(biome)) {
|
||||
return resolveLandFallback(x, z, sampler);
|
||||
}
|
||||
|
||||
return biome;
|
||||
}
|
||||
|
||||
private static boolean allowedBelowSeaLevel(Holder<Biome> biome) {
|
||||
return biome.is(MOLEWORM_CAVES) || biome.is(ELYTHIA_LUSH_CAVES)
|
||||
|| biome.is(ELYTHIA_OCEAN) || biome.is(ELYTHIA_CORAL_SPIKES);
|
||||
}
|
||||
|
||||
private static boolean isOceanBiome(Holder<Biome> biome) {
|
||||
return biome.is(ELYTHIA_OCEAN) || biome.is(ELYTHIA_CORAL_SPIKES);
|
||||
}
|
||||
|
||||
// Used for moleworm caves — returns best Y-sample, falls back to whatever the delegate gives
|
||||
@SafeVarargs
|
||||
private Holder<Biome> resolveSurfaceFallback(int x, int y, int z, Climate.Sampler sampler, ResourceKey<Biome>... excluded) {
|
||||
Holder<Biome> fallback = this.delegate.getNoiseBiome(x, this.surfaceBiomeSampleQuartY, z, sampler);
|
||||
@@ -118,6 +162,19 @@ public class ElythiaBiomeSource extends BiomeSource {
|
||||
return fallback;
|
||||
}
|
||||
|
||||
// Used when ocean bleeds into a land column — guaranteed to never return an ocean/cave biome
|
||||
private Holder<Biome> resolveLandFallback(int x, int z, Climate.Sampler sampler) {
|
||||
Holder<Biome> fallback = this.delegate.getNoiseBiome(x, this.surfaceBiomeSampleQuartY, z, sampler);
|
||||
if (!isOceanOrCave(fallback)) return fallback;
|
||||
|
||||
for (int sampleBlockY : SURFACE_FALLBACK_BLOCK_YS) {
|
||||
Holder<Biome> candidate = this.delegate.getNoiseBiome(x, QuartPos.fromBlock(sampleBlockY), z, sampler);
|
||||
if (!isOceanOrCave(candidate)) return candidate;
|
||||
}
|
||||
|
||||
return defaultLandHolder != null ? defaultLandHolder : fallback;
|
||||
}
|
||||
|
||||
@SafeVarargs
|
||||
private static boolean isExcluded(Holder<Biome> biome, ResourceKey<Biome>... excluded) {
|
||||
for (ResourceKey<Biome> key : excluded) {
|
||||
@@ -126,10 +183,15 @@ public class ElythiaBiomeSource extends BiomeSource {
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean isOceanOrCave(Holder<Biome> biome) {
|
||||
return isOceanBiome(biome) || biome.is(ELYTHIA_BEACH)
|
||||
|| biome.is(MOLEWORM_CAVES) || biome.is(ELYTHIA_LUSH_CAVES);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addDebugInfo(java.util.List<String> debug, net.minecraft.core.BlockPos pos, Climate.Sampler sampler) {
|
||||
this.delegate.addDebugInfo(debug, pos, sampler);
|
||||
debug.add("Elythia mole cave cap: y<=" + this.molewormCavesMaxY);
|
||||
debug.add("Elythia ocean cap: y<=" + this.oceanMaxY);
|
||||
debug.add("Elythia sea level: " + this.seaLevel);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package com.craisinlord.antarchy.mixins;
|
||||
|
||||
import com.craisinlord.antarchy.Antarchy;
|
||||
import java.util.Optional;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(net.minecraft.world.level.block.entity.BlockEntity.class)
|
||||
public abstract class BlockEntitySignMixin {
|
||||
private static final ResourceLocation OURANWOOD_SIGN_ID = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "ouranwood_sign");
|
||||
private static final ResourceLocation OURANWOOD_WALL_SIGN_ID = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "ouranwood_wall_sign");
|
||||
private static final ResourceLocation OURANWOOD_HANGING_SIGN_ID = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "ouranwood_hanging_sign");
|
||||
private static final ResourceLocation OURANWOOD_WALL_HANGING_SIGN_ID = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "ouranwood_wall_hanging_sign");
|
||||
|
||||
@Inject(method = "validateBlockState", at = @At("HEAD"), cancellable = true)
|
||||
private static void antarchy$skipOuranwoodSignValidation(BlockState state, CallbackInfo ci) {
|
||||
if (isOuranwoodSign(state)) {
|
||||
ci.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isOuranwoodSign(BlockState state) {
|
||||
return matches(state, OURANWOOD_SIGN_ID)
|
||||
|| matches(state, OURANWOOD_WALL_SIGN_ID)
|
||||
|| matches(state, OURANWOOD_HANGING_SIGN_ID)
|
||||
|| matches(state, OURANWOOD_WALL_HANGING_SIGN_ID);
|
||||
}
|
||||
|
||||
private static boolean matches(BlockState state, ResourceLocation id) {
|
||||
return BuiltInRegistries.BLOCK.getOptional(id).map(state::is).orElse(false);
|
||||
}
|
||||
}
|
||||
@@ -1,72 +1,31 @@
|
||||
package com.craisinlord.antarchy.mixins;
|
||||
|
||||
import com.craisinlord.antarchy.content.item.SquidzookaItem;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.enchantment.Enchantment;
|
||||
import com.craisinlord.antarchy.content.AntarchyTags;
|
||||
import net.minecraft.core.HolderGetter;
|
||||
import net.minecraft.core.HolderSet;
|
||||
import net.minecraft.tags.ItemTags;
|
||||
import net.minecraft.tags.TagKey;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.enchantment.Enchantments;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
|
||||
@Mixin(Enchantment.class)
|
||||
@Mixin(Enchantments.class)
|
||||
public abstract class SquidzookaEnchantMixin {
|
||||
private static final Registry<Enchantment> ENCHANTMENT_REGISTRY = resolveEnchantmentRegistry();
|
||||
|
||||
@Inject(method = "canEnchant", at = @At("HEAD"), cancellable = true)
|
||||
private void antarchy$limitSquidzookaCrossbowEnchants(ItemStack stack, CallbackInfoReturnable<Boolean> cir) {
|
||||
this.antarchy$applySquidzookaEnchantRules(stack, cir);
|
||||
@Redirect(
|
||||
method = "bootstrap",
|
||||
at = @At(value = "INVOKE", target = "Lnet/minecraft/core/HolderGetter;getOrThrow(Lnet/minecraft/tags/TagKey;)Lnet/minecraft/core/HolderSet$Named;"),
|
||||
slice = @org.spongepowered.asm.mixin.injection.Slice(
|
||||
from = @At(value = "FIELD", target = "Lnet/minecraft/world/item/enchantment/Enchantments;MULTISHOT:Lnet/minecraft/resources/ResourceKey;"),
|
||||
to = @At(value = "FIELD", target = "Lnet/minecraft/world/item/enchantment/Enchantments;QUICK_CHARGE:Lnet/minecraft/resources/ResourceKey;")
|
||||
)
|
||||
)
|
||||
private static HolderSet.Named<Item> antarchy$allowSquidzookaForMultishot(HolderGetter<Item> holderGetter, TagKey<Item> tag) {
|
||||
if (tag.equals(ItemTags.CROSSBOW_ENCHANTABLE)) {
|
||||
return holderGetter.getOrThrow(AntarchyTags.Items.MULTISHOT_ENCHANTABLE);
|
||||
}
|
||||
|
||||
@Inject(method = "isPrimaryItem", at = @At("HEAD"), cancellable = true)
|
||||
private void antarchy$limitSquidzookaPrimaryItem(ItemStack stack, CallbackInfoReturnable<Boolean> cir) {
|
||||
this.antarchy$applySquidzookaEnchantRules(stack, cir);
|
||||
}
|
||||
|
||||
@Inject(method = "isSupportedItem", at = @At("HEAD"), cancellable = true)
|
||||
private void antarchy$limitSquidzookaSupportedItem(ItemStack stack, CallbackInfoReturnable<Boolean> cir) {
|
||||
this.antarchy$applySquidzookaEnchantRules(stack, cir);
|
||||
}
|
||||
|
||||
private void antarchy$applySquidzookaEnchantRules(ItemStack stack, CallbackInfoReturnable<Boolean> cir) {
|
||||
if (!(stack.getItem() instanceof SquidzookaItem)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ENCHANTMENT_REGISTRY == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Enchantment enchantment = (Enchantment) (Object) this;
|
||||
ResourceLocation enchantmentId = ENCHANTMENT_REGISTRY.getKey(enchantment);
|
||||
if (enchantmentId == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (enchantmentId.equals(Enchantments.MULTISHOT.location())) {
|
||||
cir.setReturnValue(true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (enchantmentId.equals(Enchantments.QUICK_CHARGE.location())
|
||||
|| enchantmentId.equals(Enchantments.PIERCING.location())) {
|
||||
cir.setReturnValue(false);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static Registry<Enchantment> resolveEnchantmentRegistry() {
|
||||
try {
|
||||
return (Registry<Enchantment>) BuiltInRegistries.class.getField("ENCHANTMENT").get(null);
|
||||
} catch (ReflectiveOperationException primary) {
|
||||
try {
|
||||
return (Registry<Enchantment>) BuiltInRegistries.class.getField("ENCHANTMENTS").get(null);
|
||||
} catch (ReflectiveOperationException ignored) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return holderGetter.getOrThrow(tag);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package com.craisinlord.antarchy.mixins;
|
||||
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.enchantment.Enchantment;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
@Mixin(Enchantment.class)
|
||||
public abstract class SquidzookaMultishotMixin {
|
||||
@Inject(method = "canEnchant", at = @At("HEAD"), cancellable = true)
|
||||
private void antarchy$allowSquidzookaForMultishot(ItemStack stack, CallbackInfoReturnable<Boolean> cir) {
|
||||
if (antarchy$isMultishot() && antarchy$isSquidzooka(stack)) {
|
||||
cir.setReturnValue(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "isPrimaryItem", at = @At("HEAD"), cancellable = true)
|
||||
private void antarchy$allowSquidzookaAsPrimaryItem(ItemStack stack, CallbackInfoReturnable<Boolean> cir) {
|
||||
if (antarchy$isMultishot() && antarchy$isSquidzooka(stack)) {
|
||||
cir.setReturnValue(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Unique
|
||||
private boolean antarchy$isMultishot() {
|
||||
return "Multishot".equals(((Enchantment) (Object) this).description().getString());
|
||||
}
|
||||
|
||||
@Unique
|
||||
private boolean antarchy$isSquidzooka(ItemStack stack) {
|
||||
return "squidzooka".equals(BuiltInRegistries.ITEM.getKey(stack.getItem()).getPath());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package com.craisinlord.antarchy.mixins.client;
|
||||
|
||||
import com.craisinlord.antarchy.content.client.renderer.CavarynSkyRenderer;
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import com.mojang.blaze3d.vertex.VertexBuffer;
|
||||
import net.minecraft.client.Camera;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.multiplayer.ClientLevel;
|
||||
import net.minecraft.client.renderer.LevelRenderer;
|
||||
import org.joml.Matrix4f;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(LevelRenderer.class)
|
||||
public abstract class CavarynSkyMixin {
|
||||
@Shadow
|
||||
@Final
|
||||
private Minecraft minecraft;
|
||||
|
||||
@Shadow
|
||||
private ClientLevel level;
|
||||
|
||||
@Shadow
|
||||
@Final
|
||||
private VertexBuffer skyBuffer;
|
||||
|
||||
@Shadow
|
||||
@Final
|
||||
private VertexBuffer darkBuffer;
|
||||
|
||||
@Inject(method = "renderSky", at = @At("HEAD"), cancellable = true)
|
||||
private void antarchy$renderCavarynSky(Matrix4f modelViewMatrix, Matrix4f projectionMatrix, float partialTick, Camera camera, boolean isFoggy, Runnable setupFog, CallbackInfo ci) {
|
||||
if (this.level == null || !CavarynSkyRenderer.shouldRender(this.level)) {
|
||||
return;
|
||||
}
|
||||
|
||||
PoseStack poseStack = new PoseStack();
|
||||
poseStack.mulPose(modelViewMatrix);
|
||||
CavarynSkyRenderer.render(
|
||||
this.minecraft,
|
||||
this.level,
|
||||
poseStack,
|
||||
projectionMatrix,
|
||||
partialTick,
|
||||
camera,
|
||||
isFoggy,
|
||||
setupFog,
|
||||
this.skyBuffer,
|
||||
this.darkBuffer
|
||||
);
|
||||
ci.cancel();
|
||||
}
|
||||
|
||||
@Inject(method = "renderClouds", at = @At("HEAD"), cancellable = true)
|
||||
private void antarchy$skipCavarynClouds(PoseStack poseStack, Matrix4f modelViewMatrix, Matrix4f projectionMatrix, float partialTick, double camX, double camY, double camZ, CallbackInfo ci) {
|
||||
if (this.level != null && CavarynSkyRenderer.shouldRender(this.level)) {
|
||||
ci.cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
package com.craisinlord.antarchy.mixins.stellaris;
|
||||
|
||||
import com.st0x0ef.stellaris.common.oxygen.DimensionOxygenManager;
|
||||
import net.minecraft.core.component.DataComponentType;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.tags.TagKey;
|
||||
import net.minecraft.world.entity.EquipmentSlot;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
@Mixin(value = DimensionOxygenManager.class, priority = 2000)
|
||||
/*
|
||||
* Tweaks Stellaris oxygen checks for Antarchy dimensions.
|
||||
*/
|
||||
public abstract class DimensionOxygenManagerMixin {
|
||||
@Unique
|
||||
private static final TagKey<Item> ANTARCHY_MARK_39_ARMOR = TagKey.create(
|
||||
Registries.ITEM,
|
||||
ResourceLocation.fromNamespaceAndPath("marvel", "iron_man_mark_39_armor")
|
||||
);
|
||||
|
||||
@Unique
|
||||
private static final ResourceLocation ANTARCHY_HELMET_OPEN_ID =
|
||||
ResourceLocation.fromNamespaceAndPath("marvel", "helmet_open");
|
||||
|
||||
@Inject(method = "breath", at = @At("HEAD"), cancellable = true)
|
||||
private void antarchy$allowClosedMark39SuitToBreathe(
|
||||
LivingEntity entity,
|
||||
CallbackInfoReturnable<Boolean> cir
|
||||
) {
|
||||
if (antarchy$hasArmor(entity)) {
|
||||
cir.setReturnValue(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Unique
|
||||
private static boolean antarchy$hasArmor(LivingEntity entity) {
|
||||
ItemStack helmet = entity.getItemBySlot(EquipmentSlot.HEAD);
|
||||
boolean hasHelmet = helmet.is(ANTARCHY_MARK_39_ARMOR) && !antarchy$isHelmetOpen(helmet);
|
||||
boolean hasChestplate = entity.getItemBySlot(EquipmentSlot.CHEST).is(ANTARCHY_MARK_39_ARMOR);
|
||||
boolean hasLeggings = entity.getItemBySlot(EquipmentSlot.LEGS).is(ANTARCHY_MARK_39_ARMOR);
|
||||
boolean hasBoots = entity.getItemBySlot(EquipmentSlot.FEET).is(ANTARCHY_MARK_39_ARMOR);
|
||||
return hasHelmet && hasChestplate && hasLeggings && hasBoots;
|
||||
}
|
||||
|
||||
@Unique
|
||||
@SuppressWarnings("unchecked")
|
||||
private static boolean antarchy$isHelmetOpen(ItemStack helmet) {
|
||||
DataComponentType<?> componentType = BuiltInRegistries.DATA_COMPONENT_TYPE.get(ANTARCHY_HELMET_OPEN_ID);
|
||||
if (componentType == null) {
|
||||
return false;
|
||||
}
|
||||
return Boolean.TRUE.equals(helmet.get((DataComponentType<Boolean>) componentType));
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,12 @@
|
||||
{
|
||||
"required": true,
|
||||
"package": "com.craisinlord.antarchy.mixins",
|
||||
"refmap": "antarchy.refmap.json",
|
||||
"plugin": "com.craisinlord.antarchy.mixins.AntarchyMixinPlugin",
|
||||
"compatibilityLevel": "JAVA_21",
|
||||
"mixins": [
|
||||
"AccessorRangedAttribute",
|
||||
"BlockEntitySignMixin",
|
||||
"BloodglassMixin",
|
||||
"DreamFireBlockMixin",
|
||||
"DreamSandLowGravityMixin",
|
||||
@@ -13,7 +15,7 @@
|
||||
"GlowVinesUnderLeavesMixin",
|
||||
"ServerPlayerGameModeMixin",
|
||||
"SquidzookaEnchantMixin",
|
||||
"StinkyLivingEntityMixin",
|
||||
"SquidzookaMultishotMixin",
|
||||
"TorchflowerBlockStateMixin",
|
||||
"gravity.AbstractArrowGravityMixin",
|
||||
"gravity.BucketItemGravityMixin",
|
||||
@@ -43,10 +45,10 @@
|
||||
"gravity.ServerPlayerGravityMixin",
|
||||
"gravity.ThrowableProjectileGravityMixin",
|
||||
"gravity.TrackedEntityAccessor",
|
||||
"multipart.EntityMultipartLifecycleMixin",
|
||||
"stellaris.DimensionOxygenManagerMixin"
|
||||
"multipart.EntityMultipartLifecycleMixin"
|
||||
],
|
||||
"client": [
|
||||
"client.CavarynSkyMixin",
|
||||
"client.ElythiaSkyMixin",
|
||||
"client.ElytraModelAccessor",
|
||||
"client.LightningBoltRendererMixin",
|
||||
|
||||
@@ -5524,6 +5524,22 @@
|
||||
"lerp_mode": "catmullrom"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Kraken": {
|
||||
"position": {
|
||||
"0.0": {
|
||||
"post": {
|
||||
"vector": [0, 218.48, 0]
|
||||
},
|
||||
"lerp_mode": "catmullrom"
|
||||
},
|
||||
"3.0": {
|
||||
"post": {
|
||||
"vector": [0, 218.48, 0]
|
||||
},
|
||||
"lerp_mode": "catmullrom"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"variants": {
|
||||
"level=0": { "model": "minecraft:block/water" },
|
||||
"level=1": { "model": "minecraft:block/water" },
|
||||
"level=2": { "model": "minecraft:block/water" },
|
||||
"level=3": { "model": "minecraft:block/water" },
|
||||
"level=4": { "model": "minecraft:block/water" },
|
||||
"level=5": { "model": "minecraft:block/water" },
|
||||
"level=6": { "model": "minecraft:block/water" },
|
||||
"level=7": { "model": "minecraft:block/water" },
|
||||
"level=8": { "model": "minecraft:block/water" },
|
||||
"level=9": { "model": "minecraft:block/water" },
|
||||
"level=10": { "model": "minecraft:block/water" },
|
||||
"level=11": { "model": "minecraft:block/water" },
|
||||
"level=12": { "model": "minecraft:block/water" },
|
||||
"level=13": { "model": "minecraft:block/water" },
|
||||
"level=14": { "model": "minecraft:block/water" },
|
||||
"level=15": { "model": "minecraft:block/water" }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"variants": {
|
||||
"thickness=base,vertical_direction=down": { "model": "antarchy:block/chiten_spike_down_base" },
|
||||
"thickness=base,vertical_direction=up": { "model": "antarchy:block/chiten_spike_up_base" },
|
||||
"thickness=frustum,vertical_direction=down": { "model": "antarchy:block/chiten_spike_down_frustum" },
|
||||
"thickness=frustum,vertical_direction=up": { "model": "antarchy:block/chiten_spike_up_frustum" },
|
||||
"thickness=middle,vertical_direction=down": { "model": "antarchy:block/chiten_spike_down_middle" },
|
||||
"thickness=middle,vertical_direction=up": { "model": "antarchy:block/chiten_spike_up_middle" },
|
||||
"thickness=tip,vertical_direction=down": { "model": "antarchy:block/chiten_spike_down_tip" },
|
||||
"thickness=tip,vertical_direction=up": { "model": "antarchy:block/chiten_spike_up_tip" },
|
||||
"thickness=tip_merge,vertical_direction=down": { "model": "antarchy:block/chiten_spike_down_tip_merge" },
|
||||
"thickness=tip_merge,vertical_direction=up": { "model": "antarchy:block/chiten_spike_up_tip_merge" }
|
||||
}
|
||||
}
|
||||
@@ -38,6 +38,7 @@
|
||||
"block.antarchy.chiseled_nyxite": "Chiseled Nyxite",
|
||||
"block.antarchy.nyxite_bricks": "Nyxite Bricks",
|
||||
"block.antarchy.chiten_block": "Chiten Block",
|
||||
"block.antarchy.chiten_spike": "Chiten Spike",
|
||||
"block.antarchy.shellstone": "Shellstone",
|
||||
"block.antarchy.polished_shellstone": "Polished Shellstone",
|
||||
"block.antarchy.shellstone_bricks": "Shellstone Bricks",
|
||||
@@ -130,6 +131,7 @@
|
||||
"block.antarchy.uranium_bars": "Uranium Bars",
|
||||
"block.antarchy.titanium_bars": "Titanium Bars",
|
||||
"tooltip.antarchy.signal_saving_bulb": "Toggles on/off. Has light level equal to the signal strength stored on it.",
|
||||
"block.antarchy.bile": "Bile",
|
||||
"block.antarchy.ichor": "Ichor",
|
||||
"block.antarchy.antiwater": "Antiwater",
|
||||
"entity.antarchy.apple_cow": "Apple Cow",
|
||||
@@ -273,6 +275,7 @@
|
||||
"item.antarchy.creepvine": "Creepvine",
|
||||
"item.antarchy.chiten": "Chiten",
|
||||
"item.antarchy.chiten_block": "Chiten Block",
|
||||
"item.antarchy.chiten_spike": "Chiten Spike",
|
||||
"item.antarchy.myrmite": "Myrmite",
|
||||
"item.antarchy.torchflower_bush": "Torchflower Bush",
|
||||
"block.antarchy.torchflower_bush": "Torchflower Bush",
|
||||
@@ -339,6 +342,7 @@
|
||||
"item.antarchy.ultimate_mace": "Ultimate Mace",
|
||||
"item.antarchy.moggles": "Moggles",
|
||||
"itemGroup.antarchy.antarchy": "Antarchy",
|
||||
"item.antarchy.bile_bucket": "Bile Bucket",
|
||||
"item.antarchy.ichor_bucket": "Ichor Bucket",
|
||||
"item.antarchy.antiwater_bucket": "Antiwater Bucket",
|
||||
"item.antarchy.reverie_bottle": "Bottle of Reverie",
|
||||
@@ -393,6 +397,7 @@
|
||||
"biome.antarchy.butterfly_fields": "Butterfly Fields",
|
||||
"biome.antarchy.torchflower_fields": "Torchflower Fields",
|
||||
"biome.antarchy.ouranwood_forest": "Ouranwood Forest",
|
||||
"biome.antarchy.fungal_ouranwood_forest": "Fungal Ouranwood Forest",
|
||||
"message.antarchy.brown_ant_activated": "The brown ant is ready to open its destination.",
|
||||
"message.antarchy.brown_ant_needs_reagent": "This brown ant needs a reagent before it can open its destination.",
|
||||
"message.antarchy.rainbow_ant_activated": "The rainbow ant hums with a new Infinity destination.",
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"parent": "minecraft:block/pointed_dripstone",
|
||||
"textures": {
|
||||
"cross": "antarchy:block/chiten_spike/chiten_spike_down_base"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"parent": "minecraft:block/pointed_dripstone",
|
||||
"textures": {
|
||||
"cross": "antarchy:block/chiten_spike/chiten_spike_down_frustum"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"parent": "minecraft:block/pointed_dripstone",
|
||||
"textures": {
|
||||
"cross": "antarchy:block/chiten_spike/chiten_spike_down_middle"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"parent": "minecraft:block/pointed_dripstone",
|
||||
"textures": {
|
||||
"cross": "antarchy:block/chiten_spike/chiten_spike_down_tip"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"parent": "minecraft:block/pointed_dripstone",
|
||||
"textures": {
|
||||
"cross": "antarchy:block/chiten_spike/chiten_spike_down_tip_merge"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"parent": "minecraft:block/pointed_dripstone",
|
||||
"textures": {
|
||||
"cross": "antarchy:block/chiten_spike/chiten_spike_up_base"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"parent": "minecraft:block/pointed_dripstone",
|
||||
"textures": {
|
||||
"cross": "antarchy:block/chiten_spike/chiten_spike_up_frustum"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"parent": "minecraft:block/pointed_dripstone",
|
||||
"textures": {
|
||||
"cross": "antarchy:block/chiten_spike/chiten_spike_up_middle"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"parent": "minecraft:block/pointed_dripstone",
|
||||
"textures": {
|
||||
"cross": "antarchy:block/chiten_spike/chiten_spike_up_tip"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"parent": "minecraft:block/pointed_dripstone",
|
||||
"textures": {
|
||||
"cross": "antarchy:block/chiten_spike/chiten_spike_up_tip_merge"
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"parent": "minecraft:block/template_turtle_egg",
|
||||
"textures": {
|
||||
"all": "antarchy:block/creeping_horror_eggs/creeping_horror_eggs"
|
||||
"all": "antarchy:block/creeping_horror_egg/creeping_horror_egg"
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"parent": "minecraft:block/template_two_turtle_eggs",
|
||||
"textures": {
|
||||
"all": "antarchy:block/creeping_horror_eggs/creeping_horror_eggs"
|
||||
"all": "antarchy:block/creeping_horror_egg/creeping_horror_egg"
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"parent": "minecraft:block/template_three_turtle_eggs",
|
||||
"textures": {
|
||||
"all": "antarchy:block/creeping_horror_eggs/creeping_horror_eggs"
|
||||
"all": "antarchy:block/creeping_horror_egg/creeping_horror_egg"
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"parent": "minecraft:block/template_four_turtle_eggs",
|
||||
"textures": {
|
||||
"all": "antarchy:block/creeping_horror_eggs/creeping_horror_eggs"
|
||||
"all": "antarchy:block/creeping_horror_egg/creeping_horror_egg"
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"parent": "minecraft:block/template_turtle_egg",
|
||||
"textures": {
|
||||
"all": "antarchy:block/creeping_horror_eggs/creeping_horror_eggs_slightly_cracked"
|
||||
"all": "antarchy:block/creeping_horror_egg/creeping_horror_egg_slightly_cracked"
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"parent": "minecraft:block/template_two_turtle_eggs",
|
||||
"textures": {
|
||||
"all": "antarchy:block/creeping_horror_eggs/creeping_horror_eggs_slightly_cracked"
|
||||
"all": "antarchy:block/creeping_horror_egg/creeping_horror_egg_slightly_cracked"
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"parent": "minecraft:block/template_three_turtle_eggs",
|
||||
"textures": {
|
||||
"all": "antarchy:block/creeping_horror_eggs/creeping_horror_eggs_slightly_cracked"
|
||||
"all": "antarchy:block/creeping_horror_egg/creeping_horror_egg_slightly_cracked"
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"parent": "minecraft:block/template_four_turtle_eggs",
|
||||
"textures": {
|
||||
"all": "antarchy:block/creeping_horror_eggs/creeping_horror_eggs_slightly_cracked"
|
||||
"all": "antarchy:block/creeping_horror_egg/creeping_horror_egg_slightly_cracked"
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"parent": "minecraft:block/template_turtle_egg",
|
||||
"textures": {
|
||||
"all": "antarchy:block/creeping_horror_eggs/creeping_horror_eggs_very_cracked"
|
||||
"all": "antarchy:block/creeping_horror_egg/creeping_horror_egg_very_cracked"
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"parent": "minecraft:block/template_two_turtle_eggs",
|
||||
"textures": {
|
||||
"all": "antarchy:block/creeping_horror_eggs/creeping_horror_eggs_very_cracked"
|
||||
"all": "antarchy:block/creeping_horror_egg/creeping_horror_egg_very_cracked"
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"parent": "minecraft:block/template_three_turtle_eggs",
|
||||
"textures": {
|
||||
"all": "antarchy:block/creeping_horror_eggs/creeping_horror_eggs_very_cracked"
|
||||
"all": "antarchy:block/creeping_horror_egg/creeping_horror_egg_very_cracked"
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"parent": "minecraft:block/template_four_turtle_eggs",
|
||||
"textures": {
|
||||
"all": "antarchy:block/creeping_horror_eggs/creeping_horror_eggs_very_cracked"
|
||||
"all": "antarchy:block/creeping_horror_egg/creeping_horror_egg_very_cracked"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"parent": "minecraft:item/generated",
|
||||
"textures": {
|
||||
"layer0": "antarchy:item/chiten_spike"
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"parent": "minecraft:item/generated",
|
||||
"textures": {
|
||||
"layer0": "antarchy:block/uranium/uranium_bars"
|
||||
"layer0": "antarchy:item/uranium/uranium_bars"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"textures": [
|
||||
"minecraft:generic_0"
|
||||
]
|
||||
}
|
||||
|
After Width: | Height: | Size: 9.3 KiB |
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"animation": {
|
||||
"frametime": 3,
|
||||
"interpolate": true
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 6.5 KiB |
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"animation": {
|
||||
"frametime": 4,
|
||||
"interpolate": true
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 441 B |
|
After Width: | Height: | Size: 408 B |
|
After Width: | Height: | Size: 419 B |
|
After Width: | Height: | Size: 276 B |
|
After Width: | Height: | Size: 299 B |
|
After Width: | Height: | Size: 420 B |
|
After Width: | Height: | Size: 393 B |
|
After Width: | Height: | Size: 424 B |
|
After Width: | Height: | Size: 241 B |
|
After Width: | Height: | Size: 279 B |
|
After Width: | Height: | Size: 241 B |
|
After Width: | Height: | Size: 395 B |
|
After Width: | Height: | Size: 162 B |
@@ -7,7 +7,7 @@
|
||||
"type": "antarchy:elythia_biome_source",
|
||||
"moleworm_caves_max_y": 30,
|
||||
"surface_biome_sample_y": 160,
|
||||
"ocean_max_y": 85,
|
||||
"ocean_max_y": 75,
|
||||
"biomes": [
|
||||
{
|
||||
"biome": "antarchy:moleworm_caves",
|
||||
@@ -225,6 +225,30 @@
|
||||
"offset": 0.0
|
||||
}
|
||||
},
|
||||
{
|
||||
"biome": "antarchy:fungal_ouranwood_forest",
|
||||
"parameters": {
|
||||
"temperature": 0.55,
|
||||
"humidity": 0.9,
|
||||
"continentalness": 0.75,
|
||||
"erosion": 0.0,
|
||||
"weirdness": 0.85,
|
||||
"depth": 0.45,
|
||||
"offset": 0.0
|
||||
}
|
||||
},
|
||||
{
|
||||
"biome": "antarchy:fungal_ouranwood_forest",
|
||||
"parameters": {
|
||||
"temperature": 0.55,
|
||||
"humidity": 0.9,
|
||||
"continentalness": 0.75,
|
||||
"erosion": 0.0,
|
||||
"weirdness": 0.85,
|
||||
"depth": 0.0,
|
||||
"offset": 0.0
|
||||
}
|
||||
},
|
||||
{
|
||||
"biome": "antarchy:torchflower_fields",
|
||||
"parameters": {
|
||||
@@ -398,7 +422,7 @@
|
||||
"parameters": {
|
||||
"temperature": 0.65,
|
||||
"humidity": 0.90,
|
||||
"continentalness": -0.50,
|
||||
"continentalness": -0.88,
|
||||
"erosion": 0.30,
|
||||
"weirdness": 0.0,
|
||||
"depth": 0.0,
|
||||
@@ -410,7 +434,7 @@
|
||||
"parameters": {
|
||||
"temperature": 0.60,
|
||||
"humidity": 0.88,
|
||||
"continentalness": -0.55,
|
||||
"continentalness": -0.92,
|
||||
"erosion": 0.25,
|
||||
"weirdness": -0.40,
|
||||
"depth": 0.0,
|
||||
@@ -422,7 +446,7 @@
|
||||
"parameters": {
|
||||
"temperature": 0.70,
|
||||
"humidity": 0.92,
|
||||
"continentalness": -0.45,
|
||||
"continentalness": -0.85,
|
||||
"erosion": 0.35,
|
||||
"weirdness": 0.40,
|
||||
"depth": 0.0,
|
||||
@@ -434,7 +458,7 @@
|
||||
"parameters": {
|
||||
"temperature": 0.65,
|
||||
"humidity": 0.90,
|
||||
"continentalness": -0.50,
|
||||
"continentalness": -0.88,
|
||||
"erosion": 0.30,
|
||||
"weirdness": 0.0,
|
||||
"depth": 0.5,
|
||||
@@ -446,7 +470,7 @@
|
||||
"parameters": {
|
||||
"temperature": 0.60,
|
||||
"humidity": 0.88,
|
||||
"continentalness": -0.55,
|
||||
"continentalness": -0.92,
|
||||
"erosion": 0.25,
|
||||
"weirdness": -0.40,
|
||||
"depth": 0.5,
|
||||
@@ -454,38 +478,50 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"biome": "antarchy:elythia_beach",
|
||||
"biome": "antarchy:elythia_coral_spikes",
|
||||
"parameters": {
|
||||
"temperature": 0.68,
|
||||
"humidity": 0.91,
|
||||
"continentalness": -0.08,
|
||||
"erosion": 0.50,
|
||||
"weirdness": 0.0,
|
||||
"temperature": 0.80,
|
||||
"humidity": 0.85,
|
||||
"continentalness": -0.88,
|
||||
"erosion": 0.20,
|
||||
"weirdness": 0.60,
|
||||
"depth": 0.0,
|
||||
"offset": 0.0
|
||||
}
|
||||
},
|
||||
{
|
||||
"biome": "antarchy:elythia_beach",
|
||||
"biome": "antarchy:elythia_coral_spikes",
|
||||
"parameters": {
|
||||
"temperature": 0.65,
|
||||
"humidity": 0.89,
|
||||
"continentalness": -0.05,
|
||||
"erosion": 0.60,
|
||||
"weirdness": -0.30,
|
||||
"temperature": 0.75,
|
||||
"humidity": 0.82,
|
||||
"continentalness": -0.90,
|
||||
"erosion": 0.15,
|
||||
"weirdness": -0.65,
|
||||
"depth": 0.0,
|
||||
"offset": 0.0
|
||||
}
|
||||
},
|
||||
{
|
||||
"biome": "antarchy:elythia_beach",
|
||||
"biome": "antarchy:elythia_coral_spikes",
|
||||
"parameters": {
|
||||
"temperature": 0.70,
|
||||
"humidity": 0.93,
|
||||
"continentalness": -0.02,
|
||||
"erosion": 0.55,
|
||||
"weirdness": 0.30,
|
||||
"depth": 0.0,
|
||||
"temperature": 0.80,
|
||||
"humidity": 0.85,
|
||||
"continentalness": -0.88,
|
||||
"erosion": 0.20,
|
||||
"weirdness": 0.60,
|
||||
"depth": 0.5,
|
||||
"offset": 0.0
|
||||
}
|
||||
},
|
||||
{
|
||||
"biome": "antarchy:elythia_coral_spikes",
|
||||
"parameters": {
|
||||
"temperature": 0.75,
|
||||
"humidity": 0.82,
|
||||
"continentalness": -0.90,
|
||||
"erosion": 0.15,
|
||||
"weirdness": -0.65,
|
||||
"depth": 0.5,
|
||||
"offset": 0.0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
{
|
||||
"ultrawarm": false,
|
||||
"ultrawarm": true,
|
||||
"natural": false,
|
||||
"coordinate_scale": 1.0,
|
||||
"coordinate_scale": 64.0,
|
||||
"bed_works": false,
|
||||
"respawn_anchor_works": false,
|
||||
"min_y": 0,
|
||||
"height": 256,
|
||||
"logical_height": 240,
|
||||
"height": 208,
|
||||
"logical_height": 192,
|
||||
"infiniburn": "#minecraft:infiniburn_overworld",
|
||||
"effects": "minecraft:the_end",
|
||||
"effects": "minecraft:overworld",
|
||||
"ambient_light": 0.0,
|
||||
"piglin_safe": false,
|
||||
"has_raids": false,
|
||||
"monster_spawn_light_level": 7,
|
||||
"monster_spawn_light_level": 0,
|
||||
"monster_spawn_block_light_limit": 0,
|
||||
"has_skylight": false,
|
||||
"has_ceiling": true,
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"type": "minecraft:block",
|
||||
"pools": [
|
||||
{
|
||||
"rolls": 1,
|
||||
"entries": [
|
||||
{
|
||||
"type": "minecraft:item",
|
||||
"name": "antarchy:chiten_spike"
|
||||
}
|
||||
],
|
||||
"conditions": [
|
||||
{
|
||||
"condition": "minecraft:survives_explosion"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"type": "minecraft:entity",
|
||||
"pools": [
|
||||
{
|
||||
"rolls": 1,
|
||||
"entries": [
|
||||
{
|
||||
"type": "minecraft:item",
|
||||
"name": "antarchy:chiten",
|
||||
"functions": [
|
||||
{
|
||||
"function": "minecraft:set_count",
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"min": 1.0,
|
||||
"max": 2.0
|
||||
}
|
||||
},
|
||||
{
|
||||
"function": "minecraft:enchanted_count_increase",
|
||||
"enchantment": "minecraft:looting",
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"min": 0.0,
|
||||
"max": 1.0
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"conditions": [
|
||||
{
|
||||
"condition": "minecraft:killed_by_player"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"random_sequence": "antarchy:entities/creeping_horror"
|
||||
}
|
||||
@@ -28,6 +28,39 @@
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"rolls": 1,
|
||||
"entries": [
|
||||
{
|
||||
"type": "minecraft:item",
|
||||
"name": "antarchy:chiten",
|
||||
"functions": [
|
||||
{
|
||||
"function": "minecraft:set_count",
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"min": 1.0,
|
||||
"max": 2.0
|
||||
}
|
||||
},
|
||||
{
|
||||
"function": "minecraft:enchanted_count_increase",
|
||||
"enchantment": "minecraft:looting",
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"min": 0.0,
|
||||
"max": 1.0
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"conditions": [
|
||||
{
|
||||
"condition": "minecraft:killed_by_player"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"type": "minecraft:entity",
|
||||
"pools": [
|
||||
{
|
||||
"rolls": 1,
|
||||
"entries": [
|
||||
{
|
||||
"type": "minecraft:item",
|
||||
"name": "antarchy:chiten",
|
||||
"functions": [
|
||||
{
|
||||
"function": "minecraft:set_count",
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"min": 1.0,
|
||||
"max": 2.0
|
||||
}
|
||||
},
|
||||
{
|
||||
"function": "minecraft:enchanted_count_increase",
|
||||
"enchantment": "minecraft:looting",
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"min": 0.0,
|
||||
"max": 1.0
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"conditions": [
|
||||
{
|
||||
"condition": "minecraft:killed_by_player"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"random_sequence": "antarchy:entities/lurking_terror"
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"type": "minecraft:entity",
|
||||
"pools": [
|
||||
{
|
||||
"rolls": {
|
||||
"type": "minecraft:uniform",
|
||||
"min": 5,
|
||||
"max": 10
|
||||
},
|
||||
"entries": [
|
||||
{
|
||||
"type": "minecraft:item",
|
||||
"name": "minecraft:ink_sac"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -17,6 +17,7 @@
|
||||
},
|
||||
{
|
||||
"function": "minecraft:enchanted_count_increase",
|
||||
"enchantment": "minecraft:looting",
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"min": 0.0,
|
||||
@@ -51,7 +52,13 @@
|
||||
"name": "antarchy:water_cannon"
|
||||
}
|
||||
],
|
||||
"conditions": [
|
||||
{
|
||||
"condition": "minecraft:killed_by_player"
|
||||
}
|
||||
],
|
||||
"rolls": 1.0
|
||||
}
|
||||
]
|
||||
],
|
||||
"random_sequence": "antarchy:entities/toreterror"
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"type": "minecraft:crafting_shapeless",
|
||||
"category": "misc",
|
||||
"ingredients": [
|
||||
{
|
||||
"item": "antarchy:chiten"
|
||||
}
|
||||
],
|
||||
"result": {
|
||||
"count": 1,
|
||||
"id": "minecraft:bone_meal"
|
||||
}
|
||||
}
|
||||
@@ -7,8 +7,9 @@
|
||||
}
|
||||
},
|
||||
"pattern": [
|
||||
"##",
|
||||
"##"
|
||||
"###",
|
||||
"###",
|
||||
"###"
|
||||
],
|
||||
"result": {
|
||||
"count": 1,
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"values": [
|
||||
"#minecraft:moss_replaceable",
|
||||
"antarchy:myrmite",
|
||||
"antarchy:amber_moss_block"
|
||||
]
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"values": [
|
||||
"#minecraft:moss_replaceable",
|
||||
"antarchy:nyxite",
|
||||
"minecraft:blackstone",
|
||||
"antarchy:umbral_moss_block"
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"replace": false,
|
||||
"values": [
|
||||
"#minecraft:enchantable/crossbow",
|
||||
"antarchy:squidzooka"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"replace": false,
|
||||
"values": [
|
||||
"antarchy:elythia_meadow",
|
||||
"antarchy:torchflower_fields",
|
||||
"antarchy:butterfly_fields",
|
||||
"antarchy:ouranwood_forest",
|
||||
"antarchy:sparse_ouranwood_forest"
|
||||
]
|
||||
}
|
||||
@@ -1,23 +1,6 @@
|
||||
{
|
||||
"replace": false,
|
||||
"values": [
|
||||
"#minecraft:is_ocean",
|
||||
"#minecraft:is_deep_ocean",
|
||||
{
|
||||
"id": "#neoforge:is_ocean",
|
||||
"required": false
|
||||
},
|
||||
{
|
||||
"id": "#c:ocean",
|
||||
"required": false
|
||||
},
|
||||
{
|
||||
"id": "#c:shallow_ocean",
|
||||
"required": false
|
||||
},
|
||||
{
|
||||
"id": "#c:deep_ocean",
|
||||
"required": false
|
||||
}
|
||||
"#antarchy:is_elythia_ocean"
|
||||
]
|
||||
}
|
||||
@@ -10,6 +10,7 @@
|
||||
"antarchy:ouranwood_forest",
|
||||
"antarchy:sparse_ouranwood_forest",
|
||||
"antarchy:elythia_ocean",
|
||||
"antarchy:elythia_beach"
|
||||
"antarchy:elythia_coral_spikes",
|
||||
"antarchy:fungal_ouranwood_forest"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"replace": false,
|
||||
"values": [
|
||||
"antarchy:elythia_ocean",
|
||||
"antarchy:elythia_coral_spikes"
|
||||
]
|
||||
}
|
||||
@@ -9,17 +9,16 @@
|
||||
"sky_color": 524288,
|
||||
"grass_color": 4211262,
|
||||
"foliage_color": 4211262,
|
||||
"ambient_sound": "minecraft:ambient.cave",
|
||||
"mood_sound": {
|
||||
"sound": "minecraft:ambient.cave",
|
||||
"tick_delay": 6000,
|
||||
"tick_delay": 1200000,
|
||||
"block_search_extent": 8,
|
||||
"offset": 2.0
|
||||
},
|
||||
"particle": {
|
||||
"probability": 0.002,
|
||||
"options": {
|
||||
"type": "minecraft:ash"
|
||||
"type": "antarchy:orange_ash"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -29,106 +28,39 @@
|
||||
"liquid": []
|
||||
},
|
||||
"features": [
|
||||
[
|
||||
"minecraft:lake_lava_surface"
|
||||
],
|
||||
[],
|
||||
[
|
||||
"minecraft:amethyst_geode"
|
||||
],
|
||||
[
|
||||
"minecraft:monster_room",
|
||||
"minecraft:monster_room_deep"
|
||||
],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[
|
||||
"minecraft:ore_dirt",
|
||||
"minecraft:ore_gravel",
|
||||
"minecraft:ore_granite_upper",
|
||||
"minecraft:ore_granite_lower",
|
||||
"minecraft:ore_diorite_upper",
|
||||
"minecraft:ore_diorite_lower",
|
||||
"minecraft:ore_andesite_upper",
|
||||
"minecraft:ore_andesite_lower",
|
||||
"minecraft:ore_tuff",
|
||||
"minecraft:ore_coal_upper",
|
||||
"minecraft:ore_coal_lower",
|
||||
"minecraft:ore_iron_upper",
|
||||
"minecraft:ore_iron_middle",
|
||||
"minecraft:ore_iron_small",
|
||||
"minecraft:ore_gold",
|
||||
"minecraft:ore_gold_lower",
|
||||
"minecraft:ore_redstone",
|
||||
"minecraft:ore_redstone_lower",
|
||||
"minecraft:ore_diamond",
|
||||
"minecraft:ore_diamond_medium",
|
||||
"minecraft:ore_diamond_large",
|
||||
"minecraft:ore_diamond_buried",
|
||||
"minecraft:ore_lapis",
|
||||
"minecraft:ore_lapis_buried",
|
||||
"minecraft:ore_copper",
|
||||
"minecraft:underwater_magma"
|
||||
],
|
||||
[
|
||||
"minecraft:large_dripstone",
|
||||
"minecraft:pointed_dripstone",
|
||||
"minecraft:glow_lichen"
|
||||
],
|
||||
[
|
||||
"minecraft:spring_water",
|
||||
"minecraft:spring_lava"
|
||||
],
|
||||
[
|
||||
"minecraft:brown_mushroom_normal",
|
||||
"minecraft:red_mushroom_normal"
|
||||
"antarchy:cavaryn_bile_pools",
|
||||
"antarchy:cavaryn_amber_moss_patch",
|
||||
"antarchy:cavaryn_wall_amber_moss",
|
||||
"antarchy:cavaryn_creepvine",
|
||||
"minecraft:glow_lichen",
|
||||
"antarchy:cavaryn_chiten_spikes",
|
||||
"antarchy:cavaryn_terror_egg_patch"
|
||||
],
|
||||
[]
|
||||
],
|
||||
"creature_spawn_probability": 0.08,
|
||||
"creature_spawn_probability": 0.06,
|
||||
"spawners": {
|
||||
"monster": [
|
||||
{
|
||||
"type": "minecraft:zombie",
|
||||
"type": "antarchy:creeping_horror",
|
||||
"weight": 95,
|
||||
"minCount": 2,
|
||||
"maxCount": 4
|
||||
"maxCount": 5
|
||||
},
|
||||
{
|
||||
"type": "minecraft:skeleton",
|
||||
"weight": 90,
|
||||
"type": "antarchy:lurking_terror",
|
||||
"weight": 95,
|
||||
"minCount": 2,
|
||||
"maxCount": 4
|
||||
},
|
||||
{
|
||||
"type": "minecraft:creeper",
|
||||
"weight": 80,
|
||||
"minCount": 1,
|
||||
"maxCount": 2
|
||||
},
|
||||
{
|
||||
"type": "minecraft:spider",
|
||||
"weight": 70,
|
||||
"minCount": 1,
|
||||
"maxCount": 3
|
||||
},
|
||||
{
|
||||
"type": "minecraft:cave_spider",
|
||||
"weight": 45,
|
||||
"minCount": 1,
|
||||
"maxCount": 2
|
||||
},
|
||||
{
|
||||
"type": "minecraft:enderman",
|
||||
"weight": 12,
|
||||
"minCount": 1,
|
||||
"maxCount": 2
|
||||
},
|
||||
{
|
||||
"type": "minecraft:witch",
|
||||
"weight": 6,
|
||||
"minCount": 1,
|
||||
"maxCount": 1
|
||||
"maxCount": 5
|
||||
}
|
||||
],
|
||||
"creature": [],
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
{
|
||||
"has_precipitation": false,
|
||||
"temperature": 0.78,
|
||||
"downfall": 1.0,
|
||||
"effects": {
|
||||
"fog_color": 6245375,
|
||||
"water_color": 3461580,
|
||||
"water_fog_color": 1741620,
|
||||
"sky_color": 8084735,
|
||||
"foliage_color": 2867501,
|
||||
"grass_color": 4304168,
|
||||
"grass_color_modifier": "none",
|
||||
"mood_sound": {
|
||||
"sound": "minecraft:ambient.underwater.loop",
|
||||
"tick_delay": 6000,
|
||||
"block_search_extent": 8,
|
||||
"offset": 2.0
|
||||
}
|
||||
},
|
||||
"spawn_costs": {},
|
||||
"carvers": {
|
||||
"air": [],
|
||||
"liquid": []
|
||||
},
|
||||
"features": [
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[
|
||||
"minecraft:ore_coal_upper",
|
||||
"minecraft:ore_coal_lower",
|
||||
"minecraft:ore_iron_upper",
|
||||
"minecraft:ore_iron_middle",
|
||||
"minecraft:ore_iron_small",
|
||||
"minecraft:ore_gold",
|
||||
"minecraft:ore_gold_lower",
|
||||
"minecraft:ore_redstone",
|
||||
"minecraft:ore_redstone_lower",
|
||||
"minecraft:ore_diamond",
|
||||
"minecraft:ore_diamond_medium",
|
||||
"minecraft:ore_diamond_large",
|
||||
"minecraft:ore_diamond_buried",
|
||||
"minecraft:ore_lapis",
|
||||
"minecraft:ore_lapis_buried",
|
||||
"minecraft:ore_copper"
|
||||
],
|
||||
[
|
||||
"antarchy:elythia_underground"
|
||||
],
|
||||
[],
|
||||
[
|
||||
"antarchy:elythia_coral_spike"
|
||||
],
|
||||
[]
|
||||
],
|
||||
"creature_spawn_probability": 0.04,
|
||||
"spawners": {
|
||||
"monster": [
|
||||
{ "type": "antarchy:missile_squid", "weight": 12, "minCount": 1, "maxCount": 3 },
|
||||
{ "type": "antarchy:octopus_bomb", "weight": 4, "minCount": 1, "maxCount": 2 }
|
||||
],
|
||||
"creature": [
|
||||
{ "type": "minecraft:turtle", "weight": 5, "minCount": 1, "maxCount": 2 }
|
||||
],
|
||||
"ambient": [],
|
||||
"axolotls": [],
|
||||
"underground_water_creature": [],
|
||||
"water_creature": [],
|
||||
"water_ambient": [
|
||||
{ "type": "minecraft:tropical_fish", "weight": 20, "minCount": 4, "maxCount": 8 }
|
||||
],
|
||||
"misc": []
|
||||
}
|
||||
}
|
||||
@@ -51,18 +51,28 @@
|
||||
"antarchy:elythia_underground"
|
||||
],
|
||||
[],
|
||||
[],
|
||||
[
|
||||
"minecraft:sea_pickle",
|
||||
"minecraft:warm_ocean_vegetation"
|
||||
],
|
||||
[]
|
||||
],
|
||||
"creature_spawn_probability": 0.04,
|
||||
"spawners": {
|
||||
"monster": [],
|
||||
"creature": [],
|
||||
"monster": [
|
||||
{ "type": "antarchy:missile_squid", "weight": 12, "minCount": 1, "maxCount": 3 },
|
||||
{ "type": "antarchy:octopus_bomb", "weight": 4, "minCount": 1, "maxCount": 2 }
|
||||
],
|
||||
"creature": [
|
||||
{ "type": "minecraft:turtle", "weight": 5, "minCount": 1, "maxCount": 2 }
|
||||
],
|
||||
"ambient": [],
|
||||
"axolotls": [],
|
||||
"underground_water_creature": [],
|
||||
"water_creature": [],
|
||||
"water_ambient": [],
|
||||
"water_ambient": [
|
||||
{ "type": "minecraft:tropical_fish", "weight": 20, "minCount": 4, "maxCount": 8 }
|
||||
],
|
||||
"misc": []
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"type": "minecraft:vegetation_patch",
|
||||
"config": {
|
||||
"depth": 1,
|
||||
"extra_bottom_block_chance": 0.0,
|
||||
"extra_edge_column_chance": 0.75,
|
||||
"ground_state": {
|
||||
"type": "minecraft:simple_state_provider",
|
||||
"state": {
|
||||
"Name": "antarchy:amber_moss_block"
|
||||
}
|
||||
},
|
||||
"replaceable": "#antarchy:amber_moss_replaceable",
|
||||
"surface": "floor",
|
||||
"vegetation_chance": 0.6,
|
||||
"vegetation_feature": {
|
||||
"feature": "antarchy:amber_moss_vegetation",
|
||||
"placement": []
|
||||
},
|
||||
"vertical_range": 5,
|
||||
"xz_radius": {
|
||||
"type": "minecraft:uniform",
|
||||
"max_inclusive": 2,
|
||||
"min_inclusive": 1
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"type": "minecraft:simple_block",
|
||||
"config": {
|
||||
"to_place": {
|
||||
"type": "minecraft:simple_state_provider",
|
||||
"state": {
|
||||
"Name": "antarchy:amber_moss_carpet",
|
||||
"Properties": {
|
||||
"bottom": "true",
|
||||
"east": "none",
|
||||
"north": "none",
|
||||
"south": "none",
|
||||
"west": "none"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"type": "antarchy:cavaryn_bile_pools",
|
||||
"config": {}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"type": "antarchy:cavaryn_chiten_spikes",
|
||||
"config": {
|
||||
"floor_to_ceiling_search_range": 10,
|
||||
"radius": 4,
|
||||
"max_column_height": 5,
|
||||
"height_deviation": 1,
|
||||
"chance_of_column_at_max_distance_from_center": 0.3,
|
||||
"max_distance_from_center_affecting_chance_of_column": 4,
|
||||
"max_distance_from_center_affecting_height_bias": 4,
|
||||
"max_height_difference": 1,
|
||||
"large_variant_chance": 0.35,
|
||||
"large_variant_extra_radius": 3,
|
||||
"large_variant_extra_height": 4,
|
||||
"large_variant_extra_height_deviation": 2,
|
||||
"large_variant_surface_spike_multiplier": 2.5
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"type": "antarchy:cavaryn_creepvine",
|
||||
"config": {}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"type": "antarchy:cavaryn_terror_egg_patch",
|
||||
"config": {}
|
||||
}
|
||||