first pass royal decrees, time dilation transitions, kings tree fixes, manticore changes

This commit is contained in:
craisinlord committed 2026-08-29 23:15:51 -05:00
1 parent c573c574b9
commit 065a04fc93
30 files changed
+833 -24

No files matched your search

@@ -513,6 +513,23 @@ public final class AntarchySettings {
private static double manticoreAttackDamage = 7.0D;
private static int manticoreStingPoisonTicks = 100;
private static int queenManticoreCap = 15;
private static int queenManticoreSummonCooldownTicks = 600;
private static int queenManticoreSummonCount = 3;
private static double queenManticoreSummonRange = 24.0D;
private static double queenBeamDamage = 45.0D;
private static double queenBeamRange = 100.0D;
private static int queenBeamDurationTicks = 30;
private static int queenBeamCooldownTicks = 90;
private static double queenBeamTracking = 0.1D;
private static double queenBeamTerrainRadius = 5.0D;
private static int queenBeamTerrainCap = 192;
private static double kingBeamDamage = 50.0D;
private static double kingBeamRange = 100.0D;
private static int kingBeamDurationTicks = 30;
private static int kingBeamCooldownTicks = 90;
private static double kingBeamTracking = 0.1D;
private static double kingBeamTerrainRadius = 5.0D;
private static int kingBeamTerrainCap = 192;
private static double jerryInfantHealth = 20.0D;
private static double jerryInfantAttackDamage = 2.0D;
private static double jerryMatureHealth = 40.0D;
@@ -2749,6 +2766,40 @@ public final class AntarchySettings {
public static void setManticoreStingPoisonTicks(int v) { manticoreStingPoisonTicks = v; }
public static int queenManticoreCap() { return queenManticoreCap; }
public static void setQueenManticoreCap(int v) { queenManticoreCap = v; }
public static int queenManticoreSummonCooldownTicks() { return queenManticoreSummonCooldownTicks; }
public static void setQueenManticoreSummonCooldownTicks(int v) { queenManticoreSummonCooldownTicks = v; }
public static int queenManticoreSummonCount() { return queenManticoreSummonCount; }
public static void setQueenManticoreSummonCount(int v) { queenManticoreSummonCount = v; }
public static double queenManticoreSummonRange() { return queenManticoreSummonRange; }
public static void setQueenManticoreSummonRange(double v) { queenManticoreSummonRange = v; }
public static double queenBeamDamage() { return queenBeamDamage; }
public static void setQueenBeamDamage(double v) { queenBeamDamage = v; }
public static double queenBeamRange() { return queenBeamRange; }
public static void setQueenBeamRange(double v) { queenBeamRange = v; }
public static int queenBeamDurationTicks() { return queenBeamDurationTicks; }
public static void setQueenBeamDurationTicks(int v) { queenBeamDurationTicks = v; }
public static int queenBeamCooldownTicks() { return queenBeamCooldownTicks; }
public static void setQueenBeamCooldownTicks(int v) { queenBeamCooldownTicks = v; }
public static double queenBeamTracking() { return queenBeamTracking; }
public static void setQueenBeamTracking(double v) { queenBeamTracking = v; }
public static double queenBeamTerrainRadius() { return queenBeamTerrainRadius; }
public static void setQueenBeamTerrainRadius(double v) { queenBeamTerrainRadius = v; }
public static int queenBeamTerrainCap() { return queenBeamTerrainCap; }
public static void setQueenBeamTerrainCap(int v) { queenBeamTerrainCap = v; }
public static double kingBeamDamage() { return kingBeamDamage; }
public static void setKingBeamDamage(double v) { kingBeamDamage = v; }
public static double kingBeamRange() { return kingBeamRange; }
public static void setKingBeamRange(double v) { kingBeamRange = v; }
public static int kingBeamDurationTicks() { return kingBeamDurationTicks; }
public static void setKingBeamDurationTicks(int v) { kingBeamDurationTicks = v; }
public static int kingBeamCooldownTicks() { return kingBeamCooldownTicks; }
public static void setKingBeamCooldownTicks(int v) { kingBeamCooldownTicks = v; }
public static double kingBeamTracking() { return kingBeamTracking; }
public static void setKingBeamTracking(double v) { kingBeamTracking = v; }
public static double kingBeamTerrainRadius() { return kingBeamTerrainRadius; }
public static void setKingBeamTerrainRadius(double v) { kingBeamTerrainRadius = v; }
public static int kingBeamTerrainCap() { return kingBeamTerrainCap; }
public static void setKingBeamTerrainCap(int v) { kingBeamTerrainCap = v; }
public static double jerryInfantHealth() { return jerryInfantHealth; }
public static void setJerryInfantHealth(double v) { jerryInfantHealth = v; }
public static double jerryInfantAttackDamage() { return jerryInfantAttackDamage; }
@@ -186,6 +186,14 @@ public final class AntarchySoundEvents {
public static Supplier<SoundEvent> QUEEN_HURT = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> QUEEN_DEATH = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> QUEEN_BITE = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> QUEEN_ROAR = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> KING_BEAM_SHOOT = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> KING_FIREBALL_SHOOT = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> KING_DECREE_CAST = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> QUEEN_BEAM_SHOOT = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> QUEEN_BEAM_START = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> QUEEN_BEAM_LOOP = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> QUEEN_BEAM_END = (Supplier<SoundEvent>) UNBOUND;
private AntarchySoundEvents() {
}
@@ -367,7 +375,15 @@ public final class AntarchySoundEvents {
Supplier<SoundEvent> queenIdle,
Supplier<SoundEvent> queenHurt,
Supplier<SoundEvent> queenDeath,
Supplier<SoundEvent> queenBite
Supplier<SoundEvent> queenBite,
Supplier<SoundEvent> queenRoar,
Supplier<SoundEvent> kingBeamShoot,
Supplier<SoundEvent> kingFireballShoot,
Supplier<SoundEvent> kingDecreeCast,
Supplier<SoundEvent> queenBeamShoot,
Supplier<SoundEvent> queenBeamStart,
Supplier<SoundEvent> queenBeamLoop,
Supplier<SoundEvent> queenBeamEnd
) {
SQUIDZOOKA_FIRE = squidzookaFire;
SHRINK_RAY_CHARGE = shrinkRayCharge;
@@ -546,5 +562,13 @@ public final class AntarchySoundEvents {
QUEEN_HURT = queenHurt;
QUEEN_DEATH = queenDeath;
QUEEN_BITE = queenBite;
QUEEN_ROAR = queenRoar;
KING_BEAM_SHOOT = kingBeamShoot;
KING_FIREBALL_SHOOT = kingFireballShoot;
KING_DECREE_CAST = kingDecreeCast;
QUEEN_BEAM_SHOOT = queenBeamShoot;
QUEEN_BEAM_START = queenBeamStart;
QUEEN_BEAM_LOOP = queenBeamLoop;
QUEEN_BEAM_END = queenBeamEnd;
}
}
@@ -8,6 +8,7 @@ import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.entity.EntityRendererProvider;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.Nullable;
import software.bernie.geckolib.cache.object.BakedGeoModel;
import software.bernie.geckolib.renderer.GeoEntityRenderer;
@@ -30,4 +31,45 @@ public class RoyalBossRenderer extends GeoEntityRenderer<RoyalBossEntity> {
poseStack.scale(RoyalBossEntity.MODEL_RENDER_SCALE, RoyalBossEntity.MODEL_RENDER_SCALE, RoyalBossEntity.MODEL_RENDER_SCALE);
this.shadowRadius = 8.0F * RoyalBossEntity.MODEL_RENDER_SCALE;
}
@Override
public void render(RoyalBossEntity entity, float entityYaw, float partialTick, PoseStack poseStack,
MultiBufferSource bufferSource, int packedLight) {
super.render(entity, entityYaw, partialTick, poseStack, bufferSource, packedLight);
Vec3 end = entity.getRoyalBeamEndPosition();
if (!entity.isFiringRoyalBeam() || end == null) {
return;
}
double x = net.minecraft.util.Mth.lerp(partialTick, entity.xo, entity.getX());
double y = net.minecraft.util.Mth.lerp(partialTick, entity.yo, entity.getY());
double z = net.minecraft.util.Mth.lerp(partialTick, entity.zo, entity.getZ());
Vec3 start = entity.getRoyalBeamShootFrom(partialTick).subtract(x, y, z);
Vec3 finish = end.subtract(x, y, z);
Vec3 direction = finish.subtract(start).normalize();
Vec3 side = direction.cross(new Vec3(0.0D, 1.0D, 0.0D));
if (side.lengthSqr() < 1.0E-4D) {
side = direction.cross(new Vec3(1.0D, 0.0D, 0.0D));
}
side = side.normalize().scale(0.22D);
VertexConsumer vertices = bufferSource.getBuffer(RenderType.lightning());
poseStack.pushPose();
poseStack.scale(0.5F, 0.5F, 0.5F);
drawBeamQuad(vertices, poseStack, start, finish, side);
drawBeamQuad(vertices, poseStack, start, finish, new Vec3(-side.z, side.y, side.x));
poseStack.popPose();
}
private static void drawBeamQuad(VertexConsumer vertices, PoseStack poseStack, Vec3 start, Vec3 end, Vec3 side) {
var pose = poseStack.last().pose();
vertex(vertices, pose, start.subtract(side));
vertex(vertices, pose, start.add(side));
vertex(vertices, pose, end.add(side));
vertex(vertices, pose, end.subtract(side));
}
private static void vertex(VertexConsumer vertices, org.joml.Matrix4f pose, Vec3 pos) {
vertices.addVertex(pose, (float) pos.x, (float) pos.y, (float) pos.z)
.setColor(255, 255, 255, 255).setUv(0.0F, 0.0F).setUv2(15728880 & 65535, 15728880 >> 16)
.setNormal(0.0F, 1.0F, 0.0F);
}
}
@@ -3,6 +3,8 @@ package com.craisinlord.antarchy.content.entity.portal;
import com.craisinlord.antarchy.config.AntarchySettings;
import com.craisinlord.antarchy.content.AntarchyObjects;
import com.craisinlord.antarchy.content.AntarchyTags;
import com.craisinlord.antarchy.content.entity.ManticoreEntity;
import com.craisinlord.antarchy.content.entity.royal.QueenEntity;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -74,6 +76,10 @@ public class DimensionalTearEntity extends Entity implements GeoEntity {
private int eventTicks;
private int nextEventTicks;
private TearState pendingEvent = TearState.NORMAL;
@Nullable
private UUID queenOwnerId;
private int queenManticoreCount;
private boolean queenManticoreSpawned;
public DimensionalTearEntity(EntityType<? extends DimensionalTearEntity> entityType, Level level) {
super(entityType, level);
@@ -90,6 +96,14 @@ public class DimensionalTearEntity extends Entity implements GeoEntity {
return tear;
}
public static DimensionalTearEntity createQueenManticoreTear(ServerLevel level, Vec3 pos, float yaw,
int lifetimeTicks, UUID queenId, int count) {
DimensionalTearEntity tear = create(level, pos, yaw, lifetimeTicks);
tear.queenOwnerId = queenId;
tear.queenManticoreCount = Math.max(1, count);
return tear;
}
public void linkTo(DimensionalTearEntity other) {
this.linkedTearId = other.getUUID();
this.linkedFallbackPos = other.position();
@@ -121,6 +135,10 @@ public class DimensionalTearEntity extends Entity implements GeoEntity {
return;
}
if (this.queenOwnerId != null && this.ageTicks == OPENING_ANIMATION_TICKS) {
this.spawnQueenManticores((ServerLevel) this.level());
}
DimensionalTearEntity linked = this.findLinkedTear();
if (linked == null || !linked.isAlive()) {
this.discard();
@@ -179,6 +197,49 @@ public class DimensionalTearEntity extends Entity implements GeoEntity {
level.playSound(null, this.blockPosition(), eventState == TearState.LUCID ? SoundEvents.AMETHYST_BLOCK_CHIME : SoundEvents.ENDERMAN_SCREAM, SoundSource.HOSTILE, 0.75F, eventState == TearState.LUCID ? 1.45F : 0.85F);
}
private void spawnQueenManticores(ServerLevel level) {
if (this.queenManticoreSpawned) {
return;
}
this.queenManticoreSpawned = true;
Entity owner = level.getEntity(this.queenOwnerId);
if (!(owner instanceof QueenEntity queen) || !queen.isAlive()) {
return;
}
LivingEntity target = queen.getTarget();
for (int i = 0; i < this.queenManticoreCount; i++) {
if (ManticoreEntity.countSummonedBy(level, queen.getUUID()) >= com.craisinlord.antarchy.config.AntarchySettings.queenManticoreCap()) {
break;
}
ManticoreEntity manticore = AntarchyObjects.MANTICORE.get().create(level);
if (manticore == null) {
break;
}
Vec3 exit = this.exitPosition().add(0.0D, 0.8D + i * 0.35D, 0.0D);
manticore.moveTo(exit.x, exit.y, exit.z, this.getYRot(), 0.0F);
if (!level.noCollision(manticore)) {
manticore.discard();
continue;
}
manticore.finalizeSpawn(level, level.getCurrentDifficultyAt(BlockPos.containing(exit)), MobSpawnType.MOB_SUMMONED, null);
manticore.markQueenSummoned(queen.getUUID());
if (target != null && target.isAlive()) {
manticore.setTarget(target);
}
manticore.setDeltaMovement(this.getViewVector(1.0F).scale(0.2D).add(0.0D, 0.08D, 0.0D));
manticore.setNoGravity(true);
level.addFreshEntity(manticore);
}
}
public static void discardQueenOwnedTears(ServerLevel level, UUID queenId) {
for (DimensionalTearEntity tear : level.getEntitiesOfClass(DimensionalTearEntity.class,
new AABB(-3.0E7D, level.getMinBuildHeight(), -3.0E7D, 3.0E7D, level.getMaxBuildHeight(), 3.0E7D),
tear -> queenId.equals(tear.queenOwnerId))) {
tear.discard();
}
}
@Nullable
private Mob createTaggedMob(ServerLevel level, TagKey<EntityType<?>> tag) {
return BuiltInRegistries.ENTITY_TYPE.getTag(tag)
@@ -331,6 +392,11 @@ public class DimensionalTearEntity extends Entity implements GeoEntity {
tag.putDouble("LinkedX", this.linkedFallbackPos.x);
tag.putDouble("LinkedY", this.linkedFallbackPos.y);
tag.putDouble("LinkedZ", this.linkedFallbackPos.z);
if (this.queenOwnerId != null) {
tag.putUUID("QueenOwner", this.queenOwnerId);
tag.putInt("QueenManticoreCount", this.queenManticoreCount);
tag.putBoolean("QueenManticoreSpawned", this.queenManticoreSpawned);
}
}
@Override
@@ -346,6 +412,11 @@ public class DimensionalTearEntity extends Entity implements GeoEntity {
this.linkedTearId = tag.getUUID("LinkedTearId");
}
this.linkedFallbackPos = new Vec3(tag.getDouble("LinkedX"), tag.getDouble("LinkedY"), tag.getDouble("LinkedZ"));
if (tag.hasUUID("QueenOwner")) {
this.queenOwnerId = tag.getUUID("QueenOwner");
this.queenManticoreCount = tag.getInt("QueenManticoreCount");
this.queenManticoreSpawned = tag.getBoolean("QueenManticoreSpawned");
}
}
@Override
@@ -7,8 +7,30 @@ import net.minecraft.world.BossEvent;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.ai.attributes.AttributeSupplier;
import net.minecraft.world.level.Level;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.Nullable;
import net.minecraft.world.entity.LightningBolt;
import net.minecraft.world.entity.EntityType;
import com.craisinlord.antarchy.content.AntarchyObjects;
import com.craisinlord.antarchy.content.entity.royal.beam.RoyalBeamSettings;
import com.craisinlord.antarchy.content.entity.royal.beam.RoyalBeamTerrainMode;
import com.craisinlord.antarchy.content.entity.royal.decree.AdvanceDecree;
import com.craisinlord.antarchy.content.entity.royal.decree.CloseQuartersDecree;
import com.craisinlord.antarchy.content.entity.royal.decree.KeepYourDistanceDecree;
import com.craisinlord.antarchy.content.entity.royal.decree.NoRetreatDecree;
import com.craisinlord.antarchy.content.entity.royal.decree.RoyalDecree;
import java.util.List;
public class KingEntity extends RoyalBossEntity {
@Nullable
private Vec3 patrolCenter;
private int patrolCooldownTicks;
private int decreeCooldownTicks;
private int fireAttackCooldownTicks;
private int lightningAttackCooldownTicks;
private RoyalDecree activeDecree = new AdvanceDecree();
private static final List<RoyalDecree> DECREES = List.of(new CloseQuartersDecree(), new KeepYourDistanceDecree(), new NoRetreatDecree(), new AdvanceDecree());
public KingEntity(EntityType<? extends KingEntity> entityType, Level level) {
super(entityType, level);
}
@@ -51,4 +73,94 @@ public class KingEntity extends RoyalBossEntity {
protected SoundEvent royalBiteSound() {
return AntarchySoundEvents.KING_BITE.get();
}
@Override
protected SoundEvent royalBeamShootSound() { return AntarchySoundEvents.KING_BEAM_SHOOT.get(); }
@Override
protected SoundEvent royalBeamStartSound() { return AntarchySoundEvents.KING_BEAM_SHOOT.get(); }
@Override
protected SoundEvent royalBeamLoopSound() { return AntarchySoundEvents.KING_BEAM_SHOOT.get(); }
@Override
protected SoundEvent royalBeamEndSound() { return AntarchySoundEvents.KING_BEAM_SHOOT.get(); }
@Override
protected RoyalBeamSettings royalBeamSettings() {
return new RoyalBeamSettings(AntarchySettings.kingBeamRange(), AntarchySettings.kingBeamTracking(), 7.5D,
AntarchySettings.kingBeamDurationTicks(), AntarchySettings.kingBeamCooldownTicks(), 6.0F, 6.0F,
(float) AntarchySettings.kingBeamDamage(), 1.0F, 3, 100.0D,
(float) AntarchySettings.kingBeamTerrainRadius(), 4.0F, AntarchySettings.kingBeamTerrainCap(),
1.0F, 0.08F, 15.0F, true, true);
}
@Override
protected RoyalBeamTerrainMode royalBeamTerrainMode() {
return RoyalBeamTerrainMode.BUILD_ICE;
}
@Override
protected RoyalBeamTerrainMode royalBeamTerrainMode(@Nullable RoyalHead head) {
return head != null && head.slot() == RoyalHead.Slot.RIGHT ? RoyalBeamTerrainMode.BUILD_ICE : RoyalBeamTerrainMode.NONE;
}
@Override
public void tick() {
super.tick();
if (!this.level().isClientSide && this.getTarget() != null && this.level() instanceof ServerLevel level) {
this.tickElementalAttacks(level, this.getTarget());
}
if (this.level().isClientSide || this.getTarget() != null || this.isDeadOrDying()
|| !(this.level() instanceof ServerLevel)) {
if (!this.level().isClientSide && this.getTarget() != null && this.level() instanceof ServerLevel level) {
this.tickDecree(level, this.getTarget());
}
return;
}
if (this.patrolCenter == null) {
this.patrolCenter = this.position();
}
if (this.patrolCooldownTicks-- <= 0) {
this.patrolCooldownTicks = 100 + this.random.nextInt(100);
double angle = this.random.nextDouble() * Math.PI * 2.0D;
double radius = 12.0D + this.random.nextDouble() * 18.0D;
this.getMoveControl().setWantedPosition(this.patrolCenter.x + Math.cos(angle) * radius,
this.patrolCenter.y + 6.0D + this.random.nextDouble() * 12.0D,
this.patrolCenter.z + Math.sin(angle) * radius, 1.0D);
}
}
private void tickDecree(ServerLevel level, net.minecraft.world.entity.LivingEntity target) {
if (this.decreeCooldownTicks-- <= 0) {
this.decreeCooldownTicks = 220;
this.activeDecree = DECREES.get(this.random.nextInt(DECREES.size()));
this.playSound(AntarchySoundEvents.KING_DECREE_CAST.get(), 3.0F, 0.9F + this.random.nextFloat() * 0.15F);
}
this.activeDecree.apply(level, this, target);
}
private void tickElementalAttacks(ServerLevel level, net.minecraft.world.entity.LivingEntity target) {
if (this.fireAttackCooldownTicks-- <= 0) {
this.fireAttackCooldownTicks = 110;
Vec3 start = this.position().add(0.0D, 5.0D, 0.0D);
RoyalBoltEntity bolt = new RoyalBoltEntity(AntarchyObjects.ROYAL_BOLT.get(), level);
bolt.setOwner(this);
bolt.setPos(start);
Vec3 delta = target.getEyePosition().subtract(start);
bolt.shoot(delta.x, delta.y, delta.z, 1.15F, 0.0F);
level.addFreshEntity(bolt);
this.playSound(AntarchySoundEvents.KING_FIREBALL_SHOOT.get(), 3.0F, 1.0F);
}
if (this.lightningAttackCooldownTicks-- <= 0) {
this.lightningAttackCooldownTicks = 150;
for (net.minecraft.world.entity.LivingEntity living : level.getEntitiesOfClass(net.minecraft.world.entity.LivingEntity.class,
target.getBoundingBox().inflate(5.0D), entity -> entity.isAlive() && entity != this)) {
LightningBolt bolt = EntityType.LIGHTNING_BOLT.create(level);
if (bolt == null) continue;
bolt.moveTo(living.getX(), living.getY(), living.getZ());
level.addFreshEntity(bolt);
}
}
}
}
@@ -2,13 +2,36 @@ package com.craisinlord.antarchy.content.entity.royal;
import com.craisinlord.antarchy.config.AntarchySettings;
import com.craisinlord.antarchy.content.AntarchySoundEvents;
import com.craisinlord.antarchy.content.AntarchyObjects;
import com.craisinlord.antarchy.content.entity.ManticoreEntity;
import net.minecraft.core.BlockPos;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.util.Mth;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.BossEvent;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.ai.attributes.AttributeSupplier;
import net.minecraft.world.entity.MobSpawnType;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.Vec3;
import com.craisinlord.antarchy.content.entity.royal.beam.RoyalBeamSettings;
import com.craisinlord.antarchy.content.entity.royal.beam.RoyalBeamTerrainMode;
import com.craisinlord.antarchy.content.entity.portal.DimensionalTearEntity;
import com.craisinlord.antarchy.content.time.TimeDilationApi;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.phys.AABB;
public class QueenEntity extends RoyalBossEntity {
private static final String SUMMON_COOLDOWN_KEY = "ManticoreSummonCooldownTicks";
private static final int FAILED_SUMMON_RETRY_TICKS = 20;
private static final int POSITION_ATTEMPTS_PER_MANTICORE = 8;
private int manticoreSummonCooldownTicks;
private int gravityStompCooldownTicks;
private int timeFieldCooldownTicks;
public QueenEntity(EntityType<? extends QueenEntity> entityType, Level level) {
super(entityType, level);
}
@@ -51,4 +74,187 @@ public class QueenEntity extends RoyalBossEntity {
protected SoundEvent royalBiteSound() {
return AntarchySoundEvents.QUEEN_BITE.get();
}
@Override
protected SoundEvent royalBeamShootSound() { return AntarchySoundEvents.QUEEN_BEAM_SHOOT.get(); }
@Override
protected SoundEvent royalBeamStartSound() { return AntarchySoundEvents.QUEEN_BEAM_START.get(); }
@Override
protected SoundEvent royalBeamLoopSound() { return AntarchySoundEvents.QUEEN_BEAM_LOOP.get(); }
@Override
protected SoundEvent royalBeamEndSound() { return AntarchySoundEvents.QUEEN_BEAM_END.get(); }
@Override
protected RoyalBeamSettings royalBeamSettings() {
return new RoyalBeamSettings(AntarchySettings.queenBeamRange(), AntarchySettings.queenBeamTracking(), 7.5D,
AntarchySettings.queenBeamDurationTicks(), AntarchySettings.queenBeamCooldownTicks(), 6.0F, 6.0F,
(float) AntarchySettings.queenBeamDamage(), 1.0F, 3, 100.0D,
(float) AntarchySettings.queenBeamTerrainRadius(), 4.0F, AntarchySettings.queenBeamTerrainCap(),
1.0F, 0.08F, 15.0F, true, true);
}
@Override
protected RoyalBeamTerrainMode royalBeamTerrainMode() {
return RoyalBeamTerrainMode.DESTROY;
}
@Override
public void tick() {
super.tick();
if (this.level().isClientSide || this.isDeadOrDying()) {
return;
}
this.tickManticoreSummon();
this.tickGravityAttacks();
}
private void tickManticoreSummon() {
if (this.manticoreSummonCooldownTicks > 0) {
this.manticoreSummonCooldownTicks--;
return;
}
if (!(this.level() instanceof ServerLevel serverLevel)) {
return;
}
net.minecraft.world.entity.LivingEntity target = this.getTarget();
if (target == null || !target.isAlive() || !this.canAttack(target)) {
return;
}
int currentCount = ManticoreEntity.countSummonedBy(serverLevel, this.getUUID());
int cap = AntarchySettings.queenManticoreCap();
if (currentCount >= cap) {
this.manticoreSummonCooldownTicks = FAILED_SUMMON_RETRY_TICKS;
return;
}
int summonCount = Math.min(AntarchySettings.queenManticoreSummonCount(), cap - currentCount);
int spawned = this.spawnManticoreTears(serverLevel, target, summonCount);
this.manticoreSummonCooldownTicks = spawned > 0
? Math.max(20, AntarchySettings.queenManticoreSummonCooldownTicks())
: FAILED_SUMMON_RETRY_TICKS;
if (spawned > 0) {
this.triggerAnim("body_action", "minion_spawn");
this.playSound(AntarchySoundEvents.QUEEN_ROAR.get(), 3.0F, 0.8F + this.random.nextFloat() * 0.12F);
serverLevel.sendParticles(
net.minecraft.core.particles.ParticleTypes.PORTAL,
this.getX(), this.getY() + 2.0D, this.getZ(),
36, 3.0D, 2.0D, 3.0D, 0.15D);
}
}
private int spawnManticoreTears(ServerLevel level, net.minecraft.world.entity.LivingEntity target, int count) {
int pairs = Math.max(1, Math.min(3, (count + 1) / 2));
int created = 0;
for (int i = 0; i < pairs; i++) {
double angle = this.random.nextDouble() * Mth.TWO_PI;
double distance = Math.max(8.0D, AntarchySettings.queenManticoreSummonRange() * 0.65D);
Vec3 firstPos = this.position().add(Math.cos(angle) * distance, 1.0D, Math.sin(angle) * distance);
Vec3 secondPos = this.position().add(Math.cos(angle + Math.PI) * distance, 1.0D, Math.sin(angle + Math.PI) * distance);
DimensionalTearEntity first = DimensionalTearEntity.createQueenManticoreTear(level, firstPos,
(float) Math.toDegrees(angle), 240, this.getUUID(), Math.min(3, count));
DimensionalTearEntity second = DimensionalTearEntity.createQueenManticoreTear(level, secondPos,
(float) Math.toDegrees(angle + Math.PI), 240, this.getUUID(), Math.min(3, count));
first.linkTo(second);
second.linkTo(first);
level.addFreshEntity(first);
level.addFreshEntity(second);
created++;
}
return created;
}
private void tickGravityAttacks() {
if (this.gravityStompCooldownTicks > 0) {
this.gravityStompCooldownTicks--;
}
if (this.timeFieldCooldownTicks > 0) {
this.timeFieldCooldownTicks--;
}
if (!(this.level() instanceof ServerLevel level)) {
return;
}
net.minecraft.world.entity.LivingEntity target = this.getTarget();
if (target == null || !target.isAlive()) {
return;
}
if (this.gravityStompCooldownTicks <= 0) {
this.gravityStompCooldownTicks = 180;
this.triggerAnim("body_action", "stomp");
this.playSound(AntarchySoundEvents.QUEEN_ROAR.get(), 3.0F, 0.7F);
DamageSource source = this.damageSources().mobAttack(this);
for (net.minecraft.world.entity.LivingEntity living : level.getEntitiesOfClass(net.minecraft.world.entity.LivingEntity.class,
this.getBoundingBox().inflate(18.0D), entity -> entity instanceof Player && entity.isAlive())) {
double distance = Math.max(1.0D, living.distanceTo(this));
living.hurt(source, (float) (12.0D * Math.max(0.25D, 1.0D - distance / 24.0D)));
Vec3 push = living.position().subtract(this.position()).normalize().scale(1.3D);
living.setDeltaMovement(living.getDeltaMovement().add(push.x, 0.65D, push.z));
living.hasImpulse = true;
}
}
if (this.timeFieldCooldownTicks <= 0) {
this.timeFieldCooldownTicks = 260;
TimeDilationApi.createField(level, target.position(), 12.0D, 0.45D, 140);
this.triggerAnim("body_action", "wing_gust");
}
}
@Override
public void die(DamageSource damageSource) {
if (!this.level().isClientSide && this.level() instanceof ServerLevel level) {
DimensionalTearEntity.discardQueenOwnedTears(level, this.getUUID());
}
super.die(damageSource);
}
private boolean spawnManticore(ServerLevel serverLevel, net.minecraft.world.entity.LivingEntity target) {
double minimumDistance = Math.max(4.0D, this.getBbWidth() * 0.55D);
double maximumDistance = Math.max(minimumDistance + 1.0D, AntarchySettings.queenManticoreSummonRange());
for (int attempt = 0; attempt < POSITION_ATTEMPTS_PER_MANTICORE; attempt++) {
double angle = this.random.nextDouble() * Mth.TWO_PI;
double distance = Mth.lerp(this.random.nextDouble(), minimumDistance, maximumDistance);
double x = this.getX() + Math.cos(angle) * distance;
double y = this.getY() + (this.random.nextDouble() - 0.5D) * 2.0D;
double z = this.getZ() + Math.sin(angle) * distance;
ManticoreEntity manticore = AntarchyObjects.MANTICORE.get().create(serverLevel);
if (manticore == null) {
return false;
}
manticore.moveTo(x, y, z, this.random.nextFloat() * 360.0F, 0.0F);
BlockPos spawnPos = manticore.blockPosition();
if (!serverLevel.noCollision(manticore)
|| !serverLevel.isEmptyBlock(spawnPos)
|| !ManticoreEntity.canSpawn(AntarchyObjects.MANTICORE.get(), serverLevel, MobSpawnType.MOB_SUMMONED, spawnPos, this.random)) {
manticore.discard();
continue;
}
manticore.finalizeSpawn(serverLevel, serverLevel.getCurrentDifficultyAt(spawnPos), MobSpawnType.MOB_SUMMONED, null);
manticore.markQueenSummoned(this.getUUID());
manticore.setTarget(target);
serverLevel.addFreshEntity(manticore);
return true;
}
return false;
}
@Override
public void addAdditionalSaveData(CompoundTag tag) {
super.addAdditionalSaveData(tag);
tag.putInt(SUMMON_COOLDOWN_KEY, this.manticoreSummonCooldownTicks);
tag.putInt("GravityStompCooldownTicks", this.gravityStompCooldownTicks);
tag.putInt("TimeFieldCooldownTicks", this.timeFieldCooldownTicks);
}
@Override
public void readAdditionalSaveData(CompoundTag tag) {
super.readAdditionalSaveData(tag);
this.manticoreSummonCooldownTicks = Math.max(0, tag.getInt(SUMMON_COOLDOWN_KEY));
this.gravityStompCooldownTicks = Math.max(0, tag.getInt("GravityStompCooldownTicks"));
this.timeFieldCooldownTicks = Math.max(0, tag.getInt("TimeFieldCooldownTicks"));
}
}
@@ -5,6 +5,8 @@ import com.craisinlord.antarchy.content.entity.multipart.MultipartEntityOwner;
import com.craisinlord.antarchy.content.entity.multipart.MultipartLayout;
import com.craisinlord.antarchy.content.entity.multipart.MultipartPartDefinition;
import com.craisinlord.antarchy.content.entity.royal.beam.RoyalBeamController;
import com.craisinlord.antarchy.content.entity.royal.beam.RoyalBeamSettings;
import com.craisinlord.antarchy.content.entity.royal.beam.RoyalBeamTerrainMode;
import java.util.List;
import net.minecraft.network.chat.Component;
import net.minecraft.network.syncher.EntityDataAccessor;
@@ -50,7 +52,7 @@ import software.bernie.geckolib.util.GeckoLibUtil;
public abstract class RoyalBossEntity extends Monster implements GeoEntity, MultipartEntityOwner {
public static final float MODEL_RENDER_SCALE = 2.0F;
public static final float GAMEPLAY_WIDTH = 30.0F;
public static final float GAMEPLAY_HEIGHT = 30.0F;
public static final float GAMEPLAY_HEIGHT = 15.0F;
public enum Phase {
ONE(1, 1.00F),
@@ -109,6 +111,10 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
new RoyalHead(RoyalHead.Slot.CENTER),
new RoyalHead(RoyalHead.Slot.RIGHT)
};
private int beamCooldownTicks;
@Nullable
private RoyalHead activeBeamHead;
private int beamLoopSoundTicks;
@Nullable
private Entity[] multipartParts;
@@ -136,6 +142,22 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
protected abstract SoundEvent royalBiteSound();
protected abstract SoundEvent royalBeamShootSound();
protected abstract SoundEvent royalBeamStartSound();
protected abstract SoundEvent royalBeamLoopSound();
protected abstract SoundEvent royalBeamEndSound();
protected abstract RoyalBeamSettings royalBeamSettings();
protected abstract RoyalBeamTerrainMode royalBeamTerrainMode();
protected RoyalBeamTerrainMode royalBeamTerrainMode(@Nullable RoyalHead head) {
return this.royalBeamTerrainMode();
}
public static AttributeSupplier.Builder createBaseAttributes(double health, double attackDamage) {
return Mob.createMobAttributes()
.add(Attributes.MAX_HEALTH, health)
@@ -272,6 +294,7 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
this.getLookControl().setLookAt(primaryTarget, 30.0F, 30.0F);
this.assignHeadTargets(primaryTarget);
this.tickRoyalBeam(primaryTarget);
this.steerTowardTarget(primaryTarget);
this.tickBodyCrush();
@@ -304,6 +327,70 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
}
}
private void tickRoyalBeam(LivingEntity primaryTarget) {
if (this.beamCooldownTicks > 0) {
this.beamCooldownTicks--;
}
if (this.activeBeamHead != null) {
LivingEntity beamTarget = this.activeBeamHead.target(this.level());
if (!this.activeBeamHead.shooting() || beamTarget == null || !beamTarget.isAlive()) {
this.stopRoyalBeam();
} else {
if (this.beamLoopSoundTicks-- <= 0) {
this.beamLoopSoundTicks = 20;
this.playSound(this.royalBeamLoopSound(), 2.5F, 1.0F);
}
this.beamController.tick(
this.headAnchor(this.activeBeamHead),
beamTarget,
this.royalBeamSettings(),
this.royalBeamTerrainMode(this.activeBeamHead),
this::setRoyalBeamEndPosition);
if (!this.beamController.isFiring()) {
this.stopRoyalBeam();
}
return;
}
}
if (this.beamCooldownTicks > 0) {
return;
}
Phase phase = this.phase();
for (RoyalHead head : this.heads) {
LivingEntity target = head.target(this.level());
if (!head.readyToAttack() || target == null || !this.canAttack(target)
|| this.headWithinBiteReach(head, target)) {
continue;
}
int duration = Math.max(1, this.royalBeamSettings().durationTicks());
int cooldown = Math.max(1, Mth.floor(this.royalBeamSettings().cooldownTicks() * phase.cooldownScale()));
head.startShoot(duration, cooldown);
head.setBeamActive(true);
this.activeBeamHead = head;
this.beamCooldownTicks = cooldown;
this.beamController.start(this.headAnchor(head), target.getEyePosition(), duration);
this.setFiringRoyalBeam(true);
this.triggerAnim(head.slot().controllerName(), "shoot");
this.beamLoopSoundTicks = 20;
this.playSound(this.royalBeamStartSound(), 3.0F, 1.0F);
this.playSound(this.royalBeamShootSound(), 3.0F, 1.0F);
return;
}
}
private void stopRoyalBeam() {
if (this.activeBeamHead != null) {
this.activeBeamHead.setBeamActive(false);
}
this.activeBeamHead = null;
this.beamController.stop();
this.playSound(this.royalBeamEndSound(), 2.5F, 1.0F);
this.setFiringRoyalBeam(false);
this.setRoyalBeamEndPosition(null);
}
public Phase phase() {
float fraction = this.getMaxHealth() <= 0.0F ? 1.0F : this.getHealth() / this.getMaxHealth();
if (fraction > PHASE_TWO_THRESHOLD) {
@@ -512,6 +599,9 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
}
public Vec3 getRoyalBeamShootFrom(float partialTicks) {
if (this.activeBeamHead != null) {
return this.headAnchor(this.activeBeamHead);
}
double x = Mth.lerp(partialTicks, this.xo, this.getX());
double y = Mth.lerp(partialTicks, this.yo, this.getY()) + this.getBbHeight() * 0.72D;
double z = Mth.lerp(partialTicks, this.zo, this.getZ());
@@ -9,11 +9,11 @@ public final class RoyalBossMultipartLayout implements MultipartLayout {
public static final MultipartPartDefinition BODY = new MultipartPartDefinition(
"body", 20.0F, 24.0F, 1.00F, 0.0D, 3.0D, 0.0D, true);
public static final MultipartPartDefinition LEFT_HEAD = new MultipartPartDefinition(
"left_head", 8.0F, 8.0F, 1.30F, 12.0D, 20.0D, -7.0D, true);
"left_head", 4.0F, 4.0F, 1.30F, 12.0D, 20.0D, -7.0D, true);
public static final MultipartPartDefinition CENTER_HEAD = new MultipartPartDefinition(
"center_head", 9.0F, 9.0F, 1.20F, 13.0D, 22.0D, 0.0D, true);
"center_head", 4.5F, 4.5F, 1.20F, 13.0D, 22.0D, 0.0D, true);
public static final MultipartPartDefinition RIGHT_HEAD = new MultipartPartDefinition(
"right_head", 8.0F, 8.0F, 1.30F, 12.0D, 20.0D, 7.0D, true);
"right_head", 4.0F, 4.0F, 1.30F, 12.0D, 20.0D, 7.0D, true);
public static final MultipartPartDefinition LEFT_WING = new MultipartPartDefinition(
"left_wing", 14.0F, 16.0F, 0.85F, -2.0D, 14.0D, -13.0D, true);
public static final MultipartPartDefinition RIGHT_WING = new MultipartPartDefinition(
@@ -94,6 +94,10 @@ public final class RoyalHead {
return this.beamActive;
}
public boolean shooting() {
return this.shootTicks > 0;
}
public boolean tickBiteHit() {
if (this.biteTicks <= 0) {
return false;
@@ -1,6 +1,8 @@
package com.craisinlord.antarchy.content.entity.royal.beam;
import java.util.List;
import java.util.HashSet;
import java.util.Set;
import java.util.function.Consumer;
import net.minecraft.core.BlockPos;
import net.minecraft.util.Mth;
@@ -117,15 +119,16 @@ public final class RoyalBeamController {
Vec3 direction = beamEnd.subtract(shootFrom).normalize();
double distance = shootFrom.distanceTo(beamEnd);
DamageSource damageSource = this.owner.damageSources().mobAttack(this.owner);
Set<Integer> damagedThisTick = new HashSet<>();
for (double walked = settings.pathStep(); walked < Math.min(distance, settings.range()); walked += settings.pathStep()) {
Vec3 sample = shootFrom.add(direction.scale(walked));
hurtEntitiesAround(sample, settings.pathDamageRadius(), settings.damage(), settings.knockback(), damageSource, settings.requireLineOfSightForDamage());
hurtEntitiesAround(sample, settings.pathDamageRadius(), settings.damage(), settings.knockback(), damageSource, settings.requireLineOfSightForDamage(), damagedThisTick);
if (shouldMutateTerrain(settings, walked)) {
mutateTerrainAround(sample, settings.pathTerrainRadius(), settings, terrainMode);
}
}
hurtEntitiesAround(beamEnd, settings.impactDamageRadius(), settings.damage(), settings.knockback(), damageSource, settings.requireLineOfSightForDamage());
hurtEntitiesAround(beamEnd, settings.impactDamageRadius(), settings.damage(), settings.knockback(), damageSource, settings.requireLineOfSightForDamage(), damagedThisTick);
if (shouldMutateTerrain(settings, shootFrom.distanceTo(beamEnd))) {
mutateTerrainAround(beamEnd, settings.impactTerrainRadius(), settings, terrainMode);
}
@@ -145,7 +148,8 @@ public final class RoyalBeamController {
float damageAmount,
float knockbackAmount,
DamageSource damageSource,
boolean requireLineOfSight
boolean requireLineOfSight,
Set<Integer> damagedThisTick
) {
if (radius <= 0.0F || damageAmount <= 0.0F) {
return false;
@@ -163,10 +167,12 @@ public final class RoyalBeamController {
|| this.owner.isAlliedTo(living)
|| living.getType() == this.owner.getType()
|| living.distanceToSqr(center) > radius * radius
|| damagedThisTick.contains(living.getId())
|| requireLineOfSight && !canEntityBeHurtFrom(center, living)) {
continue;
}
if (living.hurt(damageSource, damageAmount)) {
damagedThisTick.add(living.getId());
hurtAny = true;
knockbackTarget(living, knockbackAmount);
}
@@ -4,6 +4,8 @@ public record RoyalBeamSettings(
double range,
double targetTracking,
double pathStep,
int durationTicks,
int cooldownTicks,
float pathDamageRadius,
float impactDamageRadius,
float damage,
@@ -24,6 +26,8 @@ public record RoyalBeamSettings(
100.0D,
0.1D,
7.5D,
30,
90,
6.0F,
6.0F,
damage,
@@ -45,6 +49,8 @@ public record RoyalBeamSettings(
range = Math.max(1.0D, range);
targetTracking = Math.clamp(targetTracking, 0.0D, 1.0D);
pathStep = Math.max(1.0D, pathStep);
durationTicks = Math.max(1, durationTicks);
cooldownTicks = Math.max(1, cooldownTicks);
pathDamageRadius = Math.max(0.0F, pathDamageRadius);
impactDamageRadius = Math.max(0.0F, impactDamageRadius);
damage = Math.max(0.0F, damage);
@@ -0,0 +1,9 @@
package com.craisinlord.antarchy.content.entity.royal.decree;
import com.craisinlord.antarchy.content.entity.royal.KingEntity;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.phys.Vec3;
public final class AdvanceDecree implements RoyalDecree {
public String name() { return "ADVANCE"; }
public void apply(ServerLevel level, KingEntity king, LivingEntity target) { Vec3 p = king.position().subtract(target.position()).normalize().scale(0.35D); target.setDeltaMovement(target.getDeltaMovement().add(p.x, 0.05D, p.z)); target.hasImpulse = true; }
}
@@ -0,0 +1,8 @@
package com.craisinlord.antarchy.content.entity.royal.decree;
import com.craisinlord.antarchy.content.entity.royal.KingEntity;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.LivingEntity;
public final class CloseQuartersDecree implements RoyalDecree {
public String name() { return "CLOSE QUARTERS"; }
public void apply(ServerLevel level, KingEntity king, LivingEntity target) { if (target.distanceTo(king) <= 12.0D) target.hurt(king.damageSources().mobAttack(king), 4.0F); }
}
@@ -0,0 +1,9 @@
package com.craisinlord.antarchy.content.entity.royal.decree;
import com.craisinlord.antarchy.content.entity.royal.KingEntity;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.phys.Vec3;
public final class KeepYourDistanceDecree implements RoyalDecree {
public String name() { return "KEEP YOUR DISTANCE"; }
public void apply(ServerLevel level, KingEntity king, LivingEntity target) { if (target.distanceTo(king) < 18.0D) { Vec3 p = target.position().subtract(king.position()).normalize().scale(0.8D); target.setDeltaMovement(target.getDeltaMovement().add(p.x, 0.2D, p.z)); target.hasImpulse = true; } }
}
@@ -0,0 +1,8 @@
package com.craisinlord.antarchy.content.entity.royal.decree;
import com.craisinlord.antarchy.content.entity.royal.KingEntity;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.LivingEntity;
public final class NoRetreatDecree implements RoyalDecree {
public String name() { return "NO RETREAT"; }
public void apply(ServerLevel level, KingEntity king, LivingEntity target) { if (target.distanceTo(king) > 28.0D) target.hurt(king.damageSources().mobAttack(king), 3.0F); }
}
@@ -0,0 +1,10 @@
package com.craisinlord.antarchy.content.entity.royal.decree;
import com.craisinlord.antarchy.content.entity.royal.KingEntity;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.LivingEntity;
public interface RoyalDecree {
String name();
void apply(ServerLevel level, KingEntity king, LivingEntity target);
}
@@ -78,6 +78,10 @@ public class TimeDilationFieldEntity extends Entity {
return TimeDilationMath.clampRate(this.entityData.get(RATE));
}
public double effectiveFieldRate() {
return TimeDilationMath.effectiveFieldRate(this.fieldRate(), this.age, this.durationTicks);
}
public int fieldDurationTicks() {
return this.durationTicks;
}
@@ -23,7 +23,7 @@ public final class TimeDilationFieldSampler {
continue;
}
double falloff = TimeDilationMath.gaussianFalloff(Math.sqrt(distanceSqr), field.fieldRadius());
combinedRate *= TimeDilationMath.localFieldRate(field.fieldRate(), falloff);
combinedRate *= TimeDilationMath.localFieldRate(field.effectiveFieldRate(), falloff);
}
return TimeDilationMath.clampRate(combinedRate);
}
@@ -3,6 +3,7 @@ package com.craisinlord.antarchy.content.time;
public final class TimeDilationMath {
public static final double MIN_RATE = 0.05D;
public static final double NORMAL_RATE = 1.0D;
public static final int FIELD_TRANSITION_TICKS = 20;
private static final double GAUSSIAN_EDGE_EXPONENT = -4.5D;
private TimeDilationMath() {
@@ -31,4 +32,25 @@ public final class TimeDilationMath {
}
return Math.pow(clampedRate, clampedFalloff);
}
public static double smoothStep(double value) {
double clamped = Math.max(0.0D, Math.min(1.0D, value));
return clamped * clamped * (3.0D - 2.0D * clamped);
}
public static double fieldStrength(int age, int durationTicks) {
double fadeIn = smoothStep((double) age / FIELD_TRANSITION_TICKS);
if (durationTicks < 0) {
return fadeIn;
}
double remaining = durationTicks - age;
double fadeOut = smoothStep(remaining / FIELD_TRANSITION_TICKS);
return Math.min(fadeIn, fadeOut);
}
public static double effectiveFieldRate(double fieldRate, int age, int durationTicks) {
double strength = fieldStrength(age, durationTicks);
return NORMAL_RATE + (clampRate(fieldRate) - NORMAL_RATE) * strength;
}
}
@@ -103,6 +103,7 @@ public class ElythiaBiomeSource extends BiomeSource {
private final Holder<Biome> glimmeringPoolsHolder;
private final Holder<Biome> ouranwoodForestHolder;
private final Holder<Biome> cloudSeaHolder;
private final Holder<Biome> firstConfiguredHolder;
public ElythiaBiomeSource(Climate.ParameterList<Holder<Biome>> parameters, int molewormCavesMaxY, int surfaceBiomeSampleY, int oceanMaxY, int seaLevel, int cloudSeaMinY) {
this.parameters = parameters;
@@ -145,6 +146,10 @@ public class ElythiaBiomeSource extends BiomeSource {
.filter(h -> h.is(CLOUD_SEA))
.findFirst()
.orElse(null);
this.firstConfiguredHolder = parameters.values().stream()
.map(Pair::getSecond)
.findFirst()
.orElse(null);
}
private Climate.ParameterList<Holder<Biome>> parameters() { return this.parameters; }
@@ -178,6 +183,9 @@ public class ElythiaBiomeSource extends BiomeSource {
}
Holder<Biome> biome = this.delegate.getNoiseBiome(x, y, z, sampler);
if (biome == null) {
return resolveConfiguredFallback(x, y, z, sampler);
}
// Defensive: cloud_sea's multi-noise parameter point is reserved far off in "offset"
// so it shouldn't win nearest-neighbor on its own, but if it ever does for a query
@@ -196,7 +204,7 @@ public class ElythiaBiomeSource extends BiomeSource {
if (biome.is(MOLEWORM_CAVES) || biome.is(ELYTHIA_LUSH_CAVES)) return biome;
Holder<Biome> seaLevelBiome = this.delegate.getNoiseBiome(x, this.seaLevelQuartY, z, sampler);
if (isOceanBiome(seaLevelBiome)) return seaLevelBiome;
return oceanHolder != null ? oceanHolder : seaLevelBiome;
return oceanHolder != null ? oceanHolder : nullToConfiguredFallback(seaLevelBiome, x, y, z, sampler);
}
if (y <= this.seaLevelQuartY && oceanHolder != null && !allowedBelowSeaLevel(biome)) {
return oceanHolder;
@@ -221,17 +229,17 @@ public class ElythiaBiomeSource extends BiomeSource {
}
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);
return biome != null && (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);
return biome != null && (biome.is(ELYTHIA_OCEAN) || biome.is(ELYTHIA_CORAL_SPIKES));
}
private static boolean isOuranwoodBiome(Holder<Biome> biome) {
return biome.is(OURANWOOD_FOREST) || biome.is(SPARSE_OURANWOOD_FOREST)
|| biome.is(FUNGAL_OURANWOOD_FOREST);
return biome != null && (biome.is(OURANWOOD_FOREST) || biome.is(SPARSE_OURANWOOD_FOREST)
|| biome.is(FUNGAL_OURANWOOD_FOREST));
}
private static boolean isGlimmeringPoolsCandidate(Holder<Biome> biome, Climate.TargetPoint target, int x, int z) {
@@ -311,6 +319,7 @@ public class ElythiaBiomeSource extends BiomeSource {
}
}
}
result = nullToConfiguredFallback(result, x, this.surfaceBiomeSampleQuartY, z, sampler);
cache.key = key;
cache.value = result;
@@ -336,6 +345,7 @@ public class ElythiaBiomeSource extends BiomeSource {
}
result = resolved != null ? resolved : (defaultLandHolder != null ? defaultLandHolder : result);
}
result = nullToConfiguredFallback(result, x, this.surfaceBiomeSampleQuartY, z, sampler);
cache.key = key;
cache.value = result;
@@ -344,6 +354,9 @@ public class ElythiaBiomeSource extends BiomeSource {
@SafeVarargs
private static boolean isExcluded(Holder<Biome> biome, ResourceKey<Biome>... excluded) {
if (biome == null) {
return true;
}
for (ResourceKey<Biome> key : excluded) {
if (biome.is(key)) return true;
}
@@ -351,11 +364,30 @@ public class ElythiaBiomeSource extends BiomeSource {
}
private static boolean isOceanOrCave(Holder<Biome> biome) {
return isOceanBiome(biome) || biome.is(ELYTHIA_BEACH)
return biome == null || isOceanBiome(biome) || biome.is(ELYTHIA_BEACH)
|| biome.is(MOLEWORM_CAVES) || biome.is(ELYTHIA_LUSH_CAVES)
|| biome.is(GLIMMERING_POOLS) || biome.is(CLOUD_SEA);
}
private Holder<Biome> nullToConfiguredFallback(Holder<Biome> biome, int x, int y, int z, Climate.Sampler sampler) {
return biome != null ? biome : resolveConfiguredFallback(x, y, z, sampler);
}
private Holder<Biome> resolveConfiguredFallback(int x, int y, int z, Climate.Sampler sampler) {
if (y <= this.oceanMaxQuartY && this.oceanHolder != null) {
Climate.TargetPoint target = sampler.sample(x, this.seaLevelQuartY, z);
if (target.continentalness() < OCEAN_CONTINENTALNESS_THRESHOLD) {
return this.oceanHolder;
}
}
if (this.defaultLandHolder != null) return this.defaultLandHolder;
if (this.ouranwoodForestHolder != null) return this.ouranwoodForestHolder;
if (this.glimmeringPoolsHolder != null) return this.glimmeringPoolsHolder;
if (this.oceanHolder != null) return this.oceanHolder;
if (this.cloudSeaHolder != null) return this.cloudSeaHolder;
return this.firstConfiguredHolder;
}
@Override
public void addDebugInfo(java.util.List<String> debug, net.minecraft.core.BlockPos pos, Climate.Sampler sampler) {
try {
@@ -8,7 +8,14 @@
"location": "antarchy:kings_tree/kings_tree2",
"processors": "antarchy:generic_processor",
"projection": "rigid",
"element_type": "minecraft:single_pool_element"
"enhanced_terrain_adaptation": {
"type": "integrated_api:custom",
"carves": true,
"beards": true,
"kernel_size": 35,
"kernel_distance": 35
},
"element_type": "integrated_api:integrated_api_single_pool_element"
}
}
]
@@ -8,7 +8,14 @@
"location": "antarchy:kings_tree/kings_tree3",
"processors": "antarchy:generic_processor",
"projection": "rigid",
"element_type": "minecraft:single_pool_element"
"enhanced_terrain_adaptation": {
"type": "integrated_api:custom",
"carves": true,
"beards": true,
"kernel_size": 35,
"kernel_distance": 35
},
"element_type": "integrated_api:integrated_api_single_pool_element"
}
}
]
@@ -8,7 +8,14 @@
"location": "antarchy:kings_tree/kings_tree4",
"processors": "antarchy:generic_processor",
"projection": "rigid",
"element_type": "minecraft:single_pool_element"
"enhanced_terrain_adaptation": {
"type": "integrated_api:custom",
"carves": true,
"beards": true,
"kernel_size": 35,
"kernel_distance": 35
},
"element_type": "integrated_api:integrated_api_single_pool_element"
}
}
]
@@ -11,7 +11,7 @@
"enhanced_terrain_adaptation": {
"type": "integrated_api:custom",
"carves": true,
"beards": false,
"beards": true,
"kernel_size": 35,
"kernel_distance": 35
},