new king sounds

worm sizes
performance improvements
This commit is contained in:
craisinlord committed 2026-09-09 22:55:40 -05:00
1 parent 89d41dd7e1
commit f4f4e0c717
96 files changed
+2039 -470

No files matched your search

@@ -468,6 +468,9 @@ public final class AntarchySettings {
private static double molevoreBreakRange = 1.8D; private static double molevoreBreakRange = 1.8D;
private static double molevoreBreakVerticalRange = 2.2D; private static double molevoreBreakVerticalRange = 2.2D;
private static double molevoreBreakHalfWidth = 1.1D; private static double molevoreBreakHalfWidth = 1.1D;
private static double jumpyBugLatchDamage = 2.0D;
private static double waterCannonCooldownSeconds = 4.0D;
private static double toreterrorRangedWaterBombChance = 0.35D;
private static double nightmareHealth = 380.0D; private static double nightmareHealth = 380.0D;
private static double nightmareAttackDamage = 16.0D; private static double nightmareAttackDamage = 16.0D;
private static double nightmareMovementSpeed = 0.28D; private static double nightmareMovementSpeed = 0.28D;
@@ -2715,6 +2718,21 @@ public final class AntarchySettings {
public static double molevoreAttackDamage() { return molevoreAttackDamage; } public static double molevoreAttackDamage() { return molevoreAttackDamage; }
public static void setMolevoreAttackDamage(double value) { molevoreAttackDamage = value; } public static void setMolevoreAttackDamage(double value) { molevoreAttackDamage = value; }
public static double molevoreMovementSpeed() { return molevoreMovementSpeed; }
public static void setMolevoreMovementSpeed(double value) { molevoreMovementSpeed = value; }
public static double molevoreArmor() { return molevoreArmor; }
public static void setMolevoreArmor(double value) { molevoreArmor = value; }
public static double molevoreKnockbackResistance() { return molevoreKnockbackResistance; }
public static void setMolevoreKnockbackResistance(double value) { molevoreKnockbackResistance = value; }
public static double molevoreFollowRange() { return molevoreFollowRange; }
public static void setMolevoreFollowRange(double value) { molevoreFollowRange = value; }
public static int molevoreXpReward() { return molevoreXpReward; }
public static void setMolevoreXpReward(int value) { molevoreXpReward = value; }
public static int molevoreSpinTicks() { return molevoreSpinTicks; } public static int molevoreSpinTicks() { return molevoreSpinTicks; }
public static void setMolevoreSpinTicks(int value) { molevoreSpinTicks = value; } public static void setMolevoreSpinTicks(int value) { molevoreSpinTicks = value; }
@@ -2985,7 +3003,10 @@ public final class AntarchySettings {
public static int bossMagicWardDurationTicks() { return bossMagicWardDurationTicks; } public static int bossMagicWardDurationTicks() { return bossMagicWardDurationTicks; }
public static int bossMagicWardTriggerBreaches() { return bossMagicWardTriggerBreaches; } public static int bossMagicWardTriggerBreaches() { return bossMagicWardTriggerBreaches; }
public static java.util.List<String> magicBurstDamageNamespaces() { return magicBurstDamageNamespaces; } public static java.util.List<String> magicBurstDamageNamespaces() { return magicBurstDamageNamespaces; }
public static double jumpyBugLatchDamage() { return 2.0D; } public static double jumpyBugLatchDamage() { return jumpyBugLatchDamage; }
public static double waterCannonCooldownSeconds() { return 4.0D; } public static void setJumpyBugLatchDamage(double value) { jumpyBugLatchDamage = value; }
public static double toreterrorRangedWaterBombChance() { return 0.35D; } public static double waterCannonCooldownSeconds() { return waterCannonCooldownSeconds; }
public static void setWaterCannonCooldownSeconds(double value) { waterCannonCooldownSeconds = value; }
public static double toreterrorRangedWaterBombChance() { return toreterrorRangedWaterBombChance; }
public static void setToreterrorRangedWaterBombChance(double value) { toreterrorRangedWaterBombChance = value; }
} }
@@ -60,6 +60,7 @@ public class DimensionalTearEntity extends Entity implements GeoEntity {
private static final double PORTAL_RADIUS = 1.35D; private static final double PORTAL_RADIUS = 1.35D;
private static final double EXIT_OFFSET = 2.4D; private static final double EXIT_OFFSET = 2.4D;
private static final Map<UUID, Long> TELEPORT_COOLDOWNS = new HashMap<>(); private static final Map<UUID, Long> TELEPORT_COOLDOWNS = new HashMap<>();
private static long lastTeleportCooldownCleanupTick = Long.MIN_VALUE;
private final AnimatableInstanceCache geoCache = GeckoLibUtil.createInstanceCache(this); private final AnimatableInstanceCache geoCache = GeckoLibUtil.createInstanceCache(this);
private UUID linkedTearId; private UUID linkedTearId;
@@ -118,6 +119,7 @@ public class DimensionalTearEntity extends Entity implements GeoEntity {
} }
this.ageTicks++; this.ageTicks++;
cleanupTeleportCooldowns(this.level().getGameTime());
if (this.ageTicks >= this.lifetimeTicks - COLLAPSE_WARNING_TICKS) { if (this.ageTicks >= this.lifetimeTicks - COLLAPSE_WARNING_TICKS) {
this.setTearState(TearState.COLLAPSING); this.setTearState(TearState.COLLAPSING);
} }
@@ -235,6 +237,15 @@ public class DimensionalTearEntity extends Entity implements GeoEntity {
this.level().playSound(null, this.blockPosition(), SoundEvents.ENDERMAN_TELEPORT, SoundSource.NEUTRAL, 0.7F, 0.65F + this.random.nextFloat() * 0.2F); this.level().playSound(null, this.blockPosition(), SoundEvents.ENDERMAN_TELEPORT, SoundSource.NEUTRAL, 0.7F, 0.65F + this.random.nextFloat() * 0.2F);
} }
private static void cleanupTeleportCooldowns(long gameTime) {
if (gameTime <= lastTeleportCooldownCleanupTick
|| gameTime - lastTeleportCooldownCleanupTick < TELEPORT_COOLDOWN_TICKS * 4L) {
return;
}
lastTeleportCooldownCleanupTick = gameTime;
TELEPORT_COOLDOWNS.entrySet().removeIf(entry -> entry.getValue() <= gameTime);
}
private Vec3 exitPosition() { private Vec3 exitPosition() {
Vec3 facing = this.getViewVector(1.0F); Vec3 facing = this.getViewVector(1.0F);
if (facing.lengthSqr() < 1.0E-4D) { if (facing.lengthSqr() < 1.0E-4D) {
@@ -1,11 +1,15 @@
package com.craisinlord.antarchy.content.item; package com.craisinlord.antarchy.content.item;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import net.minecraft.world.level.Level; import net.minecraft.world.level.Level;
import net.minecraft.ChatFormatting; import net.minecraft.ChatFormatting;
import net.minecraft.network.chat.Component; import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.item.Item; import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.SwordItem; import net.minecraft.world.item.SwordItem;
@@ -21,6 +25,7 @@ public class BloodCrystalKatanaItem extends SwordItem {
} }
private static TrailCallback trailCallback = (player, durationTicks) -> {}; private static TrailCallback trailCallback = (player, durationTicks) -> {};
private static final Map<UUID, Long> DASH_UNTIL_TICK = new ConcurrentHashMap<>();
private final Tier tier; private final Tier tier;
private final int attackDamage; private final int attackDamage;
@@ -56,9 +61,23 @@ public class BloodCrystalKatanaItem extends SwordItem {
player.hasImpulse = true; player.hasImpulse = true;
player.hurtMarked = true; player.hurtMarked = true;
trailCallback.trigger(player, AntarchySettings.bloodCrystalKatanaTrailDurationTicks()); trailCallback.trigger(player, AntarchySettings.bloodCrystalKatanaTrailDurationTicks());
long now = player.level().getGameTime();
DASH_UNTIL_TICK.put(player.getUUID(), now + AntarchySettings.bloodCrystalKatanaTrailDurationTicks());
return result; return result;
} }
public static boolean isDashing(Entity entity) {
if (!(entity instanceof ServerPlayer player)) {
return false;
}
Long until = DASH_UNTIL_TICK.get(player.getUUID());
if (until == null || player.level().getGameTime() >= until) {
DASH_UNTIL_TICK.remove(player.getUUID());
return false;
}
return true;
}
@Override @Override
public void appendHoverText(ItemStack stack, Level level, List<Component> tooltipComponents, TooltipFlag tooltipFlag) { public void appendHoverText(ItemStack stack, Level level, List<Component> tooltipComponents, TooltipFlag tooltipFlag) {
tooltipComponents.add(Component.translatable("tooltip.antarchy.blood_crystal_katana.launch").withStyle(ChatFormatting.RED)); tooltipComponents.add(Component.translatable("tooltip.antarchy.blood_crystal_katana.launch").withStyle(ChatFormatting.RED));
@@ -16,6 +16,14 @@ import net.minecraft.world.level.biome.Climate;
import net.minecraft.world.level.biome.MultiNoiseBiomeSource; import net.minecraft.world.level.biome.MultiNoiseBiomeSource;
public class ElythiaBiomeSource extends BiomeSource { public class ElythiaBiomeSource extends BiomeSource {
public static final Codec<ElythiaBiomeSource> CODEC = RecordCodecBuilder.create(instance -> instance.group(
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("sea_level", 78).forGetter(ElythiaBiomeSource::seaLevel),
Codec.INT.optionalFieldOf("cloud_sea_min_y", 360).forGetter(ElythiaBiomeSource::cloudSeaMinY)
).apply(instance, ElythiaBiomeSource::new));
private static final ResourceKey<Biome> MOLEWORM_CAVES = ResourceKey.create( private static final ResourceKey<Biome> MOLEWORM_CAVES = ResourceKey.create(
Registries.BIOME, Registries.BIOME,
new ResourceLocation(Antarchy.MODID, "moleworm_caves") new ResourceLocation(Antarchy.MODID, "moleworm_caves")
@@ -17,6 +17,12 @@ import net.minecraft.world.level.biome.Climate;
import net.minecraft.world.level.biome.MultiNoiseBiomeSource; import net.minecraft.world.level.biome.MultiNoiseBiomeSource;
public final class ThoraxisBiomeSource extends BiomeSource { public final class ThoraxisBiomeSource extends BiomeSource {
public static final Codec<ThoraxisBiomeSource> CODEC = RecordCodecBuilder.create(instance -> instance.group(
MultiNoiseBiomeSource.DIRECT_CODEC.forGetter(ThoraxisBiomeSource::parameters),
Codec.INT.optionalFieldOf("dream_dunes_max_y", 160).forGetter(ThoraxisBiomeSource::dreamDunesMaxY),
Codec.INT.optionalFieldOf("umbral_hills_max_y", 160).forGetter(ThoraxisBiomeSource::umbralHillsMaxY),
Codec.INT.optionalFieldOf("lucid_pools_min_y", 116).forGetter(ThoraxisBiomeSource::lucidPoolsMinY)
).apply(instance, ThoraxisBiomeSource::new));
private static final int REGION_NOISE_SCALE = 96; private static final int REGION_NOISE_SCALE = 96;
private static final int REGION_DETAIL_SCALE = 41; private static final int REGION_DETAIL_SCALE = 41;
private static final int VERTICAL_NOISE_SCALE = 8; private static final int VERTICAL_NOISE_SCALE = 8;
@@ -254,6 +254,7 @@ public final class AntarchySettings {
private static double queenBlackHoleRadius = 14.0D; private static double queenBlackHoleRadius = 14.0D;
private static int queenBlackHoleActiveTicks = 120; private static int queenBlackHoleActiveTicks = 120;
private static double queenBlackHolePullStrength = 0.16D; private static double queenBlackHolePullStrength = 0.16D;
private static int queenBlackHoleBlockSuctionCap = 160;
private static double queenFinalTimeFieldRadius = 20.0D; private static double queenFinalTimeFieldRadius = 20.0D;
private static double queenFinalTimeFieldRate = 0.05D; private static double queenFinalTimeFieldRate = 0.05D;
private static int queenFinalTimeFieldDurationTicks = 45; private static int queenFinalTimeFieldDurationTicks = 45;
@@ -273,7 +274,7 @@ public final class AntarchySettings {
private static double royalBossKnockbackResistance = 1.0D; private static double royalBossKnockbackResistance = 1.0D;
private static double royalBossStepHeight = 3.0D; private static double royalBossStepHeight = 3.0D;
private static double royalBossMaxSingleHitDamage = 250.0D; private static double royalBossMaxSingleHitDamage = 250.0D;
private static double royalBossBiteReach = 10.0D; private static double royalBossBiteReach = 12.0D;
private static double royalBossBiteDamageMultiplier = 1.6D; private static double royalBossBiteDamageMultiplier = 1.6D;
private static int royalBossBiteCooldownTicks = 45; private static int royalBossBiteCooldownTicks = 45;
private static boolean basiliskPetrifyingGazeEnabled = true; private static boolean basiliskPetrifyingGazeEnabled = true;
@@ -558,6 +559,10 @@ public final class AntarchySettings {
private static double queenBeamTracking = 0.1D; private static double queenBeamTracking = 0.1D;
private static double queenBeamTerrainRadius = 5.0D; private static double queenBeamTerrainRadius = 5.0D;
private static int queenBeamTerrainCap = 192; private static int queenBeamTerrainCap = 192;
private static int queenBeamPhaseOneCap = 1;
private static int queenBeamPhaseTwoCap = 2;
private static int queenBeamPhaseThreeCap = 3;
private static double queenBeamPhaseThreeMaxVolleyChance = 0.2D;
private static double kingBeamDamage = 50.0D; private static double kingBeamDamage = 50.0D;
private static double kingBeamRange = 100.0D; private static double kingBeamRange = 100.0D;
private static double kingBeamMinimumRange = 32.0D; private static double kingBeamMinimumRange = 32.0D;
@@ -2051,6 +2056,8 @@ public final class AntarchySettings {
public static void setQueenBlackHoleActiveTicks(int v) { queenBlackHoleActiveTicks = v; } public static void setQueenBlackHoleActiveTicks(int v) { queenBlackHoleActiveTicks = v; }
public static double queenBlackHolePullStrength() { return queenBlackHolePullStrength; } public static double queenBlackHolePullStrength() { return queenBlackHolePullStrength; }
public static void setQueenBlackHolePullStrength(double v) { queenBlackHolePullStrength = v; } public static void setQueenBlackHolePullStrength(double v) { queenBlackHolePullStrength = v; }
public static int queenBlackHoleBlockSuctionCap() { return queenBlackHoleBlockSuctionCap; }
public static void setQueenBlackHoleBlockSuctionCap(int v) { queenBlackHoleBlockSuctionCap = v; }
public static double queenFinalTimeFieldRadius() { return queenFinalTimeFieldRadius; } public static double queenFinalTimeFieldRadius() { return queenFinalTimeFieldRadius; }
public static void setQueenFinalTimeFieldRadius(double v) { queenFinalTimeFieldRadius = v; } public static void setQueenFinalTimeFieldRadius(double v) { queenFinalTimeFieldRadius = v; }
public static double queenFinalTimeFieldRate() { return queenFinalTimeFieldRate; } public static double queenFinalTimeFieldRate() { return queenFinalTimeFieldRate; }
@@ -2910,6 +2917,14 @@ public final class AntarchySettings {
public static void setQueenBeamTerrainRadius(double v) { queenBeamTerrainRadius = v; } public static void setQueenBeamTerrainRadius(double v) { queenBeamTerrainRadius = v; }
public static int queenBeamTerrainCap() { return queenBeamTerrainCap; } public static int queenBeamTerrainCap() { return queenBeamTerrainCap; }
public static void setQueenBeamTerrainCap(int v) { queenBeamTerrainCap = v; } public static void setQueenBeamTerrainCap(int v) { queenBeamTerrainCap = v; }
public static int queenBeamPhaseOneCap() { return queenBeamPhaseOneCap; }
public static void setQueenBeamPhaseOneCap(int v) { queenBeamPhaseOneCap = v; }
public static int queenBeamPhaseTwoCap() { return queenBeamPhaseTwoCap; }
public static void setQueenBeamPhaseTwoCap(int v) { queenBeamPhaseTwoCap = v; }
public static int queenBeamPhaseThreeCap() { return queenBeamPhaseThreeCap; }
public static void setQueenBeamPhaseThreeCap(int v) { queenBeamPhaseThreeCap = v; }
public static double queenBeamPhaseThreeMaxVolleyChance() { return queenBeamPhaseThreeMaxVolleyChance; }
public static void setQueenBeamPhaseThreeMaxVolleyChance(double v) { queenBeamPhaseThreeMaxVolleyChance = v; }
public static double kingBeamDamage() { return kingBeamDamage; } public static double kingBeamDamage() { return kingBeamDamage; }
public static void setKingBeamDamage(double v) { kingBeamDamage = v; } public static void setKingBeamDamage(double v) { kingBeamDamage = v; }
public static double kingBeamRange() { return kingBeamRange; } public static double kingBeamRange() { return kingBeamRange; }
@@ -101,6 +101,8 @@ public final class AntarchyObjects {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public static Supplier<EntityType<com.craisinlord.antarchy.content.entity.royal.RoyalBlackHoleEntity>> ROYAL_BLACK_HOLE = (Supplier<EntityType<com.craisinlord.antarchy.content.entity.royal.RoyalBlackHoleEntity>>) UNBOUND; public static Supplier<EntityType<com.craisinlord.antarchy.content.entity.royal.RoyalBlackHoleEntity>> ROYAL_BLACK_HOLE = (Supplier<EntityType<com.craisinlord.antarchy.content.entity.royal.RoyalBlackHoleEntity>>) UNBOUND;
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public static Supplier<EntityType<com.craisinlord.antarchy.content.entity.royal.RoyalAssailantBlackHoleEntity>> ROYAL_ASSAILANT_BLACK_HOLE = (Supplier<EntityType<com.craisinlord.antarchy.content.entity.royal.RoyalAssailantBlackHoleEntity>>) UNBOUND;
@SuppressWarnings("unchecked")
public static Supplier<Block> VORTEX_LENS = (Supplier<Block>) UNBOUND; public static Supplier<Block> VORTEX_LENS = (Supplier<Block>) UNBOUND;
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public static Supplier<BlockEntityType<VortexLensBlockEntity>> VORTEX_LENS_BLOCK_ENTITY = (Supplier<BlockEntityType<VortexLensBlockEntity>>) UNBOUND; public static Supplier<BlockEntityType<VortexLensBlockEntity>> VORTEX_LENS_BLOCK_ENTITY = (Supplier<BlockEntityType<VortexLensBlockEntity>>) UNBOUND;
@@ -691,6 +693,10 @@ public final class AntarchyObjects {
ROYAL_BLACK_HOLE = supplier; ROYAL_BLACK_HOLE = supplier;
} }
public static void setRoyalAssailantBlackHole(Supplier<EntityType<com.craisinlord.antarchy.content.entity.royal.RoyalAssailantBlackHoleEntity>> supplier) {
ROYAL_ASSAILANT_BLACK_HOLE = supplier;
}
public static void setVortexLens(Supplier<Block> supplier) { public static void setVortexLens(Supplier<Block> supplier) {
VORTEX_LENS = supplier; VORTEX_LENS = supplier;
} }
@@ -213,6 +213,9 @@ public final class AntarchySoundEvents {
public static Supplier<SoundEvent> PRINCESS_SHOOT = (Supplier<SoundEvent>) UNBOUND; public static Supplier<SoundEvent> PRINCESS_SHOOT = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> PRINCESS_FLY = (Supplier<SoundEvent>) UNBOUND; public static Supplier<SoundEvent> PRINCESS_FLY = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> PRINCESS_STEP = (Supplier<SoundEvent>) UNBOUND; public static Supplier<SoundEvent> PRINCESS_STEP = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> KING_DECREE = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> KING_SUCCESS = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> KING_JUDGEMENT = (Supplier<SoundEvent>) UNBOUND;
private AntarchySoundEvents() { private AntarchySoundEvents() {
} }
@@ -417,7 +420,10 @@ public final class AntarchySoundEvents {
Supplier<SoundEvent> princessBite, Supplier<SoundEvent> princessBite,
Supplier<SoundEvent> princessShoot, Supplier<SoundEvent> princessShoot,
Supplier<SoundEvent> princessFly, Supplier<SoundEvent> princessFly,
Supplier<SoundEvent> princessStep Supplier<SoundEvent> princessStep,
Supplier<SoundEvent> kingDecree,
Supplier<SoundEvent> kingSuccess,
Supplier<SoundEvent> kingJudgement
) { ) {
SQUIDZOOKA_FIRE = squidzookaFire; SQUIDZOOKA_FIRE = squidzookaFire;
SHRINK_RAY_CHARGE = shrinkRayCharge; SHRINK_RAY_CHARGE = shrinkRayCharge;
@@ -619,6 +625,9 @@ public final class AntarchySoundEvents {
PRINCESS_SHOOT = princessShoot; PRINCESS_SHOOT = princessShoot;
PRINCESS_FLY = princessFly; PRINCESS_FLY = princessFly;
PRINCESS_STEP = princessStep; PRINCESS_STEP = princessStep;
KING_DECREE = kingDecree;
KING_SUCCESS = kingSuccess;
KING_JUDGEMENT = kingJudgement;
} }
public static void bindDimensionalTearSounds( public static void bindDimensionalTearSounds(
@@ -85,6 +85,7 @@ public final class AntarchyTags {
public static final TagKey<EntityType<?>> DIMENSIONAL_TEAR_COMMON_SPAWNS = TagKey.create(Registries.ENTITY_TYPE, id("dimensional_tear_common_spawns")); public static final TagKey<EntityType<?>> DIMENSIONAL_TEAR_COMMON_SPAWNS = TagKey.create(Registries.ENTITY_TYPE, id("dimensional_tear_common_spawns"));
public static final TagKey<EntityType<?>> DIMENSIONAL_TEAR_RARE_SPAWNS = TagKey.create(Registries.ENTITY_TYPE, id("dimensional_tear_rare_spawns")); public static final TagKey<EntityType<?>> DIMENSIONAL_TEAR_RARE_SPAWNS = TagKey.create(Registries.ENTITY_TYPE, id("dimensional_tear_rare_spawns"));
public static final TagKey<EntityType<?>> TIME_DILATION_IMMUNE = TagKey.create(Registries.ENTITY_TYPE, id("time_dilation_immune")); public static final TagKey<EntityType<?>> TIME_DILATION_IMMUNE = TagKey.create(Registries.ENTITY_TYPE, id("time_dilation_immune"));
public static final TagKey<EntityType<?>> QUEEN_DOES_NOT_ATTACK = TagKey.create(Registries.ENTITY_TYPE, id("queen_does_not_attack"));
public static final TagKey<EntityType<?>> COMMANDED_BLACKLIST = TagKey.create(Registries.ENTITY_TYPE, id("commanded_blacklist")); public static final TagKey<EntityType<?>> COMMANDED_BLACKLIST = TagKey.create(Registries.ENTITY_TYPE, id("commanded_blacklist"));
public static final TagKey<EntityType<?>> DILATED_BLACKLIST = TagKey.create(Registries.ENTITY_TYPE, id("dilated_blacklist")); public static final TagKey<EntityType<?>> DILATED_BLACKLIST = TagKey.create(Registries.ENTITY_TYPE, id("dilated_blacklist"));
@@ -170,7 +170,7 @@ public abstract class RoyalEggBlock extends BaseEntityBlock {
return; return;
} }
mount.moveTo(pos.getX() + 0.5D, pos.getY(), pos.getZ() + 0.5D, level.random.nextFloat() * 360.0F, 0.0F); mount.moveTo(pos.getX() + 0.5D, pos.getY(), pos.getZ() + 0.5D, level.random.nextFloat() * 360.0F, 0.0F);
mount.setBaby(true); mount.setGrowthProgress(0.0F);
mount.finalizeSpawn(level, level.getCurrentDifficultyAt(pos), MobSpawnType.BREEDING, null); mount.finalizeSpawn(level, level.getCurrentDifficultyAt(pos), MobSpawnType.BREEDING, null);
if (placer != null) { if (placer != null) {
mount.setTame(true, true); mount.setTame(true, true);
@@ -17,6 +17,7 @@ public final class ClientTimeDilationTicker {
private static ClientLevel activeLevel; private static ClientLevel activeLevel;
private static final Map<ItemEntity, ItemSpinState> ITEM_SPIN_STATES = new WeakHashMap<>(); private static final Map<ItemEntity, ItemSpinState> ITEM_SPIN_STATES = new WeakHashMap<>();
private static final Map<LivingEntity, AttackAnimationState> ATTACK_ANIMATION_STATES = new WeakHashMap<>(); private static final Map<LivingEntity, AttackAnimationState> ATTACK_ANIMATION_STATES = new WeakHashMap<>();
private static final Map<LivingEntity, AnimationClockState> WALK_ANIMATION_STATES = new WeakHashMap<>();
private static final Map<Entity, AnimationClockState> ANIMATION_CLOCKS = new WeakHashMap<>(); private static final Map<Entity, AnimationClockState> ANIMATION_CLOCKS = new WeakHashMap<>();
private ClientTimeDilationTicker() { private ClientTimeDilationTicker() {
@@ -28,9 +29,12 @@ public final class ClientTimeDilationTicker {
activeFields = List.of(); activeFields = List.of();
ITEM_SPIN_STATES.clear(); ITEM_SPIN_STATES.clear();
ATTACK_ANIMATION_STATES.clear(); ATTACK_ANIMATION_STATES.clear();
WALK_ANIMATION_STATES.clear();
ANIMATION_CLOCKS.clear(); ANIMATION_CLOCKS.clear();
ContractionAfterimages.clear();
com.craisinlord.antarchy.content.time.TimeDilationApi.clearSyncedClientRates(); com.craisinlord.antarchy.content.time.TimeDilationApi.clearSyncedClientRates();
} }
ContractionAfterimages.tick(level);
} }
public static float dilateAnimationTime(Entity entity, float vanillaTime) { public static float dilateAnimationTime(Entity entity, float vanillaTime) {
@@ -87,6 +91,22 @@ public final class ClientTimeDilationTicker {
return rate >= TimeDilationMath.NORMAL_RATE ? amount : amount * (float) rate; return rate >= TimeDilationMath.NORMAL_RATE ? amount : amount * (float) rate;
} }
public static float dilateWalkPosition(LivingEntity entity, float vanillaPosition) {
double rate = com.craisinlord.antarchy.content.time.TimeDilationApi.getRate(entity);
if (Math.abs(rate - TimeDilationMath.NORMAL_RATE) < 0.001D) {
WALK_ANIMATION_STATES.remove(entity);
return vanillaPosition;
}
AnimationClockState state = WALK_ANIMATION_STATES.computeIfAbsent(entity,
ignored -> new AnimationClockState(vanillaPosition));
float delta = vanillaPosition - state.lastVanillaTime;
if (delta >= 0.0F) {
state.temporalTime += delta * (float) rate;
}
state.lastVanillaTime = vanillaPosition;
return state.temporalTime;
}
public static float dilateItemSpin(ItemEntity item, float vanillaSpin) { public static float dilateItemSpin(ItemEntity item, float vanillaSpin) {
double rate = com.craisinlord.antarchy.content.time.TimeDilationApi.getRate(item); double rate = com.craisinlord.antarchy.content.time.TimeDilationApi.getRate(item);
if (rate >= TimeDilationMath.NORMAL_RATE) { if (rate >= TimeDilationMath.NORMAL_RATE) {
@@ -5,19 +5,19 @@ import com.craisinlord.antarchy.content.entity.royal.RoyalBlackHoleEntity;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import software.bernie.geckolib.model.GeoModel; import software.bernie.geckolib.model.GeoModel;
public class RoyalBlackHoleModel extends GeoModel<RoyalBlackHoleEntity> { public class RoyalBlackHoleModel<T extends RoyalBlackHoleEntity> extends GeoModel<T> {
@Override @Override
public ResourceLocation getModelResource(RoyalBlackHoleEntity animatable) { public ResourceLocation getModelResource(T animatable) {
return ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "geo/black_hole.geo.json"); return ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "geo/black_hole.geo.json");
} }
@Override @Override
public ResourceLocation getTextureResource(RoyalBlackHoleEntity animatable) { public ResourceLocation getTextureResource(T animatable) {
return ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "textures/entity/queen/black_hole.png"); return ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "textures/entity/queen/black_hole.png");
} }
@Override @Override
public ResourceLocation getAnimationResource(RoyalBlackHoleEntity animatable) { public ResourceLocation getAnimationResource(T animatable) {
return ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "animations/black_hole.animation.json"); return ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "animations/black_hole.animation.json");
} }
} }
@@ -45,19 +45,14 @@ public class RoyalBossModel extends GeoModel<RoyalBossEntity> {
this.getBone(anchor).ifPresent(bone -> bone.setTrackingMatrices(true)); this.getBone(anchor).ifPresent(bone -> bone.setTrackingMatrices(true));
} }
RoyalHead.Slot slot = animatable.getRoyalBeamHeadSlot();
Vec3 beamEnd = animatable.getRoyalBeamEndPosition();
if (!animatable.isFiringRoyalBeam() || slot == null || beamEnd == null) {
return;
}
float partialTick = animationState.getPartialTick(); float partialTick = animationState.getPartialTick();
Vec3 origin = animatable.getRoyalBeamShootFrom(partialTick); for (RoyalHead.Slot slot : RoyalHead.Slot.values()) {
Vec3 beamEnd = animatable.getRoyalBeamEndPosition(slot);
if (!animatable.isFiringRoyalBeam(slot) || beamEnd == null) continue;
Vec3 origin = animatable.getRoyalBeamShootFrom(slot, partialTick);
Vec3 direction = beamEnd.subtract(origin); Vec3 direction = beamEnd.subtract(origin);
double horizontal = Math.sqrt(direction.x * direction.x + direction.z * direction.z); double horizontal = Math.sqrt(direction.x * direction.x + direction.z * direction.z);
if (direction.lengthSqr() < 1.0E-6D || horizontal < 1.0E-4D) { if (direction.lengthSqr() < 1.0E-6D || horizontal < 1.0E-4D) continue;
return;
}
float desiredYaw = (float) (Mth.atan2(-direction.x, direction.z) * Mth.RAD_TO_DEG); float desiredYaw = (float) (Mth.atan2(-direction.x, direction.z) * Mth.RAD_TO_DEG);
float bodyYaw = Mth.rotLerp(partialTick, animatable.yRotO, animatable.getYRot()); float bodyYaw = Mth.rotLerp(partialTick, animatable.yRotO, animatable.getYRot());
@@ -75,6 +70,7 @@ public class RoyalBossModel extends GeoModel<RoyalBossEntity> {
}); });
} }
} }
}
@Nullable @Nullable
public Vec3 getTrackedBeamAnchor(RoyalHead.Slot slot) { public Vec3 getTrackedBeamAnchor(RoyalHead.Slot slot) {
@@ -14,6 +14,9 @@ public class RoyalElementalProjectileModel extends GeoModel<RoyalElementalProjec
@Override @Override
public ResourceLocation getTextureResource(RoyalElementalProjectileEntity animatable) { public ResourceLocation getTextureResource(RoyalElementalProjectileEntity animatable) {
if (animatable.isDreamFireball()) {
return ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "textures/entity/queen/dream_fire_ball.png");
}
return ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, return ResourceLocation.fromNamespaceAndPath(Antarchy.MODID,
"textures/entity/king/" + (animatable.isIceball() ? "ice_ball" : "fire_ball") + ".png"); "textures/entity/king/" + (animatable.isIceball() ? "ice_ball" : "fire_ball") + ".png");
} }
@@ -6,22 +6,40 @@ import net.minecraft.resources.ResourceLocation;
import software.bernie.geckolib.model.GeoModel; import software.bernie.geckolib.model.GeoModel;
public class WormModel extends GeoModel<WormEntity> { public class WormModel extends GeoModel<WormEntity> {
private static final ResourceLocation MODEL = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "geo/worm_large.geo.json"); private static final ResourceLocation SMALL_MODEL = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "geo/worm_small.geo.json");
private static final ResourceLocation TEXTURE = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "textures/entity/worm/worm_large.png"); private static final ResourceLocation MEDIUM_MODEL = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "geo/worm_medium.geo.json");
private static final ResourceLocation ANIMATION = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "animations/worm_large.animation.json"); private static final ResourceLocation LARGE_MODEL = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "geo/worm_large.geo.json");
private static final ResourceLocation SMALL_TEXTURE = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "textures/entity/worm/worm_small.png");
private static final ResourceLocation MEDIUM_TEXTURE = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "textures/entity/worm/worm_medium.png");
private static final ResourceLocation LARGE_TEXTURE = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "textures/entity/worm/worm_large.png");
private static final ResourceLocation SMALL_ANIMATION = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "animations/worm_small.animation.json");
private static final ResourceLocation MEDIUM_ANIMATION = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "animations/worm_medium.animation.json");
private static final ResourceLocation LARGE_ANIMATION = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "animations/worm_large.animation.json");
@Override @Override
public ResourceLocation getModelResource(WormEntity animatable) { public ResourceLocation getModelResource(WormEntity animatable) {
return MODEL; return switch (animatable.getGrowthStage()) {
case SMALL -> SMALL_MODEL;
case MEDIUM -> MEDIUM_MODEL;
case LARGE -> LARGE_MODEL;
};
} }
@Override @Override
public ResourceLocation getTextureResource(WormEntity animatable) { public ResourceLocation getTextureResource(WormEntity animatable) {
return TEXTURE; return switch (animatable.getGrowthStage()) {
case SMALL -> SMALL_TEXTURE;
case MEDIUM -> MEDIUM_TEXTURE;
case LARGE -> LARGE_TEXTURE;
};
} }
@Override @Override
public ResourceLocation getAnimationResource(WormEntity animatable) { public ResourceLocation getAnimationResource(WormEntity animatable) {
return ANIMATION; return switch (animatable.getGrowthStage()) {
case SMALL -> SMALL_ANIMATION;
case MEDIUM -> MEDIUM_ANIMATION;
case LARGE -> LARGE_ANIMATION;
};
} }
} }
@@ -19,7 +19,11 @@ public class RoyalBlackHoleRenderer extends GeoEntityRenderer<RoyalBlackHoleEnti
@Override @Override
public void render(RoyalBlackHoleEntity entity, float entityYaw, float partialTick, PoseStack poseStack, public void render(RoyalBlackHoleEntity entity, float entityYaw, float partialTick, PoseStack poseStack,
MultiBufferSource bufferSource, int packedLight) { MultiBufferSource bufferSource, int packedLight) {
poseStack.pushPose();
float scale = 1.5F * entity.visualGrowth();
poseStack.scale(scale, scale, scale);
super.render(entity, entityYaw, partialTick, poseStack, bufferSource, 0xF000F0); super.render(entity, entityYaw, partialTick, poseStack, bufferSource, 0xF000F0);
poseStack.popPose();
} }
@Override @Override
@@ -5,6 +5,7 @@ import com.craisinlord.antarchy.content.entity.royal.RoyalBossEntity;
import com.craisinlord.antarchy.content.entity.royal.KingEntity; import com.craisinlord.antarchy.content.entity.royal.KingEntity;
import com.craisinlord.antarchy.content.entity.royal.RoyalHead; import com.craisinlord.antarchy.content.entity.royal.RoyalHead;
import com.craisinlord.antarchy.content.entity.royal.beam.RoyalBeamElement; import com.craisinlord.antarchy.content.entity.royal.beam.RoyalBeamElement;
import com.craisinlord.antarchy.content.effect.RoyalEffectHooks;
import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer; import com.mojang.blaze3d.vertex.VertexConsumer;
import net.minecraft.client.renderer.MultiBufferSource; import net.minecraft.client.renderer.MultiBufferSource;
@@ -13,15 +14,23 @@ import net.minecraft.client.renderer.entity.EntityRendererProvider;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.Mth; import net.minecraft.util.Mth;
import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.Vec3;
import net.minecraft.client.renderer.texture.OverlayTexture;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import software.bernie.geckolib.cache.object.BakedGeoModel; import software.bernie.geckolib.cache.object.BakedGeoModel;
import software.bernie.geckolib.renderer.GeoEntityRenderer; import software.bernie.geckolib.renderer.GeoEntityRenderer;
import software.bernie.geckolib.renderer.layer.GeoRenderLayer;
import software.bernie.geckolib.util.Color; import software.bernie.geckolib.util.Color;
public class RoyalBossRenderer extends GeoEntityRenderer<RoyalBossEntity> { public class RoyalBossRenderer extends GeoEntityRenderer<RoyalBossEntity> {
private static final ResourceLocation QUEEN_BEAM_OUTER = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/queen/queen_beam_outer.png"); private static final ResourceLocation QUEEN_PURPLE_BEAM_OUTER = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/queen/queen_purple_beam_outer.png");
private static final ResourceLocation QUEEN_BEAM_INNER = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/queen/queen_beam_inner.png"); private static final ResourceLocation QUEEN_PURPLE_BEAM_INNER = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/queen/queen_purple_beam_inner.png");
private static final ResourceLocation QUEEN_BEAM_END = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/queen/queen_beam_end_1.png"); private static final ResourceLocation QUEEN_PURPLE_BEAM_END = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/queen/queen_purple_beam_end_1.png");
private static final ResourceLocation QUEEN_RED_BEAM_OUTER = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/queen/queen_red_beam_outer.png");
private static final ResourceLocation QUEEN_RED_BEAM_INNER = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/queen/queen_red_beam_inner.png");
private static final ResourceLocation QUEEN_RED_BEAM_END = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/queen/queen_red_beam_end_1.png");
private static final ResourceLocation QUEEN_BLACK_BEAM_OUTER = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/queen/queen_black_beam_outer.png");
private static final ResourceLocation QUEEN_BLACK_BEAM_INNER = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/queen/queen_black_beam_inner.png");
private static final ResourceLocation QUEEN_BLACK_BEAM_END = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/queen/queen_black_beam_end_1.png");
private static final ResourceLocation KING_FIRE_BEAM_OUTER = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/king/fire_beam_outer.png"); private static final ResourceLocation KING_FIRE_BEAM_OUTER = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/king/fire_beam_outer.png");
private static final ResourceLocation KING_FIRE_BEAM_INNER = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/king/fire_beam_inner.png"); private static final ResourceLocation KING_FIRE_BEAM_INNER = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/king/fire_beam_inner.png");
private static final ResourceLocation KING_FIRE_BEAM_END = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/king/fire_beam_end_1.png"); private static final ResourceLocation KING_FIRE_BEAM_END = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/king/fire_beam_end_1.png");
@@ -34,6 +43,45 @@ public class RoyalBossRenderer extends GeoEntityRenderer<RoyalBossEntity> {
public RoyalBossRenderer(EntityRendererProvider.Context context) { public RoyalBossRenderer(EntityRendererProvider.Context context) {
super(context, new RoyalBossModel()); super(context, new RoyalBossModel());
this.shadowRadius = 8.0F; this.shadowRadius = 8.0F;
this.addRenderLayer(new RoyalAfterimageLayer(this));
this.addRenderLayer(new TemporalContractionGeoLayer<>(this));
}
private static final class RoyalAfterimageLayer extends GeoRenderLayer<RoyalBossEntity> {
private RoyalAfterimageLayer(RoyalBossRenderer renderer) {
super(renderer);
}
@Override
public void render(PoseStack poseStack, RoyalBossEntity animatable, BakedGeoModel bakedModel, @Nullable RenderType renderType,
MultiBufferSource bufferSource, @Nullable VertexConsumer buffer, float partialTick,
int packedLight, int packedOverlay) {
if (!animatable.isRoyalAccelerated() && (RoyalEffectHooks.contractedHolder() == null
|| !animatable.hasEffect(RoyalEffectHooks.contractedHolder()))) return;
Vec3 movement = animatable.getDeltaMovement();
if (movement.lengthSqr() < 1.0E-5D) return;
RenderType afterimageType = RenderType.entityTranslucent(this.getRenderer().getTextureLocation(animatable));
VertexConsumer afterimageBuffer = bufferSource.getBuffer(afterimageType);
int[] colors = {0x66FF5F6F, 0x4DFF4D60, 0x33FF3B52, 0x1FFF2944};
for (int i = colors.length - 1; i >= 0; i--) {
poseStack.pushPose();
Vec3 offset = movement.scale(-(i + 1) * 3.0D / RoyalBossEntity.MODEL_RENDER_SCALE);
poseStack.translate(offset.x, offset.y, offset.z);
this.getRenderer().reRender(
bakedModel,
poseStack,
bufferSource,
animatable,
afterimageType,
afterimageBuffer,
partialTick,
0xF000F0,
OverlayTexture.NO_OVERLAY,
colors[i]
);
poseStack.popPose();
}
}
} }
@Override @Override
@@ -68,18 +116,22 @@ public class RoyalBossRenderer extends GeoEntityRenderer<RoyalBossEntity> {
public void render(RoyalBossEntity entity, float entityYaw, float partialTick, PoseStack poseStack, public void render(RoyalBossEntity entity, float entityYaw, float partialTick, PoseStack poseStack,
MultiBufferSource bufferSource, int packedLight) { MultiBufferSource bufferSource, int packedLight) {
super.render(entity, entityYaw, partialTick, poseStack, bufferSource, packedLight); super.render(entity, entityYaw, partialTick, poseStack, bufferSource, packedLight);
Vec3 end = entity.getRoyalBeamEndPosition(); for (RoyalHead.Slot slot : RoyalHead.Slot.values()) {
if (!entity.isFiringRoyalBeam() || end == null) { if (entity.isFiringRoyalBeam(slot) && entity.getRoyalBeamEndPosition(slot) != null) {
return; renderBeam(entity, slot, partialTick, poseStack, bufferSource);
} }
}
}
private void renderBeam(RoyalBossEntity entity, RoyalHead.Slot beamHead, float partialTick,
PoseStack poseStack, MultiBufferSource bufferSource) {
Vec3 end = entity.getRoyalBeamEndPosition(beamHead);
if (end == null) return;
double x = Mth.lerp(partialTick, entity.xo, entity.getX()); double x = Mth.lerp(partialTick, entity.xo, entity.getX());
double y = Mth.lerp(partialTick, entity.yo, entity.getY()); double y = Mth.lerp(partialTick, entity.yo, entity.getY());
double z = Mth.lerp(partialTick, entity.zo, entity.getZ()); double z = Mth.lerp(partialTick, entity.zo, entity.getZ());
RoyalHead.Slot beamHead = entity.getRoyalBeamHeadSlot(); Vec3 trackedAnchor = ((RoyalBossModel) this.getGeoModel()).getTrackedBeamAnchor(beamHead);
Vec3 trackedAnchor = beamHead == null Vec3 start = (trackedAnchor != null ? trackedAnchor : entity.getRoyalBeamShootFrom(beamHead, partialTick))
? null
: ((RoyalBossModel) this.getGeoModel()).getTrackedBeamAnchor(beamHead);
Vec3 start = (trackedAnchor != null ? trackedAnchor : entity.getRoyalBeamShootFrom(partialTick))
.subtract(x, y, z); .subtract(x, y, z);
Vec3 finish = end.subtract(x, y, z); Vec3 finish = end.subtract(x, y, z);
Vec3 axis = finish.subtract(start); Vec3 axis = finish.subtract(start);
@@ -95,9 +147,9 @@ public class RoyalBossRenderer extends GeoEntityRenderer<RoyalBossEntity> {
side1 = side1.normalize(); side1 = side1.normalize();
Vec3 side2 = dir.cross(side1).normalize(); Vec3 side2 = dir.cross(side1).normalize();
ResourceLocation outer = QUEEN_BEAM_OUTER; ResourceLocation outer = QUEEN_PURPLE_BEAM_OUTER;
ResourceLocation inner = QUEEN_BEAM_INNER; ResourceLocation inner = QUEEN_PURPLE_BEAM_INNER;
ResourceLocation endTexture = QUEEN_BEAM_END; ResourceLocation endTexture = QUEEN_PURPLE_BEAM_END;
if (entity instanceof KingEntity) { if (entity instanceof KingEntity) {
switch (entity.getRoyalBeamElement()) { switch (entity.getRoyalBeamElement()) {
case FIRE -> { case FIRE -> {
@@ -118,6 +170,21 @@ public class RoyalBossRenderer extends GeoEntityRenderer<RoyalBossEntity> {
case GENERIC -> { case GENERIC -> {
} }
} }
} else {
switch (entity.getRoyalBeamElement(beamHead)) {
case QUEEN_RED -> {
outer = QUEEN_RED_BEAM_OUTER;
inner = QUEEN_RED_BEAM_INNER;
endTexture = QUEEN_RED_BEAM_END;
}
case QUEEN_BLACK -> {
outer = QUEEN_BLACK_BEAM_OUTER;
inner = QUEEN_BLACK_BEAM_INNER;
endTexture = QUEEN_BLACK_BEAM_END;
}
default -> {
}
}
} }
float time = entity.tickCount + partialTick; float time = entity.tickCount + partialTick;
@@ -125,19 +192,19 @@ public class RoyalBossRenderer extends GeoEntityRenderer<RoyalBossEntity> {
float scroll = -time * BEAM_SCROLL_SPEED; float scroll = -time * BEAM_SCROLL_SPEED;
var pose = poseStack.last().pose(); var pose = poseStack.last().pose();
VertexConsumer outerBuffer = bufferSource.getBuffer(RenderType.entityTranslucent(outer)); VertexConsumer outerBuffer = bufferSource.getBuffer(RenderType.entityTranslucentEmissive(outer));
drawBeamPlane(outerBuffer, pose, start, finish, side1.scale(BEAM_OUTER_RADIUS), scroll, scroll + tiles); drawBeamPlane(outerBuffer, pose, start, finish, side1.scale(BEAM_OUTER_RADIUS), scroll, scroll + tiles);
drawBeamPlane(outerBuffer, pose, start, finish, side2.scale(BEAM_OUTER_RADIUS), scroll, scroll + tiles); drawBeamPlane(outerBuffer, pose, start, finish, side2.scale(BEAM_OUTER_RADIUS), scroll, scroll + tiles);
float innerScroll = -time * BEAM_SCROLL_SPEED * 1.6F; float innerScroll = -time * BEAM_SCROLL_SPEED * 1.6F;
VertexConsumer innerBuffer = bufferSource.getBuffer(RenderType.entityTranslucent(inner)); VertexConsumer innerBuffer = bufferSource.getBuffer(RenderType.entityTranslucentEmissive(inner));
drawBeamPlane(innerBuffer, pose, start, finish, side1.scale(BEAM_INNER_RADIUS), innerScroll, innerScroll + tiles); drawBeamPlane(innerBuffer, pose, start, finish, side1.scale(BEAM_INNER_RADIUS), innerScroll, innerScroll + tiles);
drawBeamPlane(innerBuffer, pose, start, finish, side2.scale(BEAM_INNER_RADIUS), innerScroll, innerScroll + tiles); drawBeamPlane(innerBuffer, pose, start, finish, side2.scale(BEAM_INNER_RADIUS), innerScroll, innerScroll + tiles);
float impactSize = 1.1F + 0.35F * Mth.sin(time * 0.6F); float impactSize = 1.1F + 0.35F * Mth.sin(time * 0.6F);
Vec3 camLeft = new Vec3(this.entityRenderDispatcher.camera.getLeftVector()); Vec3 camLeft = new Vec3(this.entityRenderDispatcher.camera.getLeftVector());
Vec3 camUp = new Vec3(this.entityRenderDispatcher.camera.getUpVector()); Vec3 camUp = new Vec3(this.entityRenderDispatcher.camera.getUpVector());
VertexConsumer endBuffer = bufferSource.getBuffer(RenderType.entityTranslucent(endTexture)); VertexConsumer endBuffer = bufferSource.getBuffer(RenderType.entityTranslucentEmissive(endTexture));
drawBillboard(endBuffer, pose, finish, camLeft.scale(impactSize), camUp.scale(impactSize)); drawBillboard(endBuffer, pose, finish, camLeft.scale(impactSize), camUp.scale(impactSize));
} }
@@ -6,8 +6,11 @@ import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer; import com.mojang.blaze3d.vertex.VertexConsumer;
import com.mojang.math.Axis; import com.mojang.math.Axis;
import net.minecraft.client.renderer.MultiBufferSource; import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.entity.EntityRendererProvider; import net.minecraft.client.renderer.entity.EntityRendererProvider;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.Mth; import net.minecraft.util.Mth;
import org.jetbrains.annotations.Nullable;
import software.bernie.geckolib.cache.object.BakedGeoModel; import software.bernie.geckolib.cache.object.BakedGeoModel;
import software.bernie.geckolib.renderer.GeoEntityRenderer; import software.bernie.geckolib.renderer.GeoEntityRenderer;
@@ -17,13 +20,21 @@ public class RoyalElementalProjectileRenderer extends GeoEntityRenderer<RoyalEle
this.shadowRadius = 0.0F; this.shadowRadius = 0.0F;
} }
@Override
public RenderType getRenderType(RoyalElementalProjectileEntity animatable, ResourceLocation texture,
@Nullable MultiBufferSource bufferSource, float partialTick) {
return animatable.isIceball()
? RenderType.entityCutoutNoCull(texture)
: RenderType.entityTranslucentEmissive(texture);
}
@Override @Override
public void preRender(PoseStack poseStack, RoyalElementalProjectileEntity animatable, BakedGeoModel model, public void preRender(PoseStack poseStack, RoyalElementalProjectileEntity animatable, BakedGeoModel model,
MultiBufferSource bufferSource, VertexConsumer buffer, boolean isReRender, MultiBufferSource bufferSource, VertexConsumer buffer, boolean isReRender,
float partialTick, int packedLight, int packedOverlay, int colour) { float partialTick, int packedLight, int packedOverlay, int colour) {
super.preRender(poseStack, animatable, model, bufferSource, buffer, isReRender, partialTick, super.preRender(poseStack, animatable, model, bufferSource, buffer, isReRender, partialTick,
packedLight, packedOverlay, colour); packedLight, packedOverlay, colour);
float scale = animatable.isIceball() ? 0.75F : 0.36F; float scale = animatable.isIceball() ? 0.75F : 0.72F;
poseStack.scale(scale, scale, scale); poseStack.scale(scale, scale, scale);
} }
@@ -34,5 +45,8 @@ public class RoyalElementalProjectileRenderer extends GeoEntityRenderer<RoyalEle
float pitch = Mth.lerp(partialTick, entity.xRotO, entity.getXRot()); float pitch = Mth.lerp(partialTick, entity.xRotO, entity.getXRot());
poseStack.mulPose(Axis.YP.rotationDegrees(180.0F - yaw)); poseStack.mulPose(Axis.YP.rotationDegrees(180.0F - yaw));
poseStack.mulPose(Axis.XP.rotationDegrees(-pitch)); poseStack.mulPose(Axis.XP.rotationDegrees(-pitch));
if (!entity.isIceball()) {
poseStack.mulPose(Axis.ZP.rotationDegrees(180.0F));
}
} }
} }
@@ -8,14 +8,31 @@ import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.entity.EntityRendererProvider; import net.minecraft.client.renderer.entity.EntityRendererProvider;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.Mth;
import net.minecraft.world.phys.Vec3;
import com.craisinlord.antarchy.content.entity.royal.PrinceEntity;
import com.craisinlord.antarchy.content.entity.royal.beam.RoyalBeamElement;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import software.bernie.geckolib.cache.object.BakedGeoModel; import software.bernie.geckolib.cache.object.BakedGeoModel;
import software.bernie.geckolib.renderer.GeoEntityRenderer; import software.bernie.geckolib.renderer.GeoEntityRenderer;
public class RoyalMountRenderer extends GeoEntityRenderer<RoyalMountEntity> { public class RoyalMountRenderer extends GeoEntityRenderer<RoyalMountEntity> {
private static final ResourceLocation QUEEN_OUTER = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/queen/queen_purple_beam_outer.png");
private static final ResourceLocation QUEEN_INNER = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/queen/queen_purple_beam_inner.png");
private static final ResourceLocation QUEEN_END = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/queen/queen_purple_beam_end_1.png");
private static final ResourceLocation KING_FIRE_OUTER = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/king/fire_beam_outer.png");
private static final ResourceLocation KING_FIRE_INNER = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/king/fire_beam_inner.png");
private static final ResourceLocation KING_FIRE_END = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/king/fire_beam_end_1.png");
private static final ResourceLocation KING_LIGHTNING_OUTER = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/king/lightning_beam_outer.png");
private static final ResourceLocation KING_LIGHTNING_INNER = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/king/lightning_beam_inner.png");
private static final ResourceLocation KING_LIGHTNING_END = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/king/lightning_beam_end_1.png");
private static final ResourceLocation KING_ICE_OUTER = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/king/ice_beam_outer.png");
private static final ResourceLocation KING_ICE_INNER = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/king/ice_beam_inner.png");
private static final ResourceLocation KING_ICE_END = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/king/ice_beam_end_1.png");
public RoyalMountRenderer(EntityRendererProvider.Context context) { public RoyalMountRenderer(EntityRendererProvider.Context context) {
super(context, new RoyalMountModel()); super(context, new RoyalMountModel());
this.shadowRadius = 1.1F; this.shadowRadius = 1.1F;
this.addRenderLayer(new TemporalContractionGeoLayer<>(this));
} }
@Override @Override
@@ -27,8 +44,64 @@ public class RoyalMountRenderer extends GeoEntityRenderer<RoyalMountEntity> {
public void preRender(PoseStack poseStack, RoyalMountEntity animatable, BakedGeoModel model, @Nullable MultiBufferSource bufferSource, public void preRender(PoseStack poseStack, RoyalMountEntity animatable, BakedGeoModel model, @Nullable MultiBufferSource bufferSource,
@Nullable VertexConsumer buffer, boolean isReRender, float partialTick, int packedLight, int packedOverlay, int colour) { @Nullable VertexConsumer buffer, boolean isReRender, float partialTick, int packedLight, int packedOverlay, int colour) {
super.preRender(poseStack, animatable, model, bufferSource, buffer, isReRender, partialTick, packedLight, packedOverlay, colour); super.preRender(poseStack, animatable, model, bufferSource, buffer, isReRender, partialTick, packedLight, packedOverlay, colour);
float scale = animatable.getAgeScale(); float scale = animatable.getGrowthScale();
poseStack.scale(scale, scale, scale); poseStack.scale(scale, scale, scale);
this.shadowRadius = 1.1F * scale; this.shadowRadius = 1.1F * scale;
} }
@Override
public void render(RoyalMountEntity entity, float entityYaw, float partialTick, PoseStack poseStack,
MultiBufferSource bufferSource, int packedLight) {
super.render(entity, entityYaw, partialTick, poseStack, bufferSource, packedLight);
Vec3 end = entity.getBeamEndPosition();
if (!entity.isFiringBeam() || end == null) return;
double x = Mth.lerp(partialTick, entity.xo, entity.getX());
double y = Mth.lerp(partialTick, entity.yo, entity.getY());
double z = Mth.lerp(partialTick, entity.zo, entity.getZ());
Vec3 start = entity.beamShootFrom().subtract(x, y, z);
Vec3 finish = end.subtract(x, y, z);
Vec3 axis = finish.subtract(start);
double length = axis.length();
if (length < 0.05D) return;
Vec3 dir = axis.scale(1.0D / length);
Vec3 side = dir.cross(new Vec3(0.0D, 1.0D, 0.0D));
if (side.lengthSqr() < 1.0E-4D) side = dir.cross(new Vec3(1.0D, 0.0D, 0.0D));
side = side.normalize();
ResourceLocation outer = QUEEN_OUTER;
ResourceLocation inner = QUEEN_INNER;
ResourceLocation endTexture = QUEEN_END;
if (entity instanceof PrinceEntity) {
switch (entity.getBeamElement()) {
case FIRE -> { outer = KING_FIRE_OUTER; inner = KING_FIRE_INNER; endTexture = KING_FIRE_END; }
case ICE -> { outer = KING_ICE_OUTER; inner = KING_ICE_INNER; endTexture = KING_ICE_END; }
default -> { outer = KING_LIGHTNING_OUTER; inner = KING_LIGHTNING_INNER; endTexture = KING_LIGHTNING_END; }
}
}
float time = entity.tickCount + partialTick;
float tiles = (float) (length / 3.0D);
org.joml.Matrix4f pose = poseStack.last().pose();
drawBeam(bufferSource.getBuffer(RenderType.entityTranslucentEmissive(outer)), pose, start, finish, side.scale(0.24D), -time * 0.32F, -time * 0.32F + tiles);
drawBeam(bufferSource.getBuffer(RenderType.entityTranslucentEmissive(inner)), pose, start, finish, side.scale(0.13D), -time * 0.5F, -time * 0.5F + tiles);
Vec3 up = new Vec3(this.entityRenderDispatcher.camera.getUpVector()).scale(0.55D);
Vec3 left = new Vec3(this.entityRenderDispatcher.camera.getLeftVector()).scale(0.55D);
drawQuad(bufferSource.getBuffer(RenderType.entityTranslucentEmissive(endTexture)), pose, finish.add(left).add(up), finish.subtract(left).add(up), finish.subtract(left).subtract(up), finish.add(left).subtract(up));
}
private static void drawBeam(VertexConsumer vertices, org.joml.Matrix4f pose, Vec3 start, Vec3 end, Vec3 half, float startV, float endV) {
drawQuad(vertices, pose, start.subtract(half), start.add(half), end.add(half), end.subtract(half), startV, endV);
drawQuad(vertices, pose, start.add(half), start.subtract(half), end.subtract(half), end.add(half), startV, endV);
}
private static void drawQuad(VertexConsumer vertices, org.joml.Matrix4f pose, Vec3 a, Vec3 b, Vec3 c, Vec3 d) {
drawQuad(vertices, pose, a, b, c, d, 0.0F, 1.0F);
}
private static void drawQuad(VertexConsumer vertices, org.joml.Matrix4f pose, Vec3 a, Vec3 b, Vec3 c, Vec3 d, float startV, float endV) {
vertex(vertices, pose, a, 0.0F, startV); vertex(vertices, pose, b, 1.0F, startV); vertex(vertices, pose, c, 1.0F, endV); vertex(vertices, pose, d, 0.0F, endV);
}
private static void vertex(VertexConsumer vertices, org.joml.Matrix4f pose, Vec3 pos, float u, float v) {
vertices.addVertex(pose, (float) pos.x, (float) pos.y, (float) pos.z).setColor(255, 255, 255, 255).setUv(u, v)
.setOverlay(net.minecraft.client.renderer.texture.OverlayTexture.NO_OVERLAY).setLight(0x00F000F0).setNormal(0.0F, 1.0F, 0.0F);
}
} }
@@ -1,14 +1,11 @@
package com.craisinlord.antarchy.content.effect; package com.craisinlord.antarchy.content.effect;
import com.craisinlord.antarchy.Antarchy;
import com.craisinlord.antarchy.content.gravity.AntarchyGravityApi; import com.craisinlord.antarchy.content.gravity.AntarchyGravityApi;
import com.craisinlord.antarchy.content.gravity.AntarchyGravityDirection; import com.craisinlord.antarchy.content.gravity.AntarchyGravityDirection;
import com.craisinlord.antarchy.content.gravity.AntarchyGravityTransition; import com.craisinlord.antarchy.content.gravity.AntarchyGravityTransition;
import net.minecraft.world.effect.MobEffectInstance;
import net.minecraft.world.effect.MobEffect; import net.minecraft.world.effect.MobEffect;
import net.minecraft.world.effect.MobEffectCategory; import net.minecraft.world.effect.MobEffectCategory;
import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.player.Player;
public final class InvertedMobEffect extends MobEffect { public final class InvertedMobEffect extends MobEffect {
private static final AntarchyGravityTransition EFFECT_TRANSITION = new AntarchyGravityTransition(12); private static final AntarchyGravityTransition EFFECT_TRANSITION = new AntarchyGravityTransition(12);
@@ -22,19 +19,6 @@ public final class InvertedMobEffect extends MobEffect {
public boolean applyEffectTick(LivingEntity entity, int amplifier) { public boolean applyEffectTick(LivingEntity entity, int amplifier) {
if (!entity.level().isClientSide()) { if (!entity.level().isClientSide()) {
if (AntarchyGravityApi.getGravityDirection(entity) != AntarchyGravityDirection.UP || !AntarchyGravityApi.isGravityForced(entity)) { if (AntarchyGravityApi.getGravityDirection(entity) != AntarchyGravityDirection.UP || !AntarchyGravityApi.isGravityForced(entity)) {
MobEffectInstance effect = entity.getEffect(com.craisinlord.antarchy.content.AntarchyObjects.INVERTED_EFFECT.get());
if (entity instanceof Player || com.craisinlord.antarchy.content.worldgen.thoraxis.ThoraxisUndersideManager.isThoraxis(entity.level())) {
Antarchy.LOGGER.info(
"[antarchy-gravity] applying inverted gravity from effect targetType={} uuid={} pos=({}, {}, {}) dim={} duration={} ambient={} visible={} forcedBefore={} gameTime={}",
entity.getType(), entity.getUUID(), entity.getX(), entity.getY(), entity.getZ(),
entity.level().dimension().location(),
effect != null ? effect.getDuration() : -1,
effect != null && effect.isAmbient(),
effect != null && effect.isVisible(),
AntarchyGravityApi.isGravityForced(entity),
entity.level().getGameTime()
);
}
AntarchyGravityApi.setForcedGravityDirection(entity, AntarchyGravityDirection.UP, EFFECT_TRANSITION); AntarchyGravityApi.setForcedGravityDirection(entity, AntarchyGravityDirection.UP, EFFECT_TRANSITION);
} }
} else { } else {
@@ -101,7 +101,7 @@ public class ManticoreEntity extends Monster implements GeoEntity {
return Mob.createMobAttributes() return Mob.createMobAttributes()
.add(Attributes.MAX_HEALTH, AntarchySettings.manticoreHealth()) .add(Attributes.MAX_HEALTH, AntarchySettings.manticoreHealth())
.add(Attributes.MOVEMENT_SPEED, 0.3D) .add(Attributes.MOVEMENT_SPEED, 0.3D)
.add(Attributes.FLYING_SPEED, 0.6D) .add(Attributes.FLYING_SPEED, 1.2D)
.add(Attributes.ATTACK_DAMAGE, AntarchySettings.manticoreAttackDamage()) .add(Attributes.ATTACK_DAMAGE, AntarchySettings.manticoreAttackDamage())
.add(Attributes.FOLLOW_RANGE, 28.0D) .add(Attributes.FOLLOW_RANGE, 28.0D)
.add(Attributes.KNOCKBACK_RESISTANCE, 0.2D); .add(Attributes.KNOCKBACK_RESISTANCE, 0.2D);
@@ -351,6 +351,16 @@ public class ScorpionEntity extends Monster implements GeoEntity {
ScorpionEntity.this.getNavigation().stop(); ScorpionEntity.this.getNavigation().stop();
return; return;
} }
// This goal overrides MeleeAttackGoal.tick, so the gravity mixin on the
// vanilla method cannot supply the inverted movement frame here.
LivingEntity target = ScorpionEntity.this.getTarget();
if (target != null && ThoraxisUndersideManager.shouldInvertInUnderside(ScorpionEntity.this)) {
ScorpionEntity.this.getNavigation().stop();
ScorpionEntity.this.getMoveControl().setWantedPosition(
target.getX(), target.getY(), target.getZ(), 1.15D);
this.checkAndPerformAttack(target);
return;
}
super.tick(); super.tick();
} }
@@ -37,7 +37,12 @@ import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.MultifaceBlock; import net.minecraft.world.level.block.MultifaceBlock;
import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.Vec3;
import net.minecraft.world.entity.EntityDimensions;
import net.minecraft.world.entity.Pose;
import net.minecraft.world.DifficultyInstance;
import net.minecraft.world.entity.SpawnGroupData;
import software.bernie.geckolib.animatable.GeoEntity; import software.bernie.geckolib.animatable.GeoEntity;
import software.bernie.geckolib.animatable.instance.AnimatableInstanceCache; import software.bernie.geckolib.animatable.instance.AnimatableInstanceCache;
import software.bernie.geckolib.animation.AnimatableManager; import software.bernie.geckolib.animation.AnimatableManager;
@@ -48,6 +53,42 @@ import software.bernie.geckolib.animation.RawAnimation;
import software.bernie.geckolib.util.GeckoLibUtil; import software.bernie.geckolib.util.GeckoLibUtil;
public class WormEntity extends Monster implements GeoEntity { public class WormEntity extends Monster implements GeoEntity {
public enum GrowthStage {
SMALL(0, EntityDimensions.scalable(0.55F, 0.8F), 8.0D, 0.29D, 2.0D, 20.0D, 0.05D, 2),
MEDIUM(1, EntityDimensions.scalable(0.9F, 1.5F), 20.0D, 0.25D, 4.0D, 24.0D, 0.2D, 5),
LARGE(2, EntityDimensions.scalable(1.35F, 2.75F), 36.0D, 0.22D, 7.0D, 28.0D, 0.45D, 8);
private final int id;
private final EntityDimensions dimensions;
private final double health;
private final double speed;
private final double damage;
private final double followRange;
private final double knockbackResistance;
private final int xp;
GrowthStage(int id, EntityDimensions dimensions, double health, double speed, double damage,
double followRange, double knockbackResistance, int xp) {
this.id = id;
this.dimensions = dimensions;
this.health = health;
this.speed = speed;
this.damage = damage;
this.followRange = followRange;
this.knockbackResistance = knockbackResistance;
this.xp = xp;
}
public static GrowthStage byId(int id) {
for (GrowthStage stage : values()) {
if (stage.id == id) return stage;
}
return LARGE;
}
}
private static final int SMALL_GROWTH_TICKS = 20 * 60 * 2;
private static final int MEDIUM_GROWTH_TICKS = 20 * 60 * 3;
private static final ResourceKey<Biome> WORMY_CAVES = ResourceKey.create( private static final ResourceKey<Biome> WORMY_CAVES = ResourceKey.create(
Registries.BIOME, Registries.BIOME,
ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "wormy_caves") ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "wormy_caves")
@@ -57,6 +98,7 @@ public class WormEntity extends Monster implements GeoEntity {
private static final int MUCUS_TRAIL_INTERVAL = 6; private static final int MUCUS_TRAIL_INTERVAL = 6;
private static final double MUCUS_TRAIL_MIN_DISTANCE_SQR = 0.36D; private static final double MUCUS_TRAIL_MIN_DISTANCE_SQR = 0.36D;
private static final EntityDataAccessor<Integer> ANIMATION_STATE = SynchedEntityData.defineId(WormEntity.class, EntityDataSerializers.INT); private static final EntityDataAccessor<Integer> ANIMATION_STATE = SynchedEntityData.defineId(WormEntity.class, EntityDataSerializers.INT);
private static final EntityDataAccessor<Integer> GROWTH_STAGE = SynchedEntityData.defineId(WormEntity.class, EntityDataSerializers.INT);
private static final int ANIM_INGROUND = 0; private static final int ANIM_INGROUND = 0;
private static final int ANIM_CRAWL = 1; private static final int ANIM_CRAWL = 1;
private static final int ANIM_ATTACK = 2; private static final int ANIM_ATTACK = 2;
@@ -67,6 +109,7 @@ public class WormEntity extends Monster implements GeoEntity {
private int attackAnimationTicks; private int attackAnimationTicks;
private int mucusTrailCooldown; private int mucusTrailCooldown;
private Vec3 lastMucusTrailPosition; private Vec3 lastMucusTrailPosition;
private int growthAge;
public WormEntity(EntityType<? extends WormEntity> entityType, Level level) { public WormEntity(EntityType<? extends WormEntity> entityType, Level level) {
super(entityType, level); super(entityType, level);
@@ -103,6 +146,67 @@ public class WormEntity extends Monster implements GeoEntity {
protected void defineSynchedData(SynchedEntityData.Builder builder) { protected void defineSynchedData(SynchedEntityData.Builder builder) {
super.defineSynchedData(builder); super.defineSynchedData(builder);
builder.define(ANIMATION_STATE, ANIM_INGROUND); builder.define(ANIMATION_STATE, ANIM_INGROUND);
builder.define(GROWTH_STAGE, GrowthStage.LARGE.id);
}
public GrowthStage getGrowthStage() {
return GrowthStage.byId(this.entityData.get(GROWTH_STAGE));
}
public void setGrowthStage(GrowthStage stage) {
if (this.entityData.get(GROWTH_STAGE) == stage.id) return;
this.entityData.set(GROWTH_STAGE, stage.id);
this.refreshDimensions();
this.applyStageAttributes();
}
private void applyStageAttributes() {
GrowthStage stage = this.getGrowthStage();
if (this.getAttribute(Attributes.MAX_HEALTH) != null) this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(stage.health);
if (this.getAttribute(Attributes.MOVEMENT_SPEED) != null) this.getAttribute(Attributes.MOVEMENT_SPEED).setBaseValue(stage.speed);
if (this.getAttribute(Attributes.ATTACK_DAMAGE) != null) this.getAttribute(Attributes.ATTACK_DAMAGE).setBaseValue(stage.damage);
if (this.getAttribute(Attributes.FOLLOW_RANGE) != null) this.getAttribute(Attributes.FOLLOW_RANGE).setBaseValue(stage.followRange);
if (this.getAttribute(Attributes.KNOCKBACK_RESISTANCE) != null) this.getAttribute(Attributes.KNOCKBACK_RESISTANCE).setBaseValue(stage.knockbackResistance);
this.xpReward = stage.xp;
if (!this.level().isClientSide) this.setHealth(Math.min(this.getHealth(), (float) stage.health));
}
@Override
public EntityDimensions getDefaultDimensions(Pose pose) {
return this.getGrowthStage().dimensions;
}
@Override
public void onSyncedDataUpdated(EntityDataAccessor<?> key) {
super.onSyncedDataUpdated(key);
if (GROWTH_STAGE.equals(key)) this.refreshDimensions();
}
@Override
public SpawnGroupData finalizeSpawn(ServerLevelAccessor level, DifficultyInstance difficulty, MobSpawnType spawnReason, SpawnGroupData spawnData) {
SpawnGroupData result = super.finalizeSpawn(level, difficulty, spawnReason, spawnData);
if (spawnReason == MobSpawnType.NATURAL || spawnReason == MobSpawnType.CHUNK_GENERATION || spawnReason == MobSpawnType.SPAWN_EGG) {
float roll = this.random.nextFloat();
this.setGrowthStage(roll < 0.4F ? GrowthStage.SMALL : roll < 0.6F ? GrowthStage.MEDIUM : GrowthStage.LARGE);
this.growthAge = 0;
}
return result;
}
@Override
public void addAdditionalSaveData(CompoundTag tag) {
super.addAdditionalSaveData(tag);
tag.putInt("GrowthStage", this.getGrowthStage().id);
tag.putInt("GrowthAge", this.growthAge);
}
@Override
public void readAdditionalSaveData(CompoundTag tag) {
super.readAdditionalSaveData(tag);
this.entityData.set(GROWTH_STAGE, tag.contains("GrowthStage") ? tag.getInt("GrowthStage") : GrowthStage.LARGE.id);
this.growthAge = tag.getInt("GrowthAge");
this.refreshDimensions();
this.applyStageAttributes();
} }
@Override @Override
@@ -153,6 +257,7 @@ public class WormEntity extends Monster implements GeoEntity {
if (this.level().isClientSide) { if (this.level().isClientSide) {
return; return;
} }
this.tickGrowth();
this.tickMucusTrail(); this.tickMucusTrail();
if (this.attackAnimationTicks > 0) { if (this.attackAnimationTicks > 0) {
this.attackAnimationTicks--; this.attackAnimationTicks--;
@@ -160,7 +265,21 @@ public class WormEntity extends Monster implements GeoEntity {
this.updateAnimationState(); this.updateAnimationState();
} }
private void tickGrowth() {
GrowthStage stage = this.getGrowthStage();
if (stage == GrowthStage.LARGE) return;
this.growthAge++;
if (stage == GrowthStage.SMALL && this.growthAge >= SMALL_GROWTH_TICKS) {
this.growthAge = 0;
this.setGrowthStage(GrowthStage.MEDIUM);
} else if (stage == GrowthStage.MEDIUM && this.growthAge >= MEDIUM_GROWTH_TICKS) {
this.growthAge = 0;
this.setGrowthStage(GrowthStage.LARGE);
}
}
private void tickMucusTrail() { private void tickMucusTrail() {
if (this.getGrowthStage() == GrowthStage.SMALL) return;
if (this.mucusTrailCooldown > 0) { if (this.mucusTrailCooldown > 0) {
this.mucusTrailCooldown--; this.mucusTrailCooldown--;
} }
@@ -74,6 +74,7 @@ public class DimensionalTearEntity extends Entity implements GeoEntity {
private static final double PORTAL_RADIUS = 1.35D; private static final double PORTAL_RADIUS = 1.35D;
private static final double EXIT_OFFSET = 2.4D; private static final double EXIT_OFFSET = 2.4D;
private static final Map<UUID, Long> TELEPORT_COOLDOWNS = new HashMap<>(); private static final Map<UUID, Long> TELEPORT_COOLDOWNS = new HashMap<>();
private static long lastTeleportCooldownCleanupTick = Long.MIN_VALUE;
private static final Map<UUID, Set<UUID>> QUEEN_TEAR_IDS = new HashMap<>(); private static final Map<UUID, Set<UUID>> QUEEN_TEAR_IDS = new HashMap<>();
private final AnimatableInstanceCache geoCache = GeckoLibUtil.createInstanceCache(this); private final AnimatableInstanceCache geoCache = GeckoLibUtil.createInstanceCache(this);
@@ -149,6 +150,7 @@ public class DimensionalTearEntity extends Entity implements GeoEntity {
} }
this.ageTicks++; this.ageTicks++;
cleanupTeleportCooldowns(this.level().getGameTime());
if (this.ageTicks == 1) { if (this.ageTicks == 1) {
this.level().playSound(null, this.blockPosition(), AntarchySoundEvents.DIMENSIONAL_TEAR_OPEN.get(), SoundSource.HOSTILE, 1.0F, 1.0F); this.level().playSound(null, this.blockPosition(), AntarchySoundEvents.DIMENSIONAL_TEAR_OPEN.get(), SoundSource.HOSTILE, 1.0F, 1.0F);
} }
@@ -370,7 +372,7 @@ public class DimensionalTearEntity extends Entity implements GeoEntity {
entity.hasImpulse = true; entity.hasImpulse = true;
if (entity instanceof LivingEntity living) { if (entity instanceof LivingEntity living) {
int invertedDuration = AntarchySettings.dimensionalTearInvertedDurationTicks(); int invertedDuration = AntarchySettings.dimensionalTearInvertedDurationTicks();
Antarchy.LOGGER.info( Antarchy.LOGGER.debug(
"[antarchy-dimensional-tear] applying inverted effect targetType={} uuid={} fromTear={} toTear={} startPos=({}, {}, {}) exitPos=({}, {}, {}) dim={} durationTicks={}", "[antarchy-dimensional-tear] applying inverted effect targetType={} uuid={} fromTear={} toTear={} startPos=({}, {}, {}) exitPos=({}, {}, {}) dim={} durationTicks={}",
living.getType(), living.getUUID(), this.getUUID(), destination.getUUID(), living.getType(), living.getUUID(), this.getUUID(), destination.getUUID(),
startPos.x, startPos.y, startPos.z, startPos.x, startPos.y, startPos.z,
@@ -382,6 +384,15 @@ public class DimensionalTearEntity extends Entity implements GeoEntity {
this.level().playSound(null, this.blockPosition(), SoundEvents.ENDERMAN_TELEPORT, SoundSource.NEUTRAL, 0.7F, 0.65F + this.random.nextFloat() * 0.2F); this.level().playSound(null, this.blockPosition(), SoundEvents.ENDERMAN_TELEPORT, SoundSource.NEUTRAL, 0.7F, 0.65F + this.random.nextFloat() * 0.2F);
} }
private static void cleanupTeleportCooldowns(long gameTime) {
if (gameTime <= lastTeleportCooldownCleanupTick
|| gameTime - lastTeleportCooldownCleanupTick < TELEPORT_COOLDOWN_TICKS * 4L) {
return;
}
lastTeleportCooldownCleanupTick = gameTime;
TELEPORT_COOLDOWNS.entrySet().removeIf(entry -> entry.getValue() <= gameTime);
}
private Vec3 exitPosition() { private Vec3 exitPosition() {
Vec3 facing = this.getViewVector(1.0F); Vec3 facing = this.getViewVector(1.0F);
if (facing.lengthSqr() < 1.0E-4D) { if (facing.lengthSqr() < 1.0E-4D) {
@@ -332,9 +332,12 @@ public class KingEntity extends RoyalBossEntity {
return; return;
} }
this.tickRoyalPunishment(); this.tickRoyalPunishment();
if (this.iceBuildup.size() > 16) { long gameTime = this.level().getGameTime();
this.iceBuildup.clear(); this.judgmentCooldowns.entrySet().removeIf(entry -> entry.getValue() <= gameTime);
} this.lastPlayerDamageTime.entrySet().removeIf(entry -> gameTime - entry.getValue() > 600L);
ServerLevel serverLevel = (ServerLevel) this.level();
this.iceBuildup.entrySet().removeIf(entry -> serverLevel.getEntity(entry.getKey()) == null);
if (this.iceBuildup.size() > 16) this.iceBuildup.clear();
LivingEntity target = this.getTarget(); LivingEntity target = this.getTarget();
if (target != null && !this.isDeadOrDying() && this.level() instanceof ServerLevel level) { if (target != null && !this.isDeadOrDying() && this.level() instanceof ServerLevel level) {
this.trackBehavior(target); this.trackBehavior(target);
@@ -346,6 +349,7 @@ public class KingEntity extends RoyalBossEntity {
if (this.isDeadOrDying() || !(this.level() instanceof ServerLevel)) { if (this.isDeadOrDying() || !(this.level() instanceof ServerLevel)) {
return; return;
} }
this.setRoyalFlying(true);
this.decreeRetreatPressure = false; this.decreeRetreatPressure = false;
if (this.patrolCenter == null || this.position().distanceToSqr(this.patrolCenter) > 64.0D * 64.0D) { if (this.patrolCenter == null || this.position().distanceToSqr(this.patrolCenter) > 64.0D * 64.0D) {
this.patrolCenter = this.position(); this.patrolCenter = this.position();
@@ -397,7 +401,7 @@ public class KingEntity extends RoyalBossEntity {
away = away.normalize(); away = away.normalize();
this.exileTarget.setDeltaMovement(away.x * 2.6D, 1.35D, away.z * 2.6D); this.exileTarget.setDeltaMovement(away.x * 2.6D, 1.35D, away.z * 2.6D);
this.exileTarget.hasImpulse = true; this.exileTarget.hasImpulse = true;
this.triggerAnim("body_action", "wing_gust"); this.triggerAnim("wing_action", "wing_gust");
this.playRoyalSound(AntarchySoundEvents.KING_WING_FLAP.get(), 0.82F); this.playRoyalSound(AntarchySoundEvents.KING_WING_FLAP.get(), 0.82F);
} }
} }
@@ -555,6 +559,7 @@ public class KingEntity extends RoyalBossEntity {
this.activeDecree = decree; this.activeDecree = decree;
this.activeDecreeTicks = 300; this.activeDecreeTicks = 300;
this.decreeCooldownTicks = 0; this.decreeCooldownTicks = 0;
this.projectRoyalSound(AntarchySoundEvents.KING_DECREE.get(), 4.0F, 1.0F, target);
this.sendDecreeTitle(target); this.sendDecreeTitle(target);
return true; return true;
} }
@@ -672,7 +677,7 @@ public class KingEntity extends RoyalBossEntity {
&& this.beginRoyalAttack("wing_gust", RoyalAttackLane.BODY, WING_GUST_COOLDOWN_TICKS, && this.beginRoyalAttack("wing_gust", RoyalAttackLane.BODY, WING_GUST_COOLDOWN_TICKS,
WING_GUST_WINDUP_TICKS, 1, 16, new com.craisinlord.antarchy.content.entity.royal.attack.RoyalAttackScheduler.Action() { WING_GUST_WINDUP_TICKS, 1, 16, new com.craisinlord.antarchy.content.entity.royal.attack.RoyalAttackScheduler.Action() {
@Override public void onStart() { @Override public void onStart() {
KingEntity.this.triggerAnim("body_action", "wing_gust"); KingEntity.this.triggerAnim("wing_action", "wing_gust");
KingEntity.this.playRoyalSound(AntarchySoundEvents.KING_WING_FLAP.get(), 0.85F); KingEntity.this.playRoyalSound(AntarchySoundEvents.KING_WING_FLAP.get(), 0.85F);
} }
@Override public void onActive(int elapsedTicks) { @Override public void onActive(int elapsedTicks) {
@@ -775,6 +780,8 @@ public class KingEntity extends RoyalBossEntity {
new com.craisinlord.antarchy.content.entity.royal.attack.RoyalAttackScheduler.Action() { new com.craisinlord.antarchy.content.entity.royal.attack.RoyalAttackScheduler.Action() {
@Override public void onStart() { @Override public void onStart() {
KingEntity.this.triggerAnim(RoyalHead.Slot.LEFT.controllerName(), "shoot"); KingEntity.this.triggerAnim(RoyalHead.Slot.LEFT.controllerName(), "shoot");
KingEntity.this.playRoyalSound(SoundEvents.FIRECHARGE_USE, 0.8F);
KingEntity.this.playRoyalSound(SoundEvents.GHAST_WARN, 1.2F);
} }
@Override public void onActive(int elapsedTicks) { @Override public void onActive(int elapsedTicks) {
Vec3 origin = KingEntity.this.headAnchor(KingEntity.this.royalHead(RoyalHead.Slot.LEFT)); Vec3 origin = KingEntity.this.headAnchor(KingEntity.this.royalHead(RoyalHead.Slot.LEFT));
@@ -891,6 +898,7 @@ public class KingEntity extends RoyalBossEntity {
this.decreeRetreatPressure = false; this.decreeRetreatPressure = false;
this.playRoyalSound(AntarchySoundEvents.KING_DECREE_CAST.get(), 0.9F + this.random.nextFloat() * 0.15F); this.playRoyalSound(AntarchySoundEvents.KING_DECREE_CAST.get(), 0.9F + this.random.nextFloat() * 0.15F);
this.playRoyalSound(AntarchySoundEvents.KING_ROAR.get(), 0.8F + this.random.nextFloat() * 0.12F); this.playRoyalSound(AntarchySoundEvents.KING_ROAR.get(), 0.8F + this.random.nextFloat() * 0.12F);
this.projectRoyalSound(AntarchySoundEvents.KING_DECREE.get(), 4.0F, 1.0F, target);
if (target instanceof ServerPlayer player) this.sendDecreeTitle(player); if (target instanceof ServerPlayer player) this.sendDecreeTitle(player);
} }
int countdown = this.activeDecree.countdownTicks(target); int countdown = this.activeDecree.countdownTicks(target);
@@ -904,6 +912,7 @@ public class KingEntity extends RoyalBossEntity {
return; return;
} }
if (this.activeDecree != null && --this.activeDecreeTicks <= 0) { if (this.activeDecree != null && --this.activeDecreeTicks <= 0) {
this.projectRoyalSound(AntarchySoundEvents.KING_SUCCESS.get(), 4.0F, 1.0F, target);
this.endDecree(target); this.endDecree(target);
} }
} }
@@ -955,12 +964,12 @@ public class KingEntity extends RoyalBossEntity {
this.endDecree(target); this.endDecree(target);
} }
/** Resolves a decree violation once, using the existing bounded judgment while the new judgment system is pending. */
public void failActiveDecree(@Nullable LivingEntity target) { public void failActiveDecree(@Nullable LivingEntity target) {
if (this.activeDecree == null || target == null || !target.isAlive()) { if (this.activeDecree == null || target == null || !target.isAlive()) {
return; return;
} }
if (target instanceof ServerPlayer player) { if (target instanceof ServerPlayer player) {
this.projectRoyalSound(AntarchySoundEvents.KING_JUDGEMENT.get(), 4.0F, 1.0F, target);
this.debugTriggerPunishment(RoyalPunishmentType.ROYAL_EXILE, player); this.debugTriggerPunishment(RoyalPunishmentType.ROYAL_EXILE, player);
} else { } else {
this.invokeJudgment(target); this.invokeJudgment(target);
@@ -982,6 +991,7 @@ public class KingEntity extends RoyalBossEntity {
long now = this.level().getGameTime(); long now = this.level().getGameTime();
if (this.judgmentCooldowns.getOrDefault(target.getUUID(), 0L) <= now) { if (this.judgmentCooldowns.getOrDefault(target.getUUID(), 0L) <= now) {
this.judgmentCooldowns.put(target.getUUID(), now + 20L); this.judgmentCooldowns.put(target.getUUID(), now + 20L);
this.projectRoyalSound(AntarchySoundEvents.KING_JUDGEMENT.get(), 4.0F, 1.0F, target);
target.hurt(this.damageSources().magic(), 6.0F); target.hurt(this.damageSources().magic(), 6.0F);
} }
} }
@@ -31,6 +31,15 @@ public class PrinceEntity extends RoyalMountEntity {
}; };
} }
@Override
public RoyalBeamElement getBeamElement() {
return switch (this.getBeamHead()) {
case 0 -> RoyalBeamElement.FIRE;
case 2 -> RoyalBeamElement.ICE;
default -> RoyalBeamElement.LIGHTNING;
};
}
@Override @Override
protected String geoName() { protected String geoName() {
return "prince"; return "prince";
@@ -7,6 +7,7 @@ import com.craisinlord.antarchy.content.AntarchyTags;
import com.craisinlord.antarchy.content.entity.ManticoreEntity; import com.craisinlord.antarchy.content.entity.ManticoreEntity;
import com.craisinlord.antarchy.content.gravity.AntarchyGravityApi; import com.craisinlord.antarchy.content.gravity.AntarchyGravityApi;
import java.util.HashMap; import java.util.HashMap;
import java.util.Iterator;
import java.util.Map; import java.util.Map;
import java.util.UUID; import java.util.UUID;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
@@ -21,23 +22,27 @@ import net.minecraft.world.entity.Entity;
import net.minecraft.world.BossEvent; import net.minecraft.world.BossEvent;
import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.Mob;
import net.minecraft.world.entity.ai.attributes.AttributeInstance; import net.minecraft.world.entity.ai.attributes.AttributeInstance;
import net.minecraft.world.entity.ai.attributes.AttributeModifier; import net.minecraft.world.entity.ai.attributes.AttributeModifier;
import net.minecraft.world.entity.ai.attributes.AttributeSupplier; import net.minecraft.world.entity.ai.attributes.AttributeSupplier;
import net.minecraft.world.entity.ai.attributes.Attributes; import net.minecraft.world.entity.ai.attributes.Attributes;
import net.minecraft.world.entity.ai.goal.target.NearestAttackableTargetGoal;
import net.minecraft.world.entity.MobSpawnType; import net.minecraft.world.entity.MobSpawnType;
import net.minecraft.world.level.Level; import net.minecraft.world.level.Level;
import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.Vec3;
import com.craisinlord.antarchy.content.entity.royal.beam.RoyalBeamSettings; 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.beam.RoyalBeamTerrainMode;
import com.craisinlord.antarchy.content.entity.royal.beam.RoyalBeamElement;
import com.craisinlord.antarchy.content.entity.royal.attack.RoyalAttackLane; import com.craisinlord.antarchy.content.entity.royal.attack.RoyalAttackLane;
import com.craisinlord.antarchy.content.entity.royal.attack.RoyalEffectController; import com.craisinlord.antarchy.content.entity.royal.attack.RoyalEffectController;
import com.craisinlord.antarchy.content.entity.portal.DimensionalTearEntity; 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.damagesource.DamageSource;
import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.player.Player;
import org.jetbrains.annotations.Nullable;
public class QueenEntity extends RoyalBossEntity { public class QueenEntity extends RoyalBossEntity {
static final double TIME_FIELD_RADIUS_SCALE = 3.0D;
private static final String SUMMON_COOLDOWN_KEY = "ManticoreSummonCooldownTicks"; private static final String SUMMON_COOLDOWN_KEY = "ManticoreSummonCooldownTicks";
private static final int FAILED_SUMMON_RETRY_TICKS = 20; private static final int FAILED_SUMMON_RETRY_TICKS = 20;
private static final int POSITION_ATTEMPTS_PER_MANTICORE = 8; private static final int POSITION_ATTEMPTS_PER_MANTICORE = 8;
@@ -47,7 +52,6 @@ public class QueenEntity extends RoyalBossEntity {
private static final DustParticleOptions ACCEL_DUST = new DustParticleOptions(new org.joml.Vector3f(1.0F, 0.15F, 0.15F), 2.0F); private static final DustParticleOptions ACCEL_DUST = new DustParticleOptions(new org.joml.Vector3f(1.0F, 0.15F, 0.15F), 2.0F);
private static final int GRAVITY_STOMP_COOLDOWN = 180; private static final int GRAVITY_STOMP_COOLDOWN = 180;
private static final int TIME_FIELD_COOLDOWN = 260;
private static final int MOMENTUM_LOCK_COOLDOWN = 420; private static final int MOMENTUM_LOCK_COOLDOWN = 420;
private static final int MOMENTUM_LOCK_DURATION = 90; private static final int MOMENTUM_LOCK_DURATION = 90;
private static final double MOMENTUM_LOCK_RADIUS = 26.0D; private static final double MOMENTUM_LOCK_RADIUS = 26.0D;
@@ -61,13 +65,12 @@ public class QueenEntity extends RoyalBossEntity {
private int manticoreSummonCooldownTicks; private int manticoreSummonCooldownTicks;
private int gravityStompCooldownTicks; private int gravityStompCooldownTicks;
private int timeFieldCooldownTicks;
private int momentumLockCooldownTicks; private int momentumLockCooldownTicks;
private int crushingGravityCooldownTicks; private int crushingGravityCooldownTicks;
private int accelerationCooldownTicks; private int accelerationCooldownTicks;
private int blackHoleCooldownTicks; private int blackHoleCooldownTicks;
private final int[] dreamFireballCooldownTicks = new int[3];
private int idleWanderCooldownTicks; private int idleWanderCooldownTicks;
private int finalTimeControlTicks;
private final RoyalEffectController royalEffects = new RoyalEffectController(); private final RoyalEffectController royalEffects = new RoyalEffectController();
private final Map<UUID, Vec3> frozenVelocities = new HashMap<>(); private final Map<UUID, Vec3> frozenVelocities = new HashMap<>();
@@ -89,6 +92,52 @@ public class QueenEntity extends RoyalBossEntity {
return true; return true;
} }
@Override
protected double groundCombatBias() {
return 0.72D;
}
@Override
protected double biteApproachSpeed() {
return 1.55D;
}
protected void registerRoyalTargetGoals() {
this.targetSelector.addGoal(1, new NearestAttackableTargetGoal<>(this, KingEntity.class, true));
this.targetSelector.addGoal(2, new NearestAttackableTargetGoal<>(this, Player.class, true));
this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(
this, Mob.class, 10, true, false, this::canAttack));
}
@Override
protected int selectBeamVolleyLimit(Phase phase) {
return switch (phase) {
case ONE -> AntarchySettings.queenBeamPhaseOneCap();
case TWO -> AntarchySettings.queenBeamPhaseTwoCap();
case THREE -> this.random.nextDouble() < AntarchySettings.queenBeamPhaseThreeMaxVolleyChance()
? AntarchySettings.queenBeamPhaseThreeCap()
: Math.min(2, AntarchySettings.queenBeamPhaseThreeCap());
};
}
@Override
protected boolean shouldDistributeHeadTargets(LivingEntity primaryTarget) {
return !(primaryTarget instanceof KingEntity);
}
public boolean canAttack(LivingEntity target) {
return !target.getType().is(AntarchyTags.Entities.QUEEN_DOES_NOT_ATTACK)
&& super.canAttack(target);
}
public boolean canDamageWithRoyalAttack(LivingEntity target) {
return target != this
&& target.isAlive()
&& (!(target instanceof Player player) || !player.isCreative() && !player.isSpectator())
&& !target.getType().is(AntarchyTags.Entities.QUEEN_DOES_NOT_ATTACK)
&& !this.isAlliedTo(target);
}
@Override @Override
protected SoundEvent royalFlyLoopSound() { protected SoundEvent royalFlyLoopSound() {
return AntarchySoundEvents.QUEEN_FLY_LOOP.get(); return AntarchySoundEvents.QUEEN_FLY_LOOP.get();
@@ -145,9 +194,20 @@ public class QueenEntity extends RoyalBossEntity {
return RoyalBeamTerrainMode.DESTROY; return RoyalBeamTerrainMode.DESTROY;
} }
@Override
protected RoyalBeamElement royalBeamElement(@Nullable RoyalHead head) {
if (head == null) {
return RoyalBeamElement.QUEEN_BLACK;
}
return switch (head.slot()) {
case LEFT -> RoyalBeamElement.QUEEN_PURPLE;
case CENTER -> RoyalBeamElement.QUEEN_BLACK;
case RIGHT -> RoyalBeamElement.QUEEN_RED;
};
}
public static boolean isFieldImmune(Entity entity) { public static boolean isFieldImmune(Entity entity) {
return entity instanceof RoyalBossEntity return entity.getType().is(AntarchyTags.Entities.QUEEN_DOES_NOT_ATTACK);
|| entity.getType().is(AntarchyTags.Entities.TIME_DILATION_IMMUNE);
} }
@Override @Override
@@ -162,10 +222,49 @@ public class QueenEntity extends RoyalBossEntity {
} }
this.tickManticoreSummon(); this.tickManticoreSummon();
this.tickQueenAbilities(); this.tickQueenAbilities();
this.tickFinalTimeControl(); this.tickDreamFireballs();
this.tickFinalAcceleration();
this.royalEffects.tick(); this.royalEffects.tick();
} }
private void tickDreamFireballs() {
if (!(this.level() instanceof ServerLevel level)) return;
LivingEntity target = this.getTarget();
if (target == null || !target.isAlive() || !this.canAttack(target)) return;
for (RoyalHead head : new RoyalHead[] {this.royalHead(RoyalHead.Slot.LEFT), this.royalHead(RoyalHead.Slot.CENTER), this.royalHead(RoyalHead.Slot.RIGHT)}) {
int index = head.slot().ordinal();
if (this.dreamFireballCooldownTicks[index] > 0) {
this.dreamFireballCooldownTicks[index]--;
continue;
}
if (!head.readyToAttack() || this.distanceTo(target) < 12.0D || this.distanceTo(target) > 52.0D) continue;
int cooldown = this.cooldown(180);
RoyalHead.Slot slot = head.slot();
if (!this.beginRoyalAttack("dream_fireball_" + slot.name(), this.headLane(head), cooldown, 12, 1, 5,
new com.craisinlord.antarchy.content.entity.royal.attack.RoyalAttackScheduler.Action() {
@Override public void onStart() {
head.startShoot();
QueenEntity.this.triggerAnim(slot.controllerName(), "shoot");
QueenEntity.this.playRoyalSound(net.minecraft.sounds.SoundEvents.FIRECHARGE_USE, 0.75F);
QueenEntity.this.playRoyalSound(net.minecraft.sounds.SoundEvents.GHAST_WARN, 1.35F);
}
@Override public void onActive(int elapsedTicks) {
Vec3 origin = QueenEntity.this.headAnchor(head);
Vec3 direction = target.getEyePosition().subtract(origin).normalize();
RoyalElementalProjectileEntity projectile = RoyalElementalProjectileEntity.create(
level, QueenEntity.this, RoyalBeamElement.DREAM_FIRE, origin, direction);
level.addFreshEntity(projectile);
QueenEntity.this.playRoyalSound(AntarchySoundEvents.QUEEN_BEAM_SHOOT.get(), 0.9F);
}
@Override public void onComplete() {
head.stopShoot();
}
})) {
this.dreamFireballCooldownTicks[index] = cooldown;
}
}
}
private void tickIdleWander() { private void tickIdleWander() {
if (this.idleWanderCooldownTicks-- > 0) { if (this.idleWanderCooldownTicks-- > 0) {
return; return;
@@ -239,7 +338,6 @@ public class QueenEntity extends RoyalBossEntity {
private void tickQueenAbilities() { private void tickQueenAbilities() {
if (this.gravityStompCooldownTicks > 0) this.gravityStompCooldownTicks--; if (this.gravityStompCooldownTicks > 0) this.gravityStompCooldownTicks--;
if (this.timeFieldCooldownTicks > 0) this.timeFieldCooldownTicks--;
if (this.momentumLockCooldownTicks > 0) this.momentumLockCooldownTicks--; if (this.momentumLockCooldownTicks > 0) this.momentumLockCooldownTicks--;
if (this.crushingGravityCooldownTicks > 0) this.crushingGravityCooldownTicks--; if (this.crushingGravityCooldownTicks > 0) this.crushingGravityCooldownTicks--;
if (this.accelerationCooldownTicks > 0) this.accelerationCooldownTicks--; if (this.accelerationCooldownTicks > 0) this.accelerationCooldownTicks--;
@@ -257,8 +355,8 @@ public class QueenEntity extends RoyalBossEntity {
boolean majorBusy = this.royalEffects.active("momentum_lock") || this.royalEffects.active("crushing_gravity"); boolean majorBusy = this.royalEffects.active("momentum_lock") || this.royalEffects.active("crushing_gravity");
boolean allowOverlap = phase != Phase.ONE; boolean allowOverlap = phase != Phase.ONE;
String[] candidates = phase == Phase.ONE String[] candidates = phase == Phase.ONE
? new String[] {"gravity_stomp", "time_field", "black_hole"} ? new String[] {"gravity_stomp", "black_hole"}
: new String[] {"gravity_stomp", "time_field", "black_hole", "momentum_lock", "crushing_gravity", "acceleration"}; : new String[] {"gravity_stomp", "black_hole", "momentum_lock", "crushing_gravity", "acceleration"};
String selected = this.attackScheduler.chooseWeighted(candidates, id -> { String selected = this.attackScheduler.chooseWeighted(candidates, id -> {
if (!this.queenAttackReady(id, phase, majorBusy, allowOverlap)) return 0; if (!this.queenAttackReady(id, phase, majorBusy, allowOverlap)) return 0;
return switch (id) { return switch (id) {
@@ -270,36 +368,39 @@ public class QueenEntity extends RoyalBossEntity {
if (selected == null) return; if (selected == null) return;
int cooldown = switch (selected) { int cooldown = switch (selected) {
case "gravity_stomp" -> GRAVITY_STOMP_COOLDOWN; case "gravity_stomp" -> GRAVITY_STOMP_COOLDOWN;
case "time_field" -> TIME_FIELD_COOLDOWN;
case "black_hole" -> BLACK_HOLE_COOLDOWN; case "black_hole" -> BLACK_HOLE_COOLDOWN;
case "momentum_lock" -> MOMENTUM_LOCK_COOLDOWN; case "momentum_lock" -> MOMENTUM_LOCK_COOLDOWN;
case "crushing_gravity" -> CRUSHING_GRAVITY_COOLDOWN; case "crushing_gravity" -> CRUSHING_GRAVITY_COOLDOWN;
default -> ACCELERATION_COOLDOWN; default -> ACCELERATION_COOLDOWN;
}; };
if (!this.beginRoyalAttack(selected, RoyalAttackLane.HAZARD, this.cooldown(cooldown), RoyalAttackLane lane = "black_hole".equals(selected) ? RoyalAttackLane.CENTER_HEAD : RoyalAttackLane.HAZARD;
if (!this.beginRoyalAttack(selected, lane, this.cooldown(cooldown),
10, 1, 14, new com.craisinlord.antarchy.content.entity.royal.attack.RoyalAttackScheduler.Action() { 10, 1, 14, new com.craisinlord.antarchy.content.entity.royal.attack.RoyalAttackScheduler.Action() {
@Override public void onStart() { @Override public void onStart() {
if ("gravity_stomp".equals(selected)) { if ("gravity_stomp".equals(selected)) {
QueenEntity.this.triggerAnim("body_action", "stomp"); QueenEntity.this.triggerAnim("body_action", "stomp");
} else { } else if ("momentum_lock".equals(selected)) {
QueenEntity.this.triggerAnim("body_action", "wing_gust"); QueenEntity.this.triggerAnim("wing_action", "wing_gust");
} else if ("black_hole".equals(selected)) {
QueenEntity.this.royalHead(RoyalHead.Slot.CENTER).startShoot();
QueenEntity.this.triggerAnim(RoyalHead.Slot.CENTER.controllerName(), "shoot");
} }
} }
@Override public void onActive(int elapsedTicks) { @Override public void onActive(int elapsedTicks) {
QueenEntity.this.performSelectedQueenAttack(selected, phase, cooldown, level, target); QueenEntity.this.performSelectedQueenAttack(selected, cooldown, level, target);
}
@Override public void onComplete() {
if ("black_hole".equals(selected)) {
QueenEntity.this.royalHead(RoyalHead.Slot.CENTER).stopShoot();
}
} }
})) return; })) return;
} }
private void performSelectedQueenAttack(String selected, Phase phase, int cooldown, private void performSelectedQueenAttack(String selected, int cooldown,
ServerLevel level, LivingEntity target) { ServerLevel level, LivingEntity target) {
switch (selected) { switch (selected) {
case "gravity_stomp" -> { this.gravityStompCooldownTicks = cooldown; this.performGravityStomp(level, target); } case "gravity_stomp" -> { this.gravityStompCooldownTicks = cooldown; this.performGravityStomp(level, target); }
case "time_field" -> {
this.timeFieldCooldownTicks = cooldown;
double fieldRate = phase == Phase.THREE ? 0.1D : 0.4D;
TimeDilationApi.createField(level, target.position(), 12.0D, fieldRate, 140 + phase.ordinal() * 40);
}
case "black_hole" -> { this.blackHoleCooldownTicks = cooldown; this.castBlackHole(level, target); } case "black_hole" -> { this.blackHoleCooldownTicks = cooldown; this.castBlackHole(level, target); }
case "momentum_lock" -> { this.momentumLockCooldownTicks = cooldown; this.startMomentumLock(level); } case "momentum_lock" -> { this.momentumLockCooldownTicks = cooldown; this.startMomentumLock(level); }
case "crushing_gravity" -> { case "crushing_gravity" -> {
@@ -316,8 +417,9 @@ public class QueenEntity extends RoyalBossEntity {
if (!allowOverlap && majorBusy) return false; if (!allowOverlap && majorBusy) return false;
return switch (id) { return switch (id) {
case "gravity_stomp" -> this.gravityStompCooldownTicks <= 0; case "gravity_stomp" -> this.gravityStompCooldownTicks <= 0;
case "time_field" -> this.timeFieldCooldownTicks <= 0; case "black_hole" -> this.blackHoleCooldownTicks <= 0
case "black_hole" -> this.blackHoleCooldownTicks <= 0; && this.royalHead(RoyalHead.Slot.CENTER).readyToAttack()
&& !this.attackScheduler.laneBusy(RoyalAttackLane.CENTER_HEAD);
case "momentum_lock" -> !this.royalEffects.active("momentum_lock") && this.momentumLockCooldownTicks <= 0; case "momentum_lock" -> !this.royalEffects.active("momentum_lock") && this.momentumLockCooldownTicks <= 0;
case "crushing_gravity" -> phase != Phase.ONE && !this.royalEffects.active("crushing_gravity") && this.crushingGravityCooldownTicks <= 0; case "crushing_gravity" -> phase != Phase.ONE && !this.royalEffects.active("crushing_gravity") && this.crushingGravityCooldownTicks <= 0;
case "acceleration" -> phase == Phase.THREE && !this.royalEffects.active("acceleration") && this.accelerationCooldownTicks <= 0; case "acceleration" -> phase == Phase.THREE && !this.royalEffects.active("acceleration") && this.accelerationCooldownTicks <= 0;
@@ -325,21 +427,13 @@ public class QueenEntity extends RoyalBossEntity {
}; };
} }
private void tickFinalTimeControl() { private void tickFinalAcceleration() {
if (this.phase() != Phase.THREE || !(this.level() instanceof ServerLevel level)) { if (this.phase() != Phase.THREE) {
return; return;
} }
if (!this.royalEffects.active("acceleration") && this.accelerationCooldownTicks <= 0) { if (!this.royalEffects.active("acceleration") && this.accelerationCooldownTicks <= 0) {
this.startRoyalAcceleration(); this.startRoyalAcceleration();
} }
if (this.finalTimeControlTicks-- <= 0) {
this.finalTimeControlTicks = AntarchySettings.queenFinalTimeFieldCooldownTicks();
LivingEntity target = this.getTarget();
if (target != null && target.isAlive()) {
TimeDilationApi.createField(level, target.position(), AntarchySettings.queenFinalTimeFieldRadius(),
AntarchySettings.queenFinalTimeFieldRate(), AntarchySettings.queenFinalTimeFieldDurationTicks());
}
}
} }
private void performGravityStomp(ServerLevel level, LivingEntity target) { private void performGravityStomp(ServerLevel level, LivingEntity target) {
@@ -347,26 +441,32 @@ public class QueenEntity extends RoyalBossEntity {
this.playRoyalSound(AntarchySoundEvents.QUEEN_ROAR.get(), 0.7F); this.playRoyalSound(AntarchySoundEvents.QUEEN_ROAR.get(), 0.7F);
DamageSource source = this.damageSources().mobAttack(this); DamageSource source = this.damageSources().mobAttack(this);
for (LivingEntity living : level.getEntitiesOfClass(LivingEntity.class, for (LivingEntity living : level.getEntitiesOfClass(LivingEntity.class,
this.getBoundingBox().inflate(18.0D), entity -> entity instanceof Player && entity.isAlive())) { this.getBoundingBox().inflate(18.0D), this::canDamageWithRoyalAttack)) {
double distance = Math.max(1.0D, living.distanceTo(this)); double distance = Math.max(1.0D, living.distanceTo(this));
living.hurt(source, (float) (12.0D * Math.max(0.25D, 1.0D - distance / 24.0D))); 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); Vec3 push = living.position().subtract(this.position()).normalize().scale(1.3D);
living.setDeltaMovement(living.getDeltaMovement().add(push.x, 0.65D, push.z)); living.setDeltaMovement(living.getDeltaMovement().add(push.x, 0.65D, push.z));
living.hasImpulse = true; living.hasImpulse = true;
} }
level.sendParticles(ParticleTypes.SONIC_BOOM, this.getX(), this.getY() + 1.0D, this.getZ(), 4, 2.0D, 0.5D, 2.0D, 0.0D); level.sendParticles(ParticleTypes.EXPLOSION, this.getX(), this.getY() + 1.0D, this.getZ(),
18, 5.0D, 0.6D, 5.0D, 0.08D);
} }
private void castBlackHole(ServerLevel level, LivingEntity target) { private void castBlackHole(ServerLevel level, LivingEntity target) {
double vertical = 2.0D + this.random.nextDouble() * 2.0D;
if (AntarchyGravityApi.isGravityInverted(target)) {
vertical = -vertical;
}
Vec3 anchor = target.position().add( Vec3 anchor = target.position().add(
(this.random.nextDouble() - 0.5D) * 4.0D, (this.random.nextDouble() - 0.5D) * 4.0D,
2.0D + this.random.nextDouble() * 2.0D, vertical,
(this.random.nextDouble() - 0.5D) * 4.0D); (this.random.nextDouble() - 0.5D) * 4.0D);
RoyalBlackHoleEntity hole = RoyalBlackHoleEntity.create(level, anchor, this.getUUID()); Vec3 mouth = this.beamAnchor(this.royalHead(RoyalHead.Slot.CENTER));
RoyalBlackHoleEntity hole = RoyalBlackHoleEntity.createProjectile(level, mouth, anchor, this.getUUID());
level.addFreshEntity(hole); level.addFreshEntity(hole);
this.triggerAnim("body_action", "wing_gust");
this.playRoyalSound(AntarchySoundEvents.QUEEN_ROAR.get(), 1.0F); this.playRoyalSound(AntarchySoundEvents.QUEEN_ROAR.get(), 1.0F);
level.sendParticles(ParticleTypes.REVERSE_PORTAL, anchor.x, anchor.y, anchor.z, 60, 1.0D, 1.0D, 1.0D, 0.4D); level.sendParticles(ParticleTypes.REVERSE_PORTAL, mouth.x, mouth.y, mouth.z,
30, 0.6D, 0.6D, 0.6D, 0.25D);
} }
private void startMomentumLock(ServerLevel level) { private void startMomentumLock(ServerLevel level) {
@@ -390,7 +490,6 @@ public class QueenEntity extends RoyalBossEntity {
} }
this.frozenVelocities.clear(); this.frozenVelocities.clear();
}); });
this.triggerAnim("body_action", "wing_gust");
this.playRoyalSound(AntarchySoundEvents.QUEEN_ROAR.get(), 0.5F); this.playRoyalSound(AntarchySoundEvents.QUEEN_ROAR.get(), 0.5F);
} }
@@ -398,9 +497,11 @@ public class QueenEntity extends RoyalBossEntity {
if (!(this.level() instanceof ServerLevel level)) { if (!(this.level() instanceof ServerLevel level)) {
return; return;
} }
for (Map.Entry<UUID, Vec3> entry : this.frozenVelocities.entrySet()) { for (Iterator<Map.Entry<UUID, Vec3>> iterator = this.frozenVelocities.entrySet().iterator(); iterator.hasNext();) {
Map.Entry<UUID, Vec3> entry = iterator.next();
Entity entity = level.getEntity(entry.getKey()); Entity entity = level.getEntity(entry.getKey());
if (entity == null || !entity.isAlive()) { if (entity == null || !entity.isAlive()) {
iterator.remove();
continue; continue;
} }
entity.setDeltaMovement(Vec3.ZERO); entity.setDeltaMovement(Vec3.ZERO);
@@ -522,7 +623,6 @@ public class QueenEntity extends RoyalBossEntity {
super.addAdditionalSaveData(tag); super.addAdditionalSaveData(tag);
tag.putInt(SUMMON_COOLDOWN_KEY, this.manticoreSummonCooldownTicks); tag.putInt(SUMMON_COOLDOWN_KEY, this.manticoreSummonCooldownTicks);
tag.putInt("GravityStompCooldownTicks", this.gravityStompCooldownTicks); tag.putInt("GravityStompCooldownTicks", this.gravityStompCooldownTicks);
tag.putInt("TimeFieldCooldownTicks", this.timeFieldCooldownTicks);
tag.putInt("AccelerationCooldownTicks", this.accelerationCooldownTicks); tag.putInt("AccelerationCooldownTicks", this.accelerationCooldownTicks);
tag.putInt("BlackHoleCooldownTicks", this.blackHoleCooldownTicks); tag.putInt("BlackHoleCooldownTicks", this.blackHoleCooldownTicks);
} }
@@ -532,7 +632,6 @@ public class QueenEntity extends RoyalBossEntity {
super.readAdditionalSaveData(tag); super.readAdditionalSaveData(tag);
this.manticoreSummonCooldownTicks = Math.max(0, tag.getInt(SUMMON_COOLDOWN_KEY)); this.manticoreSummonCooldownTicks = Math.max(0, tag.getInt(SUMMON_COOLDOWN_KEY));
this.gravityStompCooldownTicks = Math.max(0, tag.getInt("GravityStompCooldownTicks")); this.gravityStompCooldownTicks = Math.max(0, tag.getInt("GravityStompCooldownTicks"));
this.timeFieldCooldownTicks = Math.max(0, tag.getInt("TimeFieldCooldownTicks"));
this.momentumLockCooldownTicks = Math.max(0, tag.getInt("MomentumLockCooldownTicks")); this.momentumLockCooldownTicks = Math.max(0, tag.getInt("MomentumLockCooldownTicks"));
this.crushingGravityCooldownTicks = Math.max(0, tag.getInt("CrushingGravityCooldownTicks")); this.crushingGravityCooldownTicks = Math.max(0, tag.getInt("CrushingGravityCooldownTicks"));
this.accelerationCooldownTicks = Math.max(0, tag.getInt("AccelerationCooldownTicks")); this.accelerationCooldownTicks = Math.max(0, tag.getInt("AccelerationCooldownTicks"));
@@ -2,20 +2,27 @@ package com.craisinlord.antarchy.content.entity.royal;
import com.craisinlord.antarchy.content.AntarchyObjects; import com.craisinlord.antarchy.content.AntarchyObjects;
import com.craisinlord.antarchy.content.time.TimeDilationApi; import com.craisinlord.antarchy.content.time.TimeDilationApi;
import com.craisinlord.antarchy.content.time.TimeDilationFieldEntity;
import com.craisinlord.antarchy.config.AntarchySettings; import com.craisinlord.antarchy.config.AntarchySettings;
import java.util.UUID; import java.util.UUID;
import net.minecraft.core.BlockPos;
import net.minecraft.core.particles.ParticleTypes; import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.syncher.EntityDataAccessor; import net.minecraft.network.syncher.EntityDataAccessor;
import net.minecraft.network.syncher.EntityDataSerializers; import net.minecraft.network.syncher.EntityDataSerializers;
import net.minecraft.network.syncher.SynchedEntityData; import net.minecraft.network.syncher.SynchedEntityData;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
import net.minecraft.util.Mth;
import net.minecraft.world.damagesource.DamageSource; import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.ExperienceOrb;
import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.item.FallingBlockEntity;
import net.minecraft.world.entity.item.ItemEntity;
import net.minecraft.world.level.GameRules; import net.minecraft.world.level.GameRules;
import net.minecraft.world.level.Level; import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@@ -33,21 +40,50 @@ public class RoyalBlackHoleEntity extends Entity implements GeoEntity {
SynchedEntityData.defineId(RoyalBlackHoleEntity.class, EntityDataSerializers.FLOAT); SynchedEntityData.defineId(RoyalBlackHoleEntity.class, EntityDataSerializers.FLOAT);
private static final EntityDataAccessor<Boolean> COLLAPSING = private static final EntityDataAccessor<Boolean> COLLAPSING =
SynchedEntityData.defineId(RoyalBlackHoleEntity.class, EntityDataSerializers.BOOLEAN); SynchedEntityData.defineId(RoyalBlackHoleEntity.class, EntityDataSerializers.BOOLEAN);
private static final EntityDataAccessor<Float> VISUAL_GROWTH =
SynchedEntityData.defineId(RoyalBlackHoleEntity.class, EntityDataSerializers.FLOAT);
private static final EntityDataAccessor<Float> EFFECT_RADIUS =
SynchedEntityData.defineId(RoyalBlackHoleEntity.class, EntityDataSerializers.FLOAT);
private static final String OWNER_KEY = "OwnerUuid"; private static final String OWNER_KEY = "OwnerUuid";
private static final String AGE_KEY = "Age"; private static final String AGE_KEY = "Age";
private static final String ACTIVE_KEY = "ActiveTicks"; private static final String ACTIVE_KEY = "ActiveTicks";
private static final String ASSAILANT_KEY = "AssailantVariant"; private static final String TRAVEL_TICKS_KEY = "TravelTicks";
private static final String DESTINATION_X_KEY = "DestinationX";
private static final String DESTINATION_Y_KEY = "DestinationY";
private static final String DESTINATION_Z_KEY = "DestinationZ";
private static final String ANIMATION_AGE_KEY = "AnimationAge";
private static final String DILATION_FIELD_KEY = "DilationFieldUuid";
private static final String SUCKED_BLOCKS_KEY = "SuckedBlocks";
private static final double DEFAULT_RADIUS = 14.0D; private static final double DEFAULT_RADIUS = 14.0D;
private static final int DEFAULT_ACTIVE_TICKS = 120; private static final int QUEEN_ANIMATION_TICKS = 403;
private static final int COLLAPSE_TICKS = 12; private static final int QUEEN_COLLAPSE_TICKS = 25;
private static final int QUEEN_ACTIVE_TICKS = QUEEN_ANIMATION_TICKS - QUEEN_COLLAPSE_TICKS;
private static final int QUEEN_PROJECTILE_TRAVEL_TICKS = 20;
private static final int BLOCK_SUCTION_INTERVAL = 5;
private static final int BLOCKS_PER_SUCTION = 3;
private static final int BLOCK_SUCTION_ATTEMPTS = 36;
private static final double MODEL_RENDER_SCALE = 1.5D;
private static final double EFFECT_GROWTH_START = 0.45D;
private static final double EFFECT_GROWTH_END = 1.6D;
private static final double EFFECT_GROWTH_FRACTION = 0.6D;
private static final double MODEL_BASELINE_Y = 24.0D;
private static final double CORE_CUBE_CENTER_Y = 146.0D;
private static final double CORE_START_OFFSET_Y = -100.0D;
private static final double CORE_END_OFFSET_Y = 505.0D;
private static final double CORE_RISE_END_SECONDS = 19.125D;
@Nullable @Nullable
private UUID ownerId; private UUID ownerId;
private int age; private int age;
private int activeTicks = DEFAULT_ACTIVE_TICKS; private int activeTicks = QUEEN_ACTIVE_TICKS;
private boolean assailantVariant; private int animationAge;
private int suckedBlocks;
private int travelTicksRemaining;
@Nullable
private UUID dilationFieldId;
@Nullable
private Vec3 destination;
private final AnimatableInstanceCache geoCache = GeckoLibUtil.createInstanceCache(this); private final AnimatableInstanceCache geoCache = GeckoLibUtil.createInstanceCache(this);
public RoyalBlackHoleEntity(EntityType<? extends RoyalBlackHoleEntity> entityType, Level level) { public RoyalBlackHoleEntity(EntityType<? extends RoyalBlackHoleEntity> entityType, Level level) {
@@ -58,27 +94,37 @@ public class RoyalBlackHoleEntity extends Entity implements GeoEntity {
public static RoyalBlackHoleEntity create(ServerLevel level, Vec3 center, double radius, int activeTicks, @Nullable UUID ownerId) { public static RoyalBlackHoleEntity create(ServerLevel level, Vec3 center, double radius, int activeTicks, @Nullable UUID ownerId) {
RoyalBlackHoleEntity hole = new RoyalBlackHoleEntity(AntarchyObjects.ROYAL_BLACK_HOLE.get(), level); RoyalBlackHoleEntity hole = new RoyalBlackHoleEntity(AntarchyObjects.ROYAL_BLACK_HOLE.get(), level);
hole.setPos(center.x, center.y, center.z); hole.configure(center, radius, activeTicks, ownerId);
hole.setRadius(radius > 0.0D ? radius : AntarchySettings.queenBlackHoleRadius());
hole.activeTicks = activeTicks > 0 ? Math.max(20, activeTicks) : AntarchySettings.queenBlackHoleActiveTicks();
hole.ownerId = ownerId;
return hole; return hole;
} }
public static RoyalBlackHoleEntity create(ServerLevel level, Vec3 center, @Nullable UUID ownerId) { public static RoyalBlackHoleEntity create(ServerLevel level, Vec3 center, @Nullable UUID ownerId) {
return create(level, center, AntarchySettings.queenBlackHoleRadius(), AntarchySettings.queenBlackHoleActiveTicks(), ownerId); return create(level, center, AntarchySettings.queenBlackHoleRadius(), QUEEN_ACTIVE_TICKS, ownerId);
} }
public static RoyalBlackHoleEntity createAssailant(ServerLevel level, Vec3 center, @Nullable UUID ownerId) { public static RoyalBlackHoleEntity createProjectile(ServerLevel level, Vec3 origin, Vec3 destination,
RoyalBlackHoleEntity hole = create(level, center, AntarchySettings.queenBlackHoleRadius(), AntarchySettings.queenBlackHoleActiveTicks(), ownerId); @Nullable UUID ownerId) {
hole.assailantVariant = true; RoyalBlackHoleEntity hole = create(level, origin, ownerId);
hole.activeTicks = QUEEN_ACTIVE_TICKS - QUEEN_PROJECTILE_TRAVEL_TICKS;
hole.destination = destination;
hole.travelTicksRemaining = QUEEN_PROJECTILE_TRAVEL_TICKS;
hole.entityData.set(VISUAL_GROWTH, 0.15F);
return hole; return hole;
} }
protected final void configure(Vec3 center, double radius, int activeTicks, @Nullable UUID ownerId) {
this.setPos(center.x, center.y, center.z);
this.setRadius(radius > 0.0D ? radius : AntarchySettings.queenBlackHoleRadius());
this.activeTicks = activeTicks > 0 ? Math.max(20, activeTicks) : AntarchySettings.queenBlackHoleActiveTicks();
this.ownerId = ownerId;
}
@Override @Override
protected void defineSynchedData(SynchedEntityData.Builder builder) { protected void defineSynchedData(SynchedEntityData.Builder builder) {
builder.define(RADIUS, (float) DEFAULT_RADIUS); builder.define(RADIUS, (float) DEFAULT_RADIUS);
builder.define(COLLAPSING, false); builder.define(COLLAPSING, false);
builder.define(VISUAL_GROWTH, 1.0F);
builder.define(EFFECT_RADIUS, (float) (DEFAULT_RADIUS * EFFECT_GROWTH_START));
} }
public void setRadius(double radius) { public void setRadius(double radius) {
@@ -89,28 +135,48 @@ public class RoyalBlackHoleEntity extends Entity implements GeoEntity {
return this.entityData.get(RADIUS); return this.entityData.get(RADIUS);
} }
public double effectRadius() {
return Math.max(1.0D, this.entityData.get(EFFECT_RADIUS));
}
private double computeEffectRadius() {
double progress = this.activeTicks <= 0
? 1.0D
: Mth.clamp(this.age / (this.activeTicks * EFFECT_GROWTH_FRACTION), 0.0D, 1.0D);
return this.radius() * Mth.lerp(progress, EFFECT_GROWTH_START, EFFECT_GROWTH_END);
}
public boolean isCollapsing() { public boolean isCollapsing() {
return this.entityData.get(COLLAPSING); return this.entityData.get(COLLAPSING);
} }
public float visualGrowth() {
return this.entityData.get(VISUAL_GROWTH);
}
@Override @Override
public void tick() { public void tick() {
super.tick(); super.tick();
this.animationAge++;
if (this.level().isClientSide) { if (this.level().isClientSide) {
this.spawnClientParticles(); this.spawnClientParticles();
return; return;
} }
if (this.travelTicksRemaining > 0 && this.destination != null) {
this.tickProjectileTravel();
return;
}
this.age++; this.age++;
if (!this.isCollapsing()) { if (!this.isCollapsing()) {
if (this.assailantVariant) { if (this.risesWhileActive()) {
this.setPos(this.getX(), this.getY() + 0.035D, this.getZ()); this.setPos(this.getX(), this.getY() + 0.035D, this.getZ());
} }
this.entityData.set(EFFECT_RADIUS, (float) this.computeEffectRadius());
this.ensureAttachedDilationField();
this.applyPull(); this.applyPull();
if (this.age % 10 == 0 && this.level() instanceof ServerLevel level) { if (this.pullsTerrainBlocks() && this.age % BLOCK_SUCTION_INTERVAL == 0
TimeDilationApi.createField(level, this.position(), Math.max(2.0D, this.radius() * 0.55D), 0.18D, 14, this.ownerId, !this.assailantVariant); && this.level() instanceof ServerLevel level) {
if (this.age % 20 == 0 && level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { this.pullTerrainBlocks(level);
RoyalBlockDestruction.destroySphere(level, this, this.position(), Math.min(2.0D, this.radius() * 0.2D), 8, 30.0D, 0.0F);
}
} }
if (this.age >= this.activeTicks) { if (this.age >= this.activeTicks) {
this.entityData.set(COLLAPSING, true); this.entityData.set(COLLAPSING, true);
@@ -118,22 +184,105 @@ public class RoyalBlackHoleEntity extends Entity implements GeoEntity {
} }
return; return;
} }
if (this.age >= COLLAPSE_TICKS) { if (this.age >= this.collapseDurationTicks()) {
this.collapse(); this.collapse();
this.discardAttachedDilationField();
this.discard(); this.discard();
} }
} }
private void tickProjectileTravel() {
Vec3 remaining = this.destination.subtract(this.position());
double distance = remaining.length();
if (distance <= 0.5D || this.travelTicksRemaining <= 1) {
this.setPos(this.destination.x, this.destination.y, this.destination.z);
this.travelTicksRemaining = 0;
this.destination = null;
this.entityData.set(VISUAL_GROWTH, 1.0F);
return;
}
this.setPos(this.position().add(remaining.scale(1.0D / this.travelTicksRemaining)));
this.travelTicksRemaining--;
float progress = 1.0F - this.travelTicksRemaining / (float) QUEEN_PROJECTILE_TRAVEL_TICKS;
this.entityData.set(VISUAL_GROWTH, Mth.lerp(progress, 0.15F, 1.0F));
}
protected boolean risesWhileActive() {
return false;
}
protected double timeDilationRadius() {
return Math.max(2.0D, this.effectRadius() * 0.55D) * QueenEntity.TIME_FIELD_RADIUS_SCALE;
}
protected boolean hasVisibleTimeDilationField() {
return true;
}
protected int collapseDurationTicks() {
return QUEEN_COLLAPSE_TICKS;
}
protected boolean pullsTerrainBlocks() {
return true;
}
/** Returns the center of the animated black-hole cube in world coordinates. */
public Vec3 effectCenter() {
double animationSeconds = Math.min(CORE_RISE_END_SECONDS, this.animationAge / 20.0D);
double riseProgress = animationSeconds / CORE_RISE_END_SECONDS;
double animatedOffset = Mth.lerp(riseProgress, CORE_START_OFFSET_Y, CORE_END_OFFSET_Y);
double modelY = CORE_CUBE_CENTER_Y + animatedOffset;
double worldOffsetY = (modelY - MODEL_BASELINE_Y) / 16.0D * MODEL_RENDER_SCALE;
return this.position().add(0.0D, worldOffsetY, 0.0D);
}
private void ensureAttachedDilationField() {
if (!(this.level() instanceof ServerLevel level)) {
return;
}
TimeDilationFieldEntity field = this.dilationFieldId != null
&& level.getEntity(this.dilationFieldId) instanceof TimeDilationFieldEntity existing
? existing
: null;
if (field == null || !field.isAlive()) {
int remainingTicks = Math.max(1, this.activeTicks - this.age + this.collapseDurationTicks());
field = TimeDilationApi.createField(level, this.effectCenter(), this.timeDilationRadius(), 0.18D,
remainingTicks, this.ownerId, this.hasVisibleTimeDilationField());
field.attachTo(this);
this.dilationFieldId = field.getUUID();
} else {
Vec3 center = this.effectCenter();
field.setPos(center.x, center.y, center.z);
field.setFieldRadius(this.timeDilationRadius());
}
}
private void discardAttachedDilationField() {
if (this.dilationFieldId != null && this.level() instanceof ServerLevel level
&& level.getEntity(this.dilationFieldId) instanceof TimeDilationFieldEntity field) {
field.discard();
}
this.dilationFieldId = null;
}
private void applyPull() { private void applyPull() {
Vec3 center = this.position(); Vec3 center = this.effectCenter();
double radius = this.radius(); double radius = this.effectRadius();
for (Entity entity : this.level().getEntitiesOfClass(Entity.class, new AABB(center, center).inflate(radius))) { for (Entity entity : this.level().getEntitiesOfClass(Entity.class, new AABB(center, center).inflate(radius))) {
if (entity == this || this.isImmune(entity)) { if (entity == this || entity instanceof TimeDilationFieldEntity || this.isImmune(entity)) {
continue; continue;
} }
Vec3 toCenter = center.subtract(entity.position()); Vec3 toCenter = center.subtract(entity.position());
double distance = toCenter.length(); double distance = toCenter.length();
if (distance < 0.5D || distance > radius) { if (distance < 0.75D) {
if (entity instanceof ItemEntity || entity instanceof ExperienceOrb || entity instanceof FallingBlockEntity) {
entity.discard();
}
continue;
}
if (distance > radius) {
continue; continue;
} }
double strength = AntarchySettings.queenBlackHolePullStrength() * (1.0D - distance / radius); double strength = AntarchySettings.queenBlackHolePullStrength() * (1.0D - distance / radius);
@@ -144,12 +293,59 @@ public class RoyalBlackHoleEntity extends Entity implements GeoEntity {
} }
} }
private void pullTerrainBlocks(ServerLevel level) {
int cap = AntarchySettings.queenBlackHoleBlockSuctionCap();
if (cap <= 0 || this.suckedBlocks >= cap
|| !level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) {
return;
}
Vec3 center = this.effectCenter();
Vec3 terrainOrigin = this.position();
double radius = this.effectRadius();
int pulledThisTick = 0;
for (int attempt = 0; attempt < BLOCK_SUCTION_ATTEMPTS && pulledThisTick < BLOCKS_PER_SUCTION
&& this.suckedBlocks < cap; attempt++) {
BlockPos pos = BlockPos.containing(
terrainOrigin.x + (this.random.nextDouble() * 2.0D - 1.0D) * radius,
terrainOrigin.y + (this.random.nextDouble() * 2.0D - 1.0D) * radius,
terrainOrigin.z + (this.random.nextDouble() * 2.0D - 1.0D) * radius);
if (pos.distToCenterSqr(terrainOrigin.x, terrainOrigin.y, terrainOrigin.z) > radius * radius
|| pos.getY() < level.getMinBuildHeight()
|| pos.getY() >= level.getMaxBuildHeight()
|| !level.getWorldBorder().isWithinBounds(pos)) {
continue;
}
BlockState state = level.getBlockState(pos);
if (state.isAir() || level.getBlockEntity(pos) != null
|| state.getDestroySpeed(level, pos) < 0.0F
|| state.getBlock().getExplosionResistance() > 30.0F
|| state.getCollisionShape(level, pos).isEmpty()) {
continue;
}
FallingBlockEntity falling = FallingBlockEntity.fall(level, pos, state);
if (falling == null) {
continue;
}
Vec3 towardCenter = center.subtract(falling.position());
if (towardCenter.lengthSqr() > 0.001D) {
falling.setDeltaMovement(towardCenter.normalize().scale(0.28D));
}
falling.hasImpulse = true;
falling.hurtMarked = true;
pulledThisTick++;
this.suckedBlocks++;
}
}
private void collapse() { private void collapse() {
if (!(this.level() instanceof ServerLevel level)) { if (!(this.level() instanceof ServerLevel level)) {
return; return;
} }
Vec3 center = this.position(); Vec3 center = this.effectCenter();
double radius = this.radius(); double radius = this.effectRadius();
DamageSource source = this.ownerEntity() != null DamageSource source = this.ownerEntity() != null
? this.damageSources().mobAttack(this.ownerEntity()) ? this.damageSources().mobAttack(this.ownerEntity())
: this.damageSources().magic(); : this.damageSources().magic();
@@ -172,10 +368,14 @@ public class RoyalBlackHoleEntity extends Entity implements GeoEntity {
} }
private boolean isImmune(Entity entity) { private boolean isImmune(Entity entity) {
if (entity instanceof RoyalBossEntity) { if (this.ownerId != null && this.ownerId.equals(entity.getUUID())) {
return true; return true;
} }
if (this.ownerId != null && this.ownerId.equals(entity.getUUID())) { LivingEntity owner = this.ownerEntity();
if (owner instanceof QueenEntity) {
return entity.getType().is(com.craisinlord.antarchy.content.AntarchyTags.Entities.QUEEN_DOES_NOT_ATTACK);
}
if (entity instanceof RoyalBossEntity) {
return true; return true;
} }
return entity.getType().is(com.craisinlord.antarchy.content.AntarchyTags.Entities.TIME_DILATION_IMMUNE); return entity.getType().is(com.craisinlord.antarchy.content.AntarchyTags.Entities.TIME_DILATION_IMMUNE);
@@ -190,10 +390,10 @@ public class RoyalBlackHoleEntity extends Entity implements GeoEntity {
} }
private void spawnClientParticles() { private void spawnClientParticles() {
Vec3 center = this.position(); Vec3 center = this.effectCenter();
for (int i = 0; i < 6; i++) { for (int i = 0; i < 6; i++) {
double angle = this.random.nextDouble() * Math.PI * 2.0D; double angle = this.random.nextDouble() * Math.PI * 2.0D;
double r = this.radius() * (0.3D + this.random.nextDouble() * 0.7D); double r = this.effectRadius() * this.visualGrowth() * (0.3D + this.random.nextDouble() * 0.7D);
double px = center.x + Math.cos(angle) * r; double px = center.x + Math.cos(angle) * r;
double pz = center.z + Math.sin(angle) * r; double pz = center.z + Math.sin(angle) * r;
double py = center.y + (this.random.nextDouble() - 0.5D) * 3.0D; double py = center.y + (this.random.nextDouble() - 0.5D) * 3.0D;
@@ -216,9 +416,19 @@ public class RoyalBlackHoleEntity extends Entity implements GeoEntity {
protected void readAdditionalSaveData(CompoundTag tag) { protected void readAdditionalSaveData(CompoundTag tag) {
this.setRadius(tag.getDouble("Radius")); this.setRadius(tag.getDouble("Radius"));
this.age = tag.getInt(AGE_KEY); this.age = tag.getInt(AGE_KEY);
this.activeTicks = tag.contains(ACTIVE_KEY) ? tag.getInt(ACTIVE_KEY) : DEFAULT_ACTIVE_TICKS; this.activeTicks = tag.contains(ACTIVE_KEY) ? tag.getInt(ACTIVE_KEY) : QUEEN_ACTIVE_TICKS;
this.animationAge = tag.contains(ANIMATION_AGE_KEY) ? tag.getInt(ANIMATION_AGE_KEY) : this.age;
this.suckedBlocks = tag.getInt(SUCKED_BLOCKS_KEY);
this.entityData.set(EFFECT_RADIUS, (float) this.computeEffectRadius());
this.ownerId = tag.hasUUID(OWNER_KEY) ? tag.getUUID(OWNER_KEY) : null; this.ownerId = tag.hasUUID(OWNER_KEY) ? tag.getUUID(OWNER_KEY) : null;
this.assailantVariant = tag.getBoolean(ASSAILANT_KEY); this.dilationFieldId = tag.hasUUID(DILATION_FIELD_KEY) ? tag.getUUID(DILATION_FIELD_KEY) : null;
this.travelTicksRemaining = Math.max(0, tag.getInt(TRAVEL_TICKS_KEY));
if (this.travelTicksRemaining > 0 && tag.contains(DESTINATION_X_KEY)) {
this.destination = new Vec3(tag.getDouble(DESTINATION_X_KEY), tag.getDouble(DESTINATION_Y_KEY),
tag.getDouble(DESTINATION_Z_KEY));
float progress = 1.0F - this.travelTicksRemaining / (float) QUEEN_PROJECTILE_TRAVEL_TICKS;
this.entityData.set(VISUAL_GROWTH, Mth.lerp(progress, 0.15F, 1.0F));
}
} }
@Override @Override
@@ -226,10 +436,20 @@ public class RoyalBlackHoleEntity extends Entity implements GeoEntity {
tag.putDouble("Radius", this.radius()); tag.putDouble("Radius", this.radius());
tag.putInt(AGE_KEY, this.age); tag.putInt(AGE_KEY, this.age);
tag.putInt(ACTIVE_KEY, this.activeTicks); tag.putInt(ACTIVE_KEY, this.activeTicks);
tag.putInt(ANIMATION_AGE_KEY, this.animationAge);
tag.putInt(SUCKED_BLOCKS_KEY, this.suckedBlocks);
if (this.ownerId != null) { if (this.ownerId != null) {
tag.putUUID(OWNER_KEY, this.ownerId); tag.putUUID(OWNER_KEY, this.ownerId);
} }
tag.putBoolean(ASSAILANT_KEY, this.assailantVariant); if (this.dilationFieldId != null) {
tag.putUUID(DILATION_FIELD_KEY, this.dilationFieldId);
}
tag.putInt(TRAVEL_TICKS_KEY, this.travelTicksRemaining);
if (this.destination != null) {
tag.putDouble(DESTINATION_X_KEY, this.destination.x);
tag.putDouble(DESTINATION_Y_KEY, this.destination.y);
tag.putDouble(DESTINATION_Z_KEY, this.destination.z);
}
} }
@Override @Override
@@ -238,7 +458,7 @@ public class RoyalBlackHoleEntity extends Entity implements GeoEntity {
} }
private PlayState blackHoleController(AnimationState<RoyalBlackHoleEntity> state) { private PlayState blackHoleController(AnimationState<RoyalBlackHoleEntity> state) {
return state.setAndContinue(RawAnimation.begin().thenLoop("animation")); return state.setAndContinue(RawAnimation.begin().thenPlay("animation"));
} }
@Override @Override
@@ -78,7 +78,7 @@ public class RoyalBoltEntity extends AbstractHurtingProjectile {
case FIRE -> ParticleTypes.FLAME; case FIRE -> ParticleTypes.FLAME;
case LIGHTNING -> ParticleTypes.ELECTRIC_SPARK; case LIGHTNING -> ParticleTypes.ELECTRIC_SPARK;
case ICE -> ParticleTypes.SNOWFLAKE; case ICE -> ParticleTypes.SNOWFLAKE;
case GENERIC -> ParticleTypes.WITCH; default -> ParticleTypes.WITCH;
}; };
} }
@@ -12,8 +12,10 @@ import com.craisinlord.antarchy.content.entity.royal.attack.RoyalAttackLane;
import com.craisinlord.antarchy.content.entity.royal.attack.RoyalAttackScheduler; import com.craisinlord.antarchy.content.entity.royal.attack.RoyalAttackScheduler;
import com.craisinlord.antarchy.content.gravity.AntarchyGravityApi; import com.craisinlord.antarchy.content.gravity.AntarchyGravityApi;
import com.craisinlord.antarchy.content.worldgen.thoraxis.ThoraxisUndersideManager; import com.craisinlord.antarchy.content.worldgen.thoraxis.ThoraxisUndersideManager;
import java.util.Arrays;
import java.util.List; import java.util.List;
import net.minecraft.network.chat.Component; import net.minecraft.network.chat.Component;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.syncher.EntityDataAccessor; import net.minecraft.network.syncher.EntityDataAccessor;
import net.minecraft.network.syncher.EntityDataSerializers; import net.minecraft.network.syncher.EntityDataSerializers;
import net.minecraft.network.syncher.SynchedEntityData; import net.minecraft.network.syncher.SynchedEntityData;
@@ -34,12 +36,14 @@ import net.minecraft.world.item.ItemStack;
import net.minecraft.world.entity.ai.attributes.AttributeSupplier; import net.minecraft.world.entity.ai.attributes.AttributeSupplier;
import net.minecraft.world.entity.ai.attributes.Attributes; import net.minecraft.world.entity.ai.attributes.Attributes;
import net.minecraft.world.entity.ai.control.FlyingMoveControl; import net.minecraft.world.entity.ai.control.FlyingMoveControl;
import net.minecraft.world.entity.ai.control.MoveControl;
import net.minecraft.world.entity.ai.goal.FloatGoal; import net.minecraft.world.entity.ai.goal.FloatGoal;
import net.minecraft.world.entity.ai.goal.LookAtPlayerGoal; import net.minecraft.world.entity.ai.goal.LookAtPlayerGoal;
import net.minecraft.world.entity.ai.goal.RandomLookAroundGoal; 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.HurtByTargetGoal;
import net.minecraft.world.entity.ai.goal.target.NearestAttackableTargetGoal; import net.minecraft.world.entity.ai.goal.target.NearestAttackableTargetGoal;
import net.minecraft.world.entity.ai.navigation.FlyingPathNavigation; import net.minecraft.world.entity.ai.navigation.FlyingPathNavigation;
import net.minecraft.world.entity.ai.navigation.GroundPathNavigation;
import net.minecraft.world.entity.ai.navigation.PathNavigation; import net.minecraft.world.entity.ai.navigation.PathNavigation;
import net.minecraft.world.entity.monster.Monster; import net.minecraft.world.entity.monster.Monster;
import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.player.Player;
@@ -88,6 +92,7 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
private static final int BITE_DURATION_TICKS = 16; private static final int BITE_DURATION_TICKS = 16;
private static final int BITE_HIT_TICK = 6; private static final int BITE_HIT_TICK = 6;
private static final int HEAD_ATTACK_ANIMATION_TICKS = 25;
private static final int DEATH_TICKS = 60; private static final int DEATH_TICKS = 60;
private static final double CRUSH_RADIUS = 3.5D; private static final double CRUSH_RADIUS = 3.5D;
private static final int CRUSH_MAX_BLOCKS = 24; private static final int CRUSH_MAX_BLOCKS = 24;
@@ -98,9 +103,6 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
private static final int OBSTRUCTION_CLEAR_MAX_BLOCKS = 24; private static final int OBSTRUCTION_CLEAR_MAX_BLOCKS = 24;
private static final double OBSTRUCTION_CLEAR_MAX_RESISTANCE = 60.0D; private static final double OBSTRUCTION_CLEAR_MAX_RESISTANCE = 60.0D;
private static final float OBSTRUCTION_CLEAR_DROP_CHANCE = 0.1F; private static final float OBSTRUCTION_CLEAR_DROP_CHANCE = 0.1F;
// Keep visual muzzle points separate from the centered multipart hitboxes.
// Geo mouth fronts are near z=-219, with side heads centered at x=+/-21. At 2x render scale,
// one model pixel is 1/8 block.
private static final double BEAM_MUZZLE_FORWARD = 27.375D; private static final double BEAM_MUZZLE_FORWARD = 27.375D;
private static final double BEAM_MUZZLE_CENTER_Y = 11.5D; private static final double BEAM_MUZZLE_CENTER_Y = 11.5D;
private static final double BEAM_MUZZLE_SIDE_Y = 10.25D; private static final double BEAM_MUZZLE_SIDE_Y = 10.25D;
@@ -110,22 +112,40 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
protected static final double FLYING_PREFERRED_HOVER = 6.0D; protected static final double FLYING_PREFERRED_HOVER = 6.0D;
protected static final double FLYING_MAX_HOVER_ABOVE_GROUND = 24.0D; protected static final double FLYING_MAX_HOVER_ABOVE_GROUND = 24.0D;
private static final EntityDataAccessor<Boolean> FIRING = @SuppressWarnings("unchecked")
SynchedEntityData.defineId(RoyalBossEntity.class, EntityDataSerializers.BOOLEAN); private static final EntityDataAccessor<Boolean>[] BEAM_ACTIVE = new EntityDataAccessor[] {
private static final EntityDataAccessor<Boolean> HAS_BEAM_END = SynchedEntityData.defineId(RoyalBossEntity.class, EntityDataSerializers.BOOLEAN),
SynchedEntityData.defineId(RoyalBossEntity.class, EntityDataSerializers.BOOLEAN); SynchedEntityData.defineId(RoyalBossEntity.class, EntityDataSerializers.BOOLEAN),
SynchedEntityData.defineId(RoyalBossEntity.class, EntityDataSerializers.BOOLEAN)
};
private static final EntityDataAccessor<Boolean> ACCELERATED = private static final EntityDataAccessor<Boolean> ACCELERATED =
SynchedEntityData.defineId(RoyalBossEntity.class, EntityDataSerializers.BOOLEAN); SynchedEntityData.defineId(RoyalBossEntity.class, EntityDataSerializers.BOOLEAN);
private static final EntityDataAccessor<Integer> BEAM_ELEMENT = private static final EntityDataAccessor<Boolean> ROYAL_FLYING =
SynchedEntityData.defineId(RoyalBossEntity.class, EntityDataSerializers.INT); SynchedEntityData.defineId(RoyalBossEntity.class, EntityDataSerializers.BOOLEAN);
private static final EntityDataAccessor<Integer> BEAM_HEAD_SLOT = @SuppressWarnings("unchecked")
SynchedEntityData.defineId(RoyalBossEntity.class, EntityDataSerializers.INT); private static final EntityDataAccessor<Integer>[] BEAM_ELEMENT = new EntityDataAccessor[] {
private static final EntityDataAccessor<Float> BEAM_END_X = SynchedEntityData.defineId(RoyalBossEntity.class, EntityDataSerializers.INT),
SynchedEntityData.defineId(RoyalBossEntity.class, EntityDataSerializers.FLOAT); SynchedEntityData.defineId(RoyalBossEntity.class, EntityDataSerializers.INT),
private static final EntityDataAccessor<Float> BEAM_END_Y = SynchedEntityData.defineId(RoyalBossEntity.class, EntityDataSerializers.INT)
SynchedEntityData.defineId(RoyalBossEntity.class, EntityDataSerializers.FLOAT); };
private static final EntityDataAccessor<Float> BEAM_END_Z = @SuppressWarnings("unchecked")
SynchedEntityData.defineId(RoyalBossEntity.class, EntityDataSerializers.FLOAT); private static final EntityDataAccessor<Float>[] BEAM_END_X = new EntityDataAccessor[] {
SynchedEntityData.defineId(RoyalBossEntity.class, EntityDataSerializers.FLOAT),
SynchedEntityData.defineId(RoyalBossEntity.class, EntityDataSerializers.FLOAT),
SynchedEntityData.defineId(RoyalBossEntity.class, EntityDataSerializers.FLOAT)
};
@SuppressWarnings("unchecked")
private static final EntityDataAccessor<Float>[] BEAM_END_Y = new EntityDataAccessor[] {
SynchedEntityData.defineId(RoyalBossEntity.class, EntityDataSerializers.FLOAT),
SynchedEntityData.defineId(RoyalBossEntity.class, EntityDataSerializers.FLOAT),
SynchedEntityData.defineId(RoyalBossEntity.class, EntityDataSerializers.FLOAT)
};
@SuppressWarnings("unchecked")
private static final EntityDataAccessor<Float>[] BEAM_END_Z = new EntityDataAccessor[] {
SynchedEntityData.defineId(RoyalBossEntity.class, EntityDataSerializers.FLOAT),
SynchedEntityData.defineId(RoyalBossEntity.class, EntityDataSerializers.FLOAT),
SynchedEntityData.defineId(RoyalBossEntity.class, EntityDataSerializers.FLOAT)
};
private static final RawAnimation IDLE_ANIM = RawAnimation.begin().thenLoop("idle"); private static final RawAnimation IDLE_ANIM = RawAnimation.begin().thenLoop("idle");
private static final RawAnimation WALK_ANIM = RawAnimation.begin().thenLoop("walk"); private static final RawAnimation WALK_ANIM = RawAnimation.begin().thenLoop("walk");
@@ -133,7 +153,7 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
private static final RawAnimation DEATH_ANIM = RawAnimation.begin().thenPlayAndHold("death"); private static final RawAnimation DEATH_ANIM = RawAnimation.begin().thenPlayAndHold("death");
private final AnimatableInstanceCache geoCache = GeckoLibUtil.createInstanceCache(this); private final AnimatableInstanceCache geoCache = GeckoLibUtil.createInstanceCache(this);
protected final RoyalBeamController beamController = new RoyalBeamController(this); protected final RoyalBeamController[] beamControllers = new RoyalBeamController[3];
protected final RoyalAttackScheduler attackScheduler = new RoyalAttackScheduler(); protected final RoyalAttackScheduler attackScheduler = new RoyalAttackScheduler();
private final ServerBossEvent bossEvent; private final ServerBossEvent bossEvent;
private final RoyalHead[] heads = { private final RoyalHead[] heads = {
@@ -141,21 +161,38 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
new RoyalHead(RoyalHead.Slot.CENTER), new RoyalHead(RoyalHead.Slot.CENTER),
new RoyalHead(RoyalHead.Slot.RIGHT) new RoyalHead(RoyalHead.Slot.RIGHT)
}; };
private int beamCooldownTicks; private final int[] beamLoopSoundTicks = new int[3];
private final int[] assignedHeadCounts = new int[3];
private int beamVolleyLimit;
@Nullable @Nullable
private RoyalHead activeBeamHead; private Phase beamVolleyPhase;
private int beamLoopSoundTicks; private final MoveControl groundMoveControl;
private int beamHeadCursor; private final FlyingMoveControl flyingMoveControl;
private boolean beamStarted; private final PathNavigation groundNavigation;
private final PathNavigation flyingNavigation;
private int locomotionDecisionTicks = 80;
private boolean landingForCombat;
@Nullable @Nullable
private Entity[] multipartParts; private Entity[] multipartParts;
protected RoyalBossEntity(EntityType<? extends RoyalBossEntity> entityType, Level level) { protected RoyalBossEntity(EntityType<? extends RoyalBossEntity> entityType, Level level) {
super(entityType, level); super(entityType, level);
for (int i = 0; i < this.beamControllers.length; i++) {
this.beamControllers[i] = new RoyalBeamController(this);
}
this.xpReward = 500; this.xpReward = 500;
this.groundMoveControl = new MoveControl(this);
this.flyingMoveControl = new FlyingMoveControl(this, 20, true);
this.groundNavigation = new GroundPathNavigation(this, level);
FlyingPathNavigation flyingNavigation = new FlyingPathNavigation(this, level);
flyingNavigation.setCanFloat(true);
this.flyingNavigation = flyingNavigation;
if (this.isFlyingBoss()) { if (this.isFlyingBoss()) {
this.moveControl = new FlyingMoveControl(this, 20, true); this.moveControl = this.flyingMoveControl;
this.navigation = this.flyingNavigation;
} else {
this.navigation = this.groundNavigation;
} }
this.bossEvent = new com.craisinlord.antarchy.content.boss.EntityLinkedServerBossEvent( this.bossEvent = new com.craisinlord.antarchy.content.boss.EntityLinkedServerBossEvent(
this.getUUID(), this.getUUID(),
@@ -222,6 +259,14 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
protected abstract boolean isFlyingBoss(); protected abstract boolean isFlyingBoss();
protected double groundCombatBias() {
return 0.35D;
}
protected double biteApproachSpeed() {
return 1.3D;
}
public String geoNameForRender() { public String geoNameForRender() {
return this.geoName(); return this.geoName();
} }
@@ -258,21 +303,33 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
this.goalSelector.addGoal(0, new FloatGoal(this)); this.goalSelector.addGoal(0, new FloatGoal(this));
this.goalSelector.addGoal(7, new LookAtPlayerGoal(this, Player.class, 48.0F)); this.goalSelector.addGoal(7, new LookAtPlayerGoal(this, Player.class, 48.0F));
this.goalSelector.addGoal(8, new RandomLookAroundGoal(this)); this.goalSelector.addGoal(8, new RandomLookAroundGoal(this));
this.registerRoyalTargetGoals();
}
protected void registerRoyalTargetGoals() {
this.targetSelector.addGoal(1, new HurtByTargetGoal(this)); this.targetSelector.addGoal(1, new HurtByTargetGoal(this));
this.targetSelector.addGoal(2, new NearestAttackableTargetGoal<>(this, Player.class, true)); this.targetSelector.addGoal(2, new NearestAttackableTargetGoal<>(this, Player.class, true));
} }
public boolean canDamageWithRoyalAttack(LivingEntity target) {
return target != this
&& !(target instanceof RoyalBossEntity)
&& target.getType() != this.getType()
&& !this.isAlliedTo(target);
}
@Override @Override
protected void defineSynchedData(SynchedEntityData.Builder builder) { protected void defineSynchedData(SynchedEntityData.Builder builder) {
super.defineSynchedData(builder); super.defineSynchedData(builder);
builder.define(FIRING, false);
builder.define(HAS_BEAM_END, false);
builder.define(ACCELERATED, false); builder.define(ACCELERATED, false);
builder.define(BEAM_ELEMENT, RoyalBeamElement.GENERIC.ordinal()); builder.define(ROYAL_FLYING, true);
builder.define(BEAM_HEAD_SLOT, -1); for (int i = 0; i < 3; i++) {
builder.define(BEAM_END_X, 0.0F); builder.define(BEAM_ACTIVE[i], false);
builder.define(BEAM_END_Y, 0.0F); builder.define(BEAM_ELEMENT[i], RoyalBeamElement.GENERIC.ordinal());
builder.define(BEAM_END_Z, 0.0F); builder.define(BEAM_END_X[i], 0.0F);
builder.define(BEAM_END_Y[i], 0.0F);
builder.define(BEAM_END_Z[i], 0.0F);
}
} }
@Override @Override
@@ -308,9 +365,27 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
this.playSound(sound, (float) AntarchySettings.royalBossSoundVolume(), pitch); this.playSound(sound, (float) AntarchySettings.royalBossSoundVolume(), pitch);
} }
protected void projectRoyalSound(SoundEvent sound, float volume, float pitch, @Nullable LivingEntity focus) {
if (this.level().isClientSide) {
return;
}
java.util.Set<ServerPlayer> recipients = new java.util.LinkedHashSet<>(this.bossEvent.getPlayers());
if (focus instanceof ServerPlayer focusPlayer) {
recipients.add(focusPlayer);
}
for (ServerPlayer player : recipients) {
player.connection.send(new net.minecraft.network.protocol.game.ClientboundSoundPacket(
net.minecraft.core.Holder.direct(sound),
net.minecraft.sounds.SoundSource.HOSTILE,
player.getX(), player.getY(), player.getZ(),
volume, pitch,
this.random.nextLong()));
}
}
@Override @Override
public void travel(Vec3 travelVector) { public void travel(Vec3 travelVector) {
if (this.isFlyingBoss() && this.isEffectiveAi()) { if (this.isRoyalFlying() && this.isEffectiveAi()) {
this.moveRelative(this.getSpeed(), travelVector); this.moveRelative(this.getSpeed(), travelVector);
this.move(MoverType.SELF, this.getDeltaMovement()); this.move(MoverType.SELF, this.getDeltaMovement());
this.setDeltaMovement(this.getDeltaMovement().scale(0.91D)); this.setDeltaMovement(this.getDeltaMovement().scale(0.91D));
@@ -336,19 +411,24 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
OBSTRUCTION_CLEAR_DROP_CHANCE); OBSTRUCTION_CLEAR_DROP_CHANCE);
} }
if (this.isFlyingBoss()) { if (this.isFlyingBoss()) {
this.moveControl = this.isRoyalFlying() ? this.flyingMoveControl : this.groundMoveControl;
this.navigation = this.isRoyalFlying() ? this.flyingNavigation : this.groundNavigation;
this.setNoGravity(!AntarchyGravityApi.isGravityInverted(this)); this.setNoGravity(!AntarchyGravityApi.isGravityInverted(this));
if (!this.isRoyalFlying()) {
this.setNoGravity(false);
}
} }
if (this.level().isClientSide) { if (this.level().isClientSide) {
return; return;
} }
if (this.isFlyingBoss() && !this.isDeadOrDying()) { if (this.isRoyalFlying() && !this.isDeadOrDying() && !this.landingForCombat) {
this.tickFlyingAltitude(); this.tickFlyingAltitude();
} }
this.bossEvent.setProgress(this.getHealth() / this.getMaxHealth()); this.bossEvent.setProgress(this.getHealth() / this.getMaxHealth());
this.attackScheduler.tick(); this.attackScheduler.tick();
if (this.isFlyingBoss() && this.tickCount % 40 == 0 && this.royalFlyLoopSound() != null) { if (this.isRoyalFlying() && this.tickCount % 40 == 0 && this.royalFlyLoopSound() != null) {
this.playRoyalSound(this.royalFlyLoopSound(), 0.92F + this.random.nextFloat() * 0.12F); this.playRoyalSound(this.royalFlyLoopSound(), 0.92F + this.random.nextFloat() * 0.12F);
} }
@@ -364,6 +444,7 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
return; return;
} }
this.tickCombatLocomotionMode(primaryTarget);
this.getLookControl().setLookAt(primaryTarget, 30.0F, 30.0F); this.getLookControl().setLookAt(primaryTarget, 30.0F, 30.0F);
this.assignHeadTargets(primaryTarget); this.assignHeadTargets(primaryTarget);
this.tickRoyalBeam(primaryTarget); this.tickRoyalBeam(primaryTarget);
@@ -400,45 +481,49 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
} }
private void tickRoyalBeam(LivingEntity primaryTarget) { private void tickRoyalBeam(LivingEntity primaryTarget) {
if (this.beamCooldownTicks > 0 || this.attackScheduler.laneBusy(this.beamLane())) { Phase phase = this.phase();
this.beamCooldownTicks--; int activeBeams = 0;
for (RoyalHead head : this.heads) {
if (head.beamActive()) {
activeBeams++;
} }
}
if (this.activeBeamHead != null) { if (activeBeams == 0 || this.beamVolleyPhase != phase) {
LivingEntity beamTarget = this.activeBeamHead.target(this.level()); this.beamVolleyPhase = phase;
if (!this.activeBeamHead.shooting() || beamTarget == null || !beamTarget.isAlive() this.beamVolleyLimit = Math.max(1, this.selectBeamVolleyLimit(phase));
}
for (RoyalHead head : this.heads) {
int index = head.slot().ordinal();
RoyalBeamController controller = this.beamControllers[index];
if (controller.isFiring()) {
LivingEntity beamTarget = head.target(this.level());
if (!head.shooting() || beamTarget == null || !beamTarget.isAlive()
|| this.distanceTo(beamTarget) < this.royalBeamMinimumRange()) { || this.distanceTo(beamTarget) < this.royalBeamMinimumRange()) {
this.stopRoyalBeam(); this.stopRoyalBeam(head);
} else if (!this.beamStarted) { continue;
return; }
} else { if (this.beamLoopSoundTicks[index]-- <= 0) {
if (this.beamLoopSoundTicks-- <= 0) { this.beamLoopSoundTicks[index] = 20;
this.beamLoopSoundTicks = 20;
this.playRoyalSound(this.royalBeamLoopSound(), 1.0F); this.playRoyalSound(this.royalBeamLoopSound(), 1.0F);
} }
this.beamController.tick( controller.tick(
this.beamAnchor(this.activeBeamHead), this.beamAnchor(head),
beamTarget, beamTarget,
this.royalBeamSettings(), this.royalBeamSettings(),
this.royalBeamTerrainMode(this.activeBeamHead), this.royalBeamTerrainMode(head),
this::setRoyalBeamEndPosition); end -> this.setRoyalBeamEndPosition(head.slot(), end));
Vec3 beamEnd = this.beamController.beamEndPosition(); Vec3 beamEnd = controller.beamEndPosition();
if (beamEnd != null) { if (beamEnd != null) {
this.tickRoyalBeamEffects(this.activeBeamHead, this.beamAnchor(this.activeBeamHead), beamEnd); this.tickRoyalBeamEffects(head, this.beamAnchor(head), beamEnd);
} }
if (!this.beamController.isFiring()) { if (!controller.isFiring()) {
this.stopRoyalBeam(); this.stopRoyalBeam(head);
} }
return; continue;
} }
if (activeBeams >= this.beamVolleyLimit) {
continue;
} }
if (this.beamCooldownTicks > 0) {
return;
}
Phase phase = this.phase();
for (int offset = 0; offset < this.heads.length; offset++) {
RoyalHead head = this.heads[(this.beamHeadCursor + offset) % this.heads.length];
LivingEntity target = head.target(this.level()); LivingEntity target = head.target(this.level());
if (!head.readyToAttack() || this.attackScheduler.laneBusy(this.headLane(head)) if (!head.readyToAttack() || this.attackScheduler.laneBusy(this.headLane(head))
|| target == null || !this.canAttack(target) || target == null || !this.canAttack(target)
@@ -454,62 +539,53 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
windup = Math.max(0, AntarchySettings.kingBeamWindupTicks()); windup = Math.max(0, AntarchySettings.kingBeamWindupTicks());
} }
LivingEntity committedTarget = target; LivingEntity committedTarget = target;
if (!this.attackScheduler.start("beam_" + head.slot().name(), this.beamLane(), cooldown, if (!this.beginRoyalAttack("beam_" + head.slot().name(), this.headLane(head), cooldown,
windup, duration, 8, new RoyalAttackScheduler.Action() { windup, duration, 8, new RoyalAttackScheduler.Action() {
@Override @Override
public void onStart() { public void onStart() {
head.startShoot(); head.startShoot();
RoyalBossEntity.this.triggerAnim(head.slot().controllerName(), "shoot");
} }
@Override @Override
public void onActive(int elapsedTicks) { public void onActive(int elapsedTicks) {
if (elapsedTicks != 0 || RoyalBossEntity.this.beamStarted) { if (elapsedTicks != 0 || RoyalBossEntity.this.beamControllers[index].isFiring()) {
return; return;
} }
RoyalBossEntity.this.beamStarted = true; RoyalBossEntity.this.beamControllers[index].start(
RoyalBossEntity.this.beamController.start(
RoyalBossEntity.this.beamAnchor(head), committedTarget.getEyePosition(), duration); RoyalBossEntity.this.beamAnchor(head), committedTarget.getEyePosition(), duration);
RoyalBossEntity.this.entityData.set(BEAM_ELEMENT, RoyalBossEntity.this.royalBeamElement(head).ordinal()); RoyalBossEntity.this.entityData.set(BEAM_ELEMENT[index], RoyalBossEntity.this.royalBeamElement(head).ordinal());
RoyalBossEntity.this.entityData.set(BEAM_HEAD_SLOT, head.slot().ordinal()); RoyalBossEntity.this.entityData.set(BEAM_ACTIVE[index], true);
RoyalBossEntity.this.setFiringRoyalBeam(true); RoyalBossEntity.this.beamLoopSoundTicks[index] = 20;
RoyalBossEntity.this.triggerAnim(head.slot().controllerName(), "shoot");
RoyalBossEntity.this.beamLoopSoundTicks = 20;
RoyalBossEntity.this.playRoyalSound(RoyalBossEntity.this.royalBeamStartSound(head), 1.0F); RoyalBossEntity.this.playRoyalSound(RoyalBossEntity.this.royalBeamStartSound(head), 1.0F);
RoyalBossEntity.this.playRoyalSound(RoyalBossEntity.this.royalBeamShootSound(), 1.0F); RoyalBossEntity.this.playRoyalSound(RoyalBossEntity.this.royalBeamShootSound(), 1.0F);
} }
@Override @Override
public void onComplete() { public void onComplete() {
if (RoyalBossEntity.this.activeBeamHead == head) { RoyalBossEntity.this.stopRoyalBeam(head);
RoyalBossEntity.this.stopRoyalBeam();
}
} }
})) { })) {
continue; continue;
} }
head.setBeamActive(true); head.setBeamActive(true);
this.activeBeamHead = head; activeBeams++;
this.beamHeadCursor = (head.slot().partIndex()) % this.heads.length;
this.beamCooldownTicks = cooldown;
this.beamStarted = false;
return;
} }
} }
private void stopRoyalBeam() { protected int selectBeamVolleyLimit(Phase phase) {
if (this.activeBeamHead != null) { return phase.maxConcurrentHeadAttacks();
this.activeBeamHead.setBeamActive(false);
this.activeBeamHead.stopShoot();
} }
this.activeBeamHead = null;
this.beamStarted = false; private void stopRoyalBeam(RoyalHead head) {
this.attackScheduler.cancel(this.beamLane()); int index = head.slot().ordinal();
this.beamController.stop(); head.setBeamActive(false);
this.entityData.set(BEAM_ELEMENT, RoyalBeamElement.GENERIC.ordinal()); head.stopShoot();
this.entityData.set(BEAM_HEAD_SLOT, -1); this.beamControllers[index].stop();
this.entityData.set(BEAM_ACTIVE[index], false);
this.entityData.set(BEAM_ELEMENT[index], RoyalBeamElement.GENERIC.ordinal());
this.playRoyalSound(this.royalBeamEndSound(), 1.0F); this.playRoyalSound(this.royalBeamEndSound(), 1.0F);
this.setFiringRoyalBeam(false); this.setRoyalBeamEndPosition(head.slot(), null);
this.setRoyalBeamEndPosition(null);
} }
public Phase phase() { public Phase phase() {
@@ -524,6 +600,12 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
} }
private void assignHeadTargets(LivingEntity fallback) { private void assignHeadTargets(LivingEntity fallback) {
if (!this.shouldDistributeHeadTargets(fallback)) {
for (RoyalHead head : this.heads) {
head.setTarget(fallback);
}
return;
}
List<Player> candidates = this.level().getEntitiesOfClass( List<Player> candidates = this.level().getEntitiesOfClass(
Player.class, Player.class,
this.getBoundingBox().inflate(AntarchySettings.royalBossFollowRange()), this.getBoundingBox().inflate(AntarchySettings.royalBossFollowRange()),
@@ -535,7 +617,7 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
return; return;
} }
int[] assignedCounts = new int[candidates.size()]; Arrays.fill(this.assignedHeadCounts, 0);
for (RoyalHead head : this.heads) { for (RoyalHead head : this.heads) {
Vec3 anchor = this.headAnchor(head); Vec3 anchor = this.headAnchor(head);
int bestIndex = 0; int bestIndex = 0;
@@ -543,47 +625,113 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
double bestDistance = Double.MAX_VALUE; double bestDistance = Double.MAX_VALUE;
for (int i = 0; i < candidates.size(); i++) { for (int i = 0; i < candidates.size(); i++) {
double distance = candidates.get(i).position().distanceToSqr(anchor); double distance = candidates.get(i).position().distanceToSqr(anchor);
if (assignedCounts[i] < bestCount || (assignedCounts[i] == bestCount && distance < bestDistance)) { if (this.assignedHeadCounts[i] < bestCount || (this.assignedHeadCounts[i] == bestCount && distance < bestDistance)) {
bestIndex = i; bestIndex = i;
bestCount = assignedCounts[i]; bestCount = this.assignedHeadCounts[i];
bestDistance = distance; bestDistance = distance;
} }
} }
assignedCounts[bestIndex]++; this.assignedHeadCounts[bestIndex]++;
head.setTarget(candidates.get(bestIndex)); head.setTarget(candidates.get(bestIndex));
} }
} }
protected boolean shouldDistributeHeadTargets(LivingEntity primaryTarget) {
return true;
}
private void steerTowardTarget(LivingEntity target) { private void steerTowardTarget(LivingEntity target) {
double reach = this.getBbWidth() * 0.5D + this.biteReach() * 0.5D;
double dx = target.getX() - this.getX(); double dx = target.getX() - this.getX();
double dz = target.getZ() - this.getZ(); double dz = target.getZ() - this.getZ();
boolean closeHorizontally = (dx * dx + dz * dz) <= reach * reach; double horizontalDistance = Math.sqrt(dx * dx + dz * dz);
Vec3 awayFromTarget = horizontalDistance > 1.0E-4D
? new Vec3(-dx / horizontalDistance, 0.0D, -dz / horizontalDistance)
: this.getViewVector(1.0F).multiply(-1.0D, 0.0D, -1.0D).normalize();
double standoff = Math.max(this.getBbWidth() * 0.55D,
BEAM_MUZZLE_FORWARD - this.biteReach() * 0.65D);
Vec3 wanted = target.position().add(awayFromTarget.scale(standoff));
boolean atStandoff = this.position().multiply(1.0D, 0.0D, 1.0D)
.distanceToSqr(wanted.multiply(1.0D, 0.0D, 1.0D)) <= 4.0D;
boolean anyHeadInBiteRange = Arrays.stream(this.heads)
.anyMatch(head -> this.headWithinBiteReach(head, target));
double approachSpeed = anyHeadInBiteRange ? 1.0D : this.biteApproachSpeed();
if (!this.isFlyingBoss()) { if (!this.isRoyalFlying()) {
if (closeHorizontally) { if (atStandoff) {
this.getNavigation().stop(); this.getNavigation().stop();
} else { } else {
this.getMoveControl().setWantedPosition(target.getX(), target.getY(), target.getZ(), 1.0D); this.getMoveControl().setWantedPosition(wanted.x, target.getY(), wanted.z, approachSpeed);
} }
return; return;
} }
boolean inverted = AntarchyGravityApi.isGravityInverted(this); boolean inverted = AntarchyGravityApi.isGravityInverted(this);
double groundBelowTarget = this.groundYBelow(target.getX(), target.getZ()); double groundBelowTarget = this.groundYBelow(wanted.x, wanted.z);
double wantedY = inverted double wantedY = this.landingForCombat
? (inverted ? target.getY() + target.getBbHeight() - this.getBbHeight() : groundBelowTarget)
: inverted
? target.getY() - 3.0D ? target.getY() - 3.0D
: Mth.clamp(target.getY() + 3.0D, : Mth.clamp(target.getY() + 3.0D,
groundBelowTarget + FLYING_MIN_HOVER, groundBelowTarget + FLYING_MIN_HOVER,
groundBelowTarget + FLYING_MAX_HOVER_ABOVE_GROUND); groundBelowTarget + FLYING_MAX_HOVER_ABOVE_GROUND);
boolean closeVertically = Math.abs(wantedY - this.getY()) <= 2.5D; boolean closeVertically = Math.abs(wantedY - this.getY()) <= 2.5D;
if (closeHorizontally && closeVertically) { if (atStandoff && closeVertically) {
this.getNavigation().stop(); this.getNavigation().stop();
} else { } else {
this.getMoveControl().setWantedPosition(target.getX(), wantedY, target.getZ(), 1.1D); this.getMoveControl().setWantedPosition(wanted.x, wantedY, wanted.z, approachSpeed);
} }
} }
private void tickCombatLocomotionMode(LivingEntity target) {
if (!this.isFlyingBoss()) {
return;
}
boolean inverted = AntarchyGravityApi.isGravityInverted(this);
double groundY = this.groundYBelow(this.getX(), this.getZ());
if (this.landingForCombat) {
if (!target.onGround()) {
this.landingForCombat = false;
this.locomotionDecisionTicks = 60;
} else if (this.onGround() || !inverted && this.getY() <= groundY + 1.0D) {
this.landingForCombat = false;
this.setRoyalFlying(false);
this.locomotionDecisionTicks = 100 + this.random.nextInt(100);
}
return;
}
if (--this.locomotionDecisionTicks > 0) {
return;
}
if (this.isRoyalFlying()) {
this.locomotionDecisionTicks = 60 + this.random.nextInt(80);
if (target.onGround() && this.random.nextDouble() < this.groundCombatBias()) {
this.landingForCombat = true;
}
} else if (!target.onGround() || this.random.nextDouble() >= this.groundCombatBias()) {
this.setRoyalFlying(true);
this.setDeltaMovement(this.getDeltaMovement().add(0.0D, 0.35D, 0.0D));
this.locomotionDecisionTicks = 80 + this.random.nextInt(100);
} else {
this.locomotionDecisionTicks = 80 + this.random.nextInt(80);
}
}
public boolean isRoyalFlying() {
return this.isFlyingBoss() && this.entityData.get(ROYAL_FLYING);
}
protected final void setRoyalFlying(boolean flying) {
if (this.entityData.get(ROYAL_FLYING) == flying) {
return;
}
this.navigation.stop();
this.entityData.set(ROYAL_FLYING, flying);
this.moveControl = flying ? this.flyingMoveControl : this.groundMoveControl;
this.navigation = flying ? this.flyingNavigation : this.groundNavigation;
this.setNoGravity(flying && !AntarchyGravityApi.isGravityInverted(this));
}
protected double groundYBelow(double x, double z) { protected double groundYBelow(double x, double z) {
return this.level().getHeight(net.minecraft.world.level.levelgen.Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, return this.level().getHeight(net.minecraft.world.level.levelgen.Heightmap.Types.MOTION_BLOCKING_NO_LEAVES,
Mth.floor(x), Mth.floor(z)); Mth.floor(x), Mth.floor(z));
@@ -619,14 +767,14 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
Vec3 forward = new Vec3(-Math.sin(yaw), 0.0D, Math.cos(yaw)); Vec3 forward = new Vec3(-Math.sin(yaw), 0.0D, Math.cos(yaw));
Vec3 center = new Vec3( Vec3 center = new Vec3(
this.getX() + forward.x * (this.getBbWidth() * 0.45D), this.getX() + forward.x * (this.getBbWidth() * 0.45D),
this.getY() + (this.isFlyingBoss() ? 0.0D : 1.0D), this.getY() + (this.isRoyalFlying() ? 0.0D : 1.0D),
this.getZ() + forward.z * (this.getBbWidth() * 0.45D)); this.getZ() + forward.z * (this.getBbWidth() * 0.45D));
RoyalBlockDestruction.destroySphere(serverLevel, this, center, CRUSH_RADIUS, CRUSH_MAX_BLOCKS, CRUSH_MAX_RESISTANCE, CRUSH_DROP_CHANCE); RoyalBlockDestruction.destroySphere(serverLevel, this, center, CRUSH_RADIUS, CRUSH_MAX_BLOCKS, CRUSH_MAX_RESISTANCE, CRUSH_DROP_CHANCE);
} }
private void startBite(RoyalHead head, Phase phase) { private void startBite(RoyalHead head, Phase phase) {
int cooldown = Math.max(10, Mth.floor(AntarchySettings.royalBossBiteCooldownTicks() * phase.cooldownScale())); int cooldown = Math.max(10, Mth.floor(AntarchySettings.royalBossBiteCooldownTicks() * phase.cooldownScale()));
if (!this.attackScheduler.start("bite_" + head.slot().name(), headLane(head), cooldown, if (!this.beginRoyalAttack("bite_" + head.slot().name(), headLane(head), cooldown,
BITE_HIT_TICK, 1, BITE_DURATION_TICKS - BITE_HIT_TICK, BITE_HIT_TICK, 1, BITE_DURATION_TICKS - BITE_HIT_TICK,
new RoyalAttackScheduler.Action() { new RoyalAttackScheduler.Action() {
@Override @Override
@@ -663,7 +811,12 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
protected boolean beginRoyalAttack(String id, RoyalAttackLane lane, int cooldownTicks, int windupTicks, protected boolean beginRoyalAttack(String id, RoyalAttackLane lane, int cooldownTicks, int windupTicks,
int activeTicks, int recoveryTicks, RoyalAttackScheduler.Action action) { int activeTicks, int recoveryTicks, RoyalAttackScheduler.Action action) {
return this.attackScheduler.start(id, lane, cooldownTicks, windupTicks, activeTicks, recoveryTicks, action); int protectedRecovery = recoveryTicks;
if (lane == RoyalAttackLane.LEFT_HEAD || lane == RoyalAttackLane.CENTER_HEAD || lane == RoyalAttackLane.RIGHT_HEAD) {
protectedRecovery = Math.max(recoveryTicks,
HEAD_ATTACK_ANIMATION_TICKS - Math.max(0, windupTicks) - Math.max(1, activeTicks));
}
return this.attackScheduler.start(id, lane, cooldownTicks, windupTicks, activeTicks, protectedRecovery, action);
} }
private void applyBiteHit(RoyalHead head) { private void applyBiteHit(RoyalHead head) {
@@ -676,7 +829,7 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
DamageSource damageSource = this.damageSources().mobAttack(this); DamageSource damageSource = this.damageSources().mobAttack(this);
AABB box = new AABB(anchor, anchor).inflate(reach); AABB box = new AABB(anchor, anchor).inflate(reach);
for (LivingEntity living : this.level().getEntitiesOfClass(LivingEntity.class, box, entity -> entity.isAlive() && entity != this)) { for (LivingEntity living : this.level().getEntitiesOfClass(LivingEntity.class, box, entity -> entity.isAlive() && entity != this)) {
if (living instanceof RoyalBossEntity || living.getType() == this.getType()) { if (!this.canDamageWithRoyalAttack(living)) {
continue; continue;
} }
if (living.position().distanceToSqr(anchor) > reach * reach) { if (living.position().distanceToSqr(anchor) > reach * reach) {
@@ -753,12 +906,22 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
@Override @Override
public void die(DamageSource damageSource) { public void die(DamageSource damageSource) {
if (!this.level().isClientSide) { if (!this.level().isClientSide) {
this.stopTriggeredAnim("wing_action", null);
this.stopTriggeredAnim("head_left", null);
this.stopTriggeredAnim("head_center", null);
this.stopTriggeredAnim("head_right", null);
this.stopTriggeredAnim("body_action", null);
for (RoyalHead head : this.heads) { for (RoyalHead head : this.heads) {
head.reset(); head.reset();
} }
this.beamController.stop(); for (RoyalBeamController controller : this.beamControllers) {
controller.stop();
}
this.setFiringRoyalBeam(false); this.setFiringRoyalBeam(false);
this.setRoyalBeamEndPosition(null); for (RoyalHead head : this.heads) {
this.stopRoyalBeam(head);
}
this.setRoyalFlying(false);
this.getNavigation().stop(); this.getNavigation().stop();
this.setDeltaMovement(Vec3.ZERO); this.setDeltaMovement(Vec3.ZERO);
} }
@@ -791,6 +954,13 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
@Override @Override
protected void tickDeath() { protected void tickDeath() {
this.deathTime++; this.deathTime++;
this.setNoGravity(false);
Vec3 movement = this.getDeltaMovement();
if (this.onGround()) {
this.setDeltaMovement(movement.x * 0.6D, 0.0D, movement.z * 0.6D);
} else {
this.setDeltaMovement(movement.x * 0.8D, Math.min(-0.12D, movement.y), movement.z * 0.8D);
}
if (this.deathTime == 20 && !this.level().isClientSide()) { if (this.deathTime == 20 && !this.level().isClientSide()) {
this.level().broadcastEntityEvent(this, (byte) 60); this.level().broadcastEntityEvent(this, (byte) 60);
} }
@@ -835,71 +1005,85 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
} }
public boolean isFiringRoyalBeam() { public boolean isFiringRoyalBeam() {
return this.entityData.get(FIRING); for (RoyalHead.Slot slot : RoyalHead.Slot.values()) {
if (this.isFiringRoyalBeam(slot)) return true;
}
return false;
} }
public void setFiringRoyalBeam(boolean firing) { public void setFiringRoyalBeam(boolean firing) {
this.entityData.set(FIRING, firing);
} }
public RoyalBeamController beamController() { public RoyalBeamController beamController() {
return this.beamController; return this.beamControllers[0];
} }
public void setRoyalBeamEndPosition(@Nullable Vec3 position) { public void setRoyalBeamEndPosition(@Nullable Vec3 position) {
this.entityData.set(HAS_BEAM_END, position != null); this.setRoyalBeamEndPosition(RoyalHead.Slot.CENTER, position);
}
public void setRoyalBeamEndPosition(RoyalHead.Slot slot, @Nullable Vec3 position) {
int index = slot.ordinal();
this.entityData.set(BEAM_ACTIVE[index], position != null || this.beamControllers[index].isFiring());
if (position != null) { if (position != null) {
this.entityData.set(BEAM_END_X, (float) position.x); this.entityData.set(BEAM_END_X[index], (float) position.x);
this.entityData.set(BEAM_END_Y, (float) position.y); this.entityData.set(BEAM_END_Y[index], (float) position.y);
this.entityData.set(BEAM_END_Z, (float) position.z); this.entityData.set(BEAM_END_Z[index], (float) position.z);
} }
} }
@Nullable @Nullable
public Vec3 getRoyalBeamEndPosition() { public Vec3 getRoyalBeamEndPosition() {
if (!this.entityData.get(HAS_BEAM_END)) { return this.getRoyalBeamEndPosition(RoyalHead.Slot.CENTER);
return null;
}
return new Vec3(this.entityData.get(BEAM_END_X), this.entityData.get(BEAM_END_Y), this.entityData.get(BEAM_END_Z));
} }
public Vec3 getRoyalBeamShootFrom(float partialTicks) { public Vec3 getRoyalBeamShootFrom(float partialTicks) {
int slotIndex = this.activeBeamHead != null return this.getRoyalBeamShootFrom(RoyalHead.Slot.CENTER, partialTicks);
? this.activeBeamHead.slot().ordinal() }
: this.entityData.get(BEAM_HEAD_SLOT);
if (slotIndex >= 0 && slotIndex < RoyalHead.Slot.values().length) { public Vec3 getRoyalBeamShootFrom(RoyalHead.Slot slot, float partialTicks) {
double x = Mth.lerp(partialTicks, this.xo, this.getX()); double x = Mth.lerp(partialTicks, this.xo, this.getX());
double y = Mth.lerp(partialTicks, this.yo, this.getY()); double y = Mth.lerp(partialTicks, this.yo, this.getY());
double z = Mth.lerp(partialTicks, this.zo, this.getZ()); double z = Mth.lerp(partialTicks, this.zo, this.getZ());
float yaw = Mth.rotLerp(partialTicks, this.yRotO, this.getYRot()); float yaw = Mth.rotLerp(partialTicks, this.yRotO, this.getYRot());
return this.beamAnchor(RoyalHead.Slot.values()[slotIndex], x, y, z, yaw); return this.beamAnchor(slot, x, y, z, yaw);
}
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());
return new Vec3(x, y, z);
} }
public RoyalBeamElement getRoyalBeamElement() { public RoyalBeamElement getRoyalBeamElement() {
int ordinal = this.entityData.get(BEAM_ELEMENT); return this.getRoyalBeamElement(RoyalHead.Slot.CENTER);
}
public RoyalBeamElement getRoyalBeamElement(RoyalHead.Slot slot) {
int ordinal = this.entityData.get(BEAM_ELEMENT[slot.ordinal()]);
return ordinal >= 0 && ordinal < RoyalBeamElement.values().length return ordinal >= 0 && ordinal < RoyalBeamElement.values().length
? RoyalBeamElement.values()[ordinal] ? RoyalBeamElement.values()[ordinal]
: RoyalBeamElement.GENERIC; : RoyalBeamElement.GENERIC;
} }
public boolean isFiringRoyalBeam(RoyalHead.Slot slot) {
return this.entityData.get(BEAM_ACTIVE[slot.ordinal()]);
}
@Nullable
public Vec3 getRoyalBeamEndPosition(RoyalHead.Slot slot) {
int index = slot.ordinal();
if (!this.entityData.get(BEAM_ACTIVE[index])) return null;
return new Vec3(this.entityData.get(BEAM_END_X[index]), this.entityData.get(BEAM_END_Y[index]), this.entityData.get(BEAM_END_Z[index]));
}
@Nullable @Nullable
public RoyalHead.Slot getRoyalBeamHeadSlot() { public RoyalHead.Slot getRoyalBeamHeadSlot() {
int slotIndex = this.activeBeamHead != null for (RoyalHead.Slot slot : RoyalHead.Slot.values()) {
? this.activeBeamHead.slot().ordinal() if (this.isFiringRoyalBeam(slot)) return slot;
: this.entityData.get(BEAM_HEAD_SLOT); }
return slotIndex >= 0 && slotIndex < RoyalHead.Slot.values().length return null;
? RoyalHead.Slot.values()[slotIndex]
: null;
} }
@Override @Override
public void registerControllers(AnimatableManager.ControllerRegistrar controllers) { public void registerControllers(AnimatableManager.ControllerRegistrar controllers) {
controllers.add(new AnimationController<>(this, "locomotion", 4, this::locomotionPredicate)); controllers.add(new AnimationController<>(this, "locomotion", 4, this::locomotionPredicate));
controllers.add(new AnimationController<>(this, "wing_action", 0, RoyalBossEntity::headIdle)
.triggerableAnim("wing_gust", RawAnimation.begin().thenPlay("wing_gust")));
controllers.add(new AnimationController<>(this, "head_left", 0, RoyalBossEntity::headIdle) controllers.add(new AnimationController<>(this, "head_left", 0, RoyalBossEntity::headIdle)
.triggerableAnim("bite", RawAnimation.begin().thenPlay("bite_3")) .triggerableAnim("bite", RawAnimation.begin().thenPlay("bite_3"))
.triggerableAnim("shoot", RawAnimation.begin().thenPlay("shoot_3"))); .triggerableAnim("shoot", RawAnimation.begin().thenPlay("shoot_3")));
@@ -911,7 +1095,6 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
.triggerableAnim("shoot", RawAnimation.begin().thenPlay("shoot_2"))); .triggerableAnim("shoot", RawAnimation.begin().thenPlay("shoot_2")));
controllers.add(new AnimationController<>(this, "body_action", 0, this::bodyActionPredicate) controllers.add(new AnimationController<>(this, "body_action", 0, this::bodyActionPredicate)
.triggerableAnim("stomp", RawAnimation.begin().thenPlay("stomp")) .triggerableAnim("stomp", RawAnimation.begin().thenPlay("stomp"))
.triggerableAnim("wing_gust", RawAnimation.begin().thenPlay("wing_gust"))
.triggerableAnim("minion_spawn", RawAnimation.begin().thenPlay("minion_spawn"))); .triggerableAnim("minion_spawn", RawAnimation.begin().thenPlay("minion_spawn")));
} }
@@ -919,7 +1102,7 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
if (this.isDeadOrDying()) { if (this.isDeadOrDying()) {
return PlayState.STOP; return PlayState.STOP;
} }
if (this.isFlyingBoss()) { if (this.isRoyalFlying()) {
state.setAndContinue(FLY_ANIM); state.setAndContinue(FLY_ANIM);
} else if (state.isMoving()) { } else if (state.isMoving()) {
state.setAndContinue(WALK_ANIM); state.setAndContinue(WALK_ANIM);
@@ -940,6 +1123,18 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
return PlayState.STOP; return PlayState.STOP;
} }
@Override
public void addAdditionalSaveData(CompoundTag tag) {
super.addAdditionalSaveData(tag);
tag.putBoolean("RoyalFlying", this.isRoyalFlying());
}
@Override
public void readAdditionalSaveData(CompoundTag tag) {
super.readAdditionalSaveData(tag);
this.setRoyalFlying(!tag.contains("RoyalFlying") || tag.getBoolean("RoyalFlying"));
}
@Override @Override
public AnimatableInstanceCache getAnimatableInstanceCache() { public AnimatableInstanceCache getAnimatableInstanceCache() {
return this.geoCache; return this.geoCache;
@@ -5,6 +5,7 @@ import com.craisinlord.antarchy.content.entity.royal.beam.RoyalBeamElement;
import com.craisinlord.antarchy.config.AntarchySettings; import com.craisinlord.antarchy.config.AntarchySettings;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.core.particles.ParticleTypes; import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.syncher.EntityDataAccessor; import net.minecraft.network.syncher.EntityDataAccessor;
import net.minecraft.network.syncher.EntityDataSerializers; import net.minecraft.network.syncher.EntityDataSerializers;
@@ -32,12 +33,12 @@ import software.bernie.geckolib.animation.PlayState;
import software.bernie.geckolib.animation.RawAnimation; import software.bernie.geckolib.animation.RawAnimation;
import software.bernie.geckolib.util.GeckoLibUtil; import software.bernie.geckolib.util.GeckoLibUtil;
/** Shared travelling shell for the King's fireball and iceball. Impact rules stay element-specific. */
public class RoyalElementalProjectileEntity extends ThrowableProjectile implements GeoEntity { public class RoyalElementalProjectileEntity extends ThrowableProjectile implements GeoEntity {
private static final EntityDataAccessor<Integer> ATTACK_TYPE = private static final EntityDataAccessor<Integer> ATTACK_TYPE =
SynchedEntityData.defineId(RoyalElementalProjectileEntity.class, EntityDataSerializers.INT); SynchedEntityData.defineId(RoyalElementalProjectileEntity.class, EntityDataSerializers.INT);
private static final int FIREBALL = 0; private static final int FIREBALL = 0;
private static final int ICEBALL = 1; private static final int ICEBALL = 1;
private static final int DREAM_FIREBALL = 2;
private static final int MAX_LIFETIME = 100; private static final int MAX_LIFETIME = 100;
private final AnimatableInstanceCache geoCache = GeckoLibUtil.createInstanceCache(this); private final AnimatableInstanceCache geoCache = GeckoLibUtil.createInstanceCache(this);
@@ -52,7 +53,8 @@ public class RoyalElementalProjectileEntity extends ThrowableProjectile implemen
AntarchyObjects.ROYAL_ELEMENTAL_PROJECTILE.get(), level); AntarchyObjects.ROYAL_ELEMENTAL_PROJECTILE.get(), level);
projectile.setOwner(owner); projectile.setOwner(owner);
projectile.setPos(position.x, position.y, position.z); projectile.setPos(position.x, position.y, position.z);
projectile.setAttackType(element == RoyalBeamElement.ICE ? ICEBALL : FIREBALL); projectile.setAttackType(element == RoyalBeamElement.ICE ? ICEBALL
: element == RoyalBeamElement.DREAM_FIRE ? DREAM_FIREBALL : FIREBALL);
projectile.shoot(direction.x, direction.y, direction.z, element == RoyalBeamElement.ICE ? 0.8F : 1.05F, 0.0F); projectile.shoot(direction.x, direction.y, direction.z, element == RoyalBeamElement.ICE ? 0.8F : 1.05F, 0.0F);
return projectile; return projectile;
} }
@@ -66,6 +68,10 @@ public class RoyalElementalProjectileEntity extends ThrowableProjectile implemen
return this.entityData.get(ATTACK_TYPE) == ICEBALL; return this.entityData.get(ATTACK_TYPE) == ICEBALL;
} }
public boolean isDreamFireball() {
return this.entityData.get(ATTACK_TYPE) == DREAM_FIREBALL;
}
private void setAttackType(int type) { private void setAttackType(int type) {
this.entityData.set(ATTACK_TYPE, type); this.entityData.set(ATTACK_TYPE, type);
} }
@@ -87,7 +93,7 @@ public class RoyalElementalProjectileEntity extends ThrowableProjectile implemen
} }
super.tick(); super.tick();
if (this.level().isClientSide) { if (this.level().isClientSide) {
this.level().addParticle(this.isIceball() ? ParticleTypes.SNOWFLAKE : ParticleTypes.FLAME, this.level().addParticle(this.getTrailParticle(),
this.getX(), this.getY(), this.getZ(), 0.0D, 0.0D, 0.0D); this.getX(), this.getY(), this.getZ(), 0.0D, 0.0D, 0.0D);
} }
} }
@@ -127,20 +133,43 @@ public class RoyalElementalProjectileEntity extends ThrowableProjectile implemen
private void impactFire(ServerLevel level, Vec3 center) { private void impactFire(ServerLevel level, Vec3 center) {
DamageSource source = this.damageSources().mobProjectile(this, this.getOwner() instanceof LivingEntity living ? living : null); DamageSource source = this.damageSources().mobProjectile(this, this.getOwner() instanceof LivingEntity living ? living : null);
for (LivingEntity living : level.getEntitiesOfClass(LivingEntity.class, new AABB(center, center).inflate(5.0D), for (LivingEntity living : level.getEntitiesOfClass(LivingEntity.class, new AABB(center, center).inflate(5.0D),
entity -> entity.isAlive() && entity != this.getOwner())) { this::canDamageWithImpact)) {
double distance = Math.max(0.5D, living.position().distanceTo(center)); double distance = Math.max(0.5D, living.position().distanceTo(center));
float falloff = (float) Math.max(0.25D, 1.0D - distance / 5.0D); float falloff = (float) Math.max(0.25D, 1.0D - distance / 5.0D);
living.hurt(source, (float) (AntarchySettings.kingFireballDamage() * falloff)); living.hurt(source, (float) (AntarchySettings.kingFireballDamage() * falloff));
living.setRemainingFireTicks(Math.max(living.getRemainingFireTicks(), 120)); living.setRemainingFireTicks(Math.max(living.getRemainingFireTicks(), 120));
} }
RoyalBlockDestruction.destroySphere(level, this, center, AntarchySettings.kingFireballRadius(), AntarchySettings.kingElementalTerrainCap(), 80.0D, 0.15F); igniteImpact(level, center, this.isDreamFireball());
level.sendParticles(ParticleTypes.EXPLOSION_EMITTER, center.x, center.y, center.z, 4, 1.5D, 1.5D, 1.5D, 0.0D); level.sendParticles(ParticleTypes.EXPLOSION_EMITTER, center.x, center.y, center.z, 4, 1.5D, 1.5D, 1.5D, 0.0D);
} }
private void igniteImpact(ServerLevel level, Vec3 center, boolean dream) {
double radius = AntarchySettings.kingFireballRadius();
int minX = (int) Math.floor(center.x - radius);
int minY = (int) Math.floor(center.y - radius);
int minZ = (int) Math.floor(center.z - radius);
int maxX = (int) Math.floor(center.x + radius);
int maxY = (int) Math.floor(center.y + radius);
int maxZ = (int) Math.floor(center.z + radius);
var fire = dream
? BuiltInRegistries.BLOCK.getOptional(net.minecraft.resources.ResourceLocation.fromNamespaceAndPath(
"antarchy", "dream_fire")).orElse(Blocks.FIRE)
: Blocks.FIRE;
for (BlockPos pos : BlockPos.betweenClosed(minX, minY, minZ, maxX, maxY, maxZ)) {
if (pos.distToCenterSqr(center.x, center.y, center.z) > radius * radius || !level.isEmptyBlock(pos)) {
continue;
}
BlockState state = fire.defaultBlockState();
if (state.canSurvive(level, pos)) {
level.setBlockAndUpdate(pos, state);
}
}
}
private void impactIce(ServerLevel level, Vec3 center) { private void impactIce(ServerLevel level, Vec3 center) {
DamageSource source = this.damageSources().mobProjectile(this, this.getOwner() instanceof LivingEntity living ? living : null); DamageSource source = this.damageSources().mobProjectile(this, this.getOwner() instanceof LivingEntity living ? living : null);
for (LivingEntity living : level.getEntitiesOfClass(LivingEntity.class, new AABB(center, center).inflate(5.0D), for (LivingEntity living : level.getEntitiesOfClass(LivingEntity.class, new AABB(center, center).inflate(5.0D),
entity -> entity.isAlive() && entity != this.getOwner())) { this::canDamageWithImpact)) {
living.hurt(source, (float) AntarchySettings.kingIceballDamage()); living.hurt(source, (float) AntarchySettings.kingIceballDamage());
living.setTicksFrozen(Math.min(living.getTicksRequiredToFreeze() + 80, living.getTicksFrozen() + 180)); living.setTicksFrozen(Math.min(living.getTicksRequiredToFreeze() + 80, living.getTicksFrozen() + 180));
living.addEffect(new net.minecraft.world.effect.MobEffectInstance(net.minecraft.world.effect.MobEffects.MOVEMENT_SLOWDOWN, 120, 3)); living.addEffect(new net.minecraft.world.effect.MobEffectInstance(net.minecraft.world.effect.MobEffects.MOVEMENT_SLOWDOWN, 120, 3));
@@ -149,6 +178,14 @@ public class RoyalElementalProjectileEntity extends ThrowableProjectile implemen
level.sendParticles(ParticleTypes.SNOWFLAKE, center.x, center.y, center.z, 80, 4.0D, 2.0D, 4.0D, 0.08D); level.sendParticles(ParticleTypes.SNOWFLAKE, center.x, center.y, center.z, 80, 4.0D, 2.0D, 4.0D, 0.08D);
} }
private boolean canDamageWithImpact(LivingEntity target) {
Entity owner = this.getOwner();
if (!target.isAlive() || target == owner) {
return false;
}
return !(owner instanceof RoyalBossEntity royalBoss) || royalBoss.canDamageWithRoyalAttack(target);
}
private void coverInIce(ServerLevel level, Vec3 center, double radius, int cap) { private void coverInIce(ServerLevel level, Vec3 center, double radius, int cap) {
int changed = 0; int changed = 0;
int minX = (int) Math.floor(center.x - radius); int minX = (int) Math.floor(center.x - radius);
@@ -168,7 +205,15 @@ public class RoyalElementalProjectileEntity extends ThrowableProjectile implemen
} }
protected net.minecraft.core.particles.ParticleOptions getTrailParticle() { protected net.minecraft.core.particles.ParticleOptions getTrailParticle() {
return this.isIceball() ? ParticleTypes.SNOWFLAKE : ParticleTypes.FLAME; if (this.isIceball()) {
return ParticleTypes.SNOWFLAKE;
}
if (this.isDreamFireball()
&& BuiltInRegistries.PARTICLE_TYPE.get(net.minecraft.resources.ResourceLocation.fromNamespaceAndPath(
"antarchy", "dream_fire_flame")) instanceof net.minecraft.core.particles.ParticleOptions dreamFlame) {
return dreamFlame;
}
return ParticleTypes.FLAME;
} }
@Override @Override
@@ -22,7 +22,6 @@ import software.bernie.geckolib.animation.RawAnimation;
import software.bernie.geckolib.util.GeckoLibUtil; import software.bernie.geckolib.util.GeckoLibUtil;
public class RoyalIceSpikeEntity extends Entity implements GeoEntity { public class RoyalIceSpikeEntity extends Entity implements GeoEntity {
// The exported asset contains one coordinated animation for all four spike groups.
private static final RawAnimation SPIKE_ANIMATION = RawAnimation.begin().thenPlay("animation"); private static final RawAnimation SPIKE_ANIMATION = RawAnimation.begin().thenPlay("animation");
private static final String ANIMATION_CONTROLLER = "ice_spike"; private static final String ANIMATION_CONTROLLER = "ice_spike";
private final AnimatableInstanceCache geoCache = GeckoLibUtil.createInstanceCache(this); private final AnimatableInstanceCache geoCache = GeckoLibUtil.createInstanceCache(this);
@@ -4,6 +4,12 @@ import com.craisinlord.antarchy.config.AntarchySettings;
import com.craisinlord.antarchy.content.AntarchySoundEvents; import com.craisinlord.antarchy.content.AntarchySoundEvents;
import com.craisinlord.antarchy.content.AntarchyTags; import com.craisinlord.antarchy.content.AntarchyTags;
import com.craisinlord.antarchy.content.entity.royal.beam.RoyalBeamElement; import com.craisinlord.antarchy.content.entity.royal.beam.RoyalBeamElement;
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 com.craisinlord.antarchy.content.effect.RoyalEffectEligibility;
import com.craisinlord.antarchy.content.effect.RoyalEffectHooks;
import com.craisinlord.antarchy.content.time.TimeDilationApi;
import java.util.UUID; import java.util.UUID;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.CompoundTag;
@@ -20,10 +26,12 @@ import net.minecraft.world.InteractionResult;
import net.minecraft.world.damagesource.DamageSource; import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.entity.AgeableMob; import net.minecraft.world.entity.AgeableMob;
import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityDimensions;
import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.Mob; import net.minecraft.world.entity.Mob;
import net.minecraft.world.entity.MoverType; import net.minecraft.world.entity.MoverType;
import net.minecraft.world.entity.Pose;
import net.minecraft.world.entity.TamableAnimal; import net.minecraft.world.entity.TamableAnimal;
import net.minecraft.world.entity.ai.attributes.AttributeSupplier; import net.minecraft.world.entity.ai.attributes.AttributeSupplier;
import net.minecraft.world.entity.ai.attributes.Attributes; import net.minecraft.world.entity.ai.attributes.Attributes;
@@ -40,14 +48,18 @@ import net.minecraft.world.entity.ai.goal.target.OwnerHurtTargetGoal;
import net.minecraft.world.entity.ai.navigation.FlyingPathNavigation; import net.minecraft.world.entity.ai.navigation.FlyingPathNavigation;
import net.minecraft.world.entity.ai.navigation.GroundPathNavigation; import net.minecraft.world.entity.ai.navigation.GroundPathNavigation;
import net.minecraft.world.entity.ai.navigation.PathNavigation; import net.minecraft.world.entity.ai.navigation.PathNavigation;
import net.minecraft.world.entity.monster.Enemy;
import net.minecraft.world.entity.animal.FlyingAnimal; import net.minecraft.world.entity.animal.FlyingAnimal;
import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items; import net.minecraft.world.item.Items;
import net.minecraft.world.level.Level; import net.minecraft.world.level.Level;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.pathfinder.PathType; import net.minecraft.world.level.pathfinder.PathType;
import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.Vec3;
import net.minecraft.world.effect.MobEffectInstance;
import net.minecraft.core.particles.ParticleTypes;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import software.bernie.geckolib.animatable.GeoEntity; import software.bernie.geckolib.animatable.GeoEntity;
import software.bernie.geckolib.animatable.instance.AnimatableInstanceCache; import software.bernie.geckolib.animatable.instance.AnimatableInstanceCache;
@@ -73,6 +85,20 @@ public abstract class RoyalMountEntity extends TamableAnimal implements GeoEntit
SynchedEntityData.defineId(RoyalMountEntity.class, EntityDataSerializers.BOOLEAN); SynchedEntityData.defineId(RoyalMountEntity.class, EntityDataSerializers.BOOLEAN);
private static final EntityDataAccessor<Boolean> SADDLED = private static final EntityDataAccessor<Boolean> SADDLED =
SynchedEntityData.defineId(RoyalMountEntity.class, EntityDataSerializers.BOOLEAN); SynchedEntityData.defineId(RoyalMountEntity.class, EntityDataSerializers.BOOLEAN);
private static final EntityDataAccessor<Boolean> BEAM_ACTIVE =
SynchedEntityData.defineId(RoyalMountEntity.class, EntityDataSerializers.BOOLEAN);
private static final EntityDataAccessor<Integer> BEAM_HEAD =
SynchedEntityData.defineId(RoyalMountEntity.class, EntityDataSerializers.INT);
private static final EntityDataAccessor<Boolean> HAS_BEAM_END =
SynchedEntityData.defineId(RoyalMountEntity.class, EntityDataSerializers.BOOLEAN);
private static final EntityDataAccessor<Float> BEAM_END_X =
SynchedEntityData.defineId(RoyalMountEntity.class, EntityDataSerializers.FLOAT);
private static final EntityDataAccessor<Float> BEAM_END_Y =
SynchedEntityData.defineId(RoyalMountEntity.class, EntityDataSerializers.FLOAT);
private static final EntityDataAccessor<Float> BEAM_END_Z =
SynchedEntityData.defineId(RoyalMountEntity.class, EntityDataSerializers.FLOAT);
private static final EntityDataAccessor<Float> GROWTH_PROGRESS =
SynchedEntityData.defineId(RoyalMountEntity.class, EntityDataSerializers.FLOAT);
private static final RawAnimation IDLE_ANIM = RawAnimation.begin().thenLoop("idle"); private static final RawAnimation IDLE_ANIM = RawAnimation.begin().thenLoop("idle");
private static final RawAnimation QUIRK_ANIM = RawAnimation.begin().thenLoop("quirk"); private static final RawAnimation QUIRK_ANIM = RawAnimation.begin().thenLoop("quirk");
@@ -85,7 +111,11 @@ public abstract class RoyalMountEntity extends TamableAnimal implements GeoEntit
private static final int ACTION_TICKS = 12; private static final int ACTION_TICKS = 12;
private static final int BITE_HIT_TICK = 6; private static final int BITE_HIT_TICK = 6;
private static final int GROW_TICKS = 24000; private static final int BEAM_WINDUP_TICKS = 24;
private static final int BEAM_COOLDOWN_TICKS = 240;
private static final int BEAM_DURATION_TICKS = 12;
private static final int COMMAND_COOLDOWN_TICKS = 360;
private static final int TIME_FIELD_COOLDOWN_TICKS = 260;
private final AnimatableInstanceCache geoCache = GeckoLibUtil.createInstanceCache(this); private final AnimatableInstanceCache geoCache = GeckoLibUtil.createInstanceCache(this);
private final MoveControl groundMoveControl; private final MoveControl groundMoveControl;
@@ -100,6 +130,12 @@ public abstract class RoyalMountEntity extends TamableAnimal implements GeoEntit
private int flapSoundTimer; private int flapSoundTimer;
private boolean riderAscendPressed; private boolean riderAscendPressed;
private boolean riderDescendPressed; private boolean riderDescendPressed;
private final RoyalBeamController beamController = new RoyalBeamController(this);
private int beamWindupTicks;
private int beamCooldownTicks;
private int beamHead;
private int commandCooldownTicks;
private int timeFieldCooldownTicks;
protected RoyalMountEntity(EntityType<? extends RoyalMountEntity> entityType, Level level) { protected RoyalMountEntity(EntityType<? extends RoyalMountEntity> entityType, Level level) {
super(entityType, level); super(entityType, level);
@@ -167,7 +203,7 @@ public abstract class RoyalMountEntity extends TamableAnimal implements GeoEntit
this.goalSelector.addGoal(1, new SitWhenOrderedToGoal(this) { this.goalSelector.addGoal(1, new SitWhenOrderedToGoal(this) {
@Override @Override
public boolean canUse() { public boolean canUse() {
return !RoyalMountEntity.this.isBaby() && super.canUse(); return super.canUse();
} }
}); });
this.goalSelector.addGoal(2, new RoyalMountSpitGoal(this)); this.goalSelector.addGoal(2, new RoyalMountSpitGoal(this));
@@ -182,14 +218,14 @@ public abstract class RoyalMountEntity extends TamableAnimal implements GeoEntit
@Override @Override
public boolean canUse() { public boolean canUse() {
return !RoyalMountEntity.this.isBaby() && !RoyalMountEntity.this.isVehicle() && super.canUse(); return !RoyalMountEntity.this.isVehicle() && super.canUse();
} }
}); });
this.goalSelector.addGoal(4, new RoyalMountFollowOwnerGoal(this)); this.goalSelector.addGoal(4, new RoyalMountFollowOwnerGoal(this));
this.goalSelector.addGoal(6, new WaterAvoidingRandomStrollGoal(this, 0.8D) { this.goalSelector.addGoal(6, new WaterAvoidingRandomStrollGoal(this, 0.8D) {
@Override @Override
public boolean canUse() { public boolean canUse() {
return !RoyalMountEntity.this.isBaby() && !RoyalMountEntity.this.isVehicle() return !RoyalMountEntity.this.isVehicle()
&& !RoyalMountEntity.this.isOrderedToSit() && super.canUse(); && !RoyalMountEntity.this.isOrderedToSit() && super.canUse();
} }
}); });
@@ -197,25 +233,25 @@ public abstract class RoyalMountEntity extends TamableAnimal implements GeoEntit
this.goalSelector.addGoal(8, new RandomLookAroundGoal(this) { this.goalSelector.addGoal(8, new RandomLookAroundGoal(this) {
@Override @Override
public boolean canUse() { public boolean canUse() {
return !RoyalMountEntity.this.isBaby() && super.canUse(); return super.canUse();
} }
}); });
this.targetSelector.addGoal(1, new OwnerHurtByTargetGoal(this) { this.targetSelector.addGoal(1, new OwnerHurtByTargetGoal(this) {
@Override @Override
public boolean canUse() { public boolean canUse() {
return !RoyalMountEntity.this.isBaby() && super.canUse(); return super.canUse();
} }
}); });
this.targetSelector.addGoal(2, new OwnerHurtTargetGoal(this) { this.targetSelector.addGoal(2, new OwnerHurtTargetGoal(this) {
@Override @Override
public boolean canUse() { public boolean canUse() {
return !RoyalMountEntity.this.isBaby() && super.canUse(); return super.canUse();
} }
}); });
this.targetSelector.addGoal(3, new HurtByTargetGoal(this) { this.targetSelector.addGoal(3, new HurtByTargetGoal(this) {
@Override @Override
public boolean canUse() { public boolean canUse() {
return !RoyalMountEntity.this.isBaby() && super.canUse(); return super.canUse();
} }
}.setAlertOthers()); }.setAlertOthers());
} }
@@ -226,6 +262,13 @@ public abstract class RoyalMountEntity extends TamableAnimal implements GeoEntit
builder.define(ANIM_STATE, ANIM_IDLE); builder.define(ANIM_STATE, ANIM_IDLE);
builder.define(FLYING, false); builder.define(FLYING, false);
builder.define(SADDLED, false); builder.define(SADDLED, false);
builder.define(BEAM_ACTIVE, false);
builder.define(BEAM_HEAD, -1);
builder.define(HAS_BEAM_END, false);
builder.define(BEAM_END_X, 0.0F);
builder.define(BEAM_END_Y, 0.0F);
builder.define(BEAM_END_Z, 0.0F);
builder.define(GROWTH_PROGRESS, 0.0F);
} }
@Override @Override
@@ -233,6 +276,10 @@ public abstract class RoyalMountEntity extends TamableAnimal implements GeoEntit
super.addAdditionalSaveData(tag); super.addAdditionalSaveData(tag);
tag.putBoolean("Saddled", this.isSaddled()); tag.putBoolean("Saddled", this.isSaddled());
tag.putBoolean("Flying", this.isFlying()); tag.putBoolean("Flying", this.isFlying());
tag.putInt("BeamCooldown", this.beamCooldownTicks);
tag.putInt("CommandCooldown", this.commandCooldownTicks);
tag.putInt("TimeFieldCooldown", this.timeFieldCooldownTicks);
tag.putFloat("GrowthProgress", this.getGrowthProgress());
} }
@Override @Override
@@ -240,12 +287,30 @@ public abstract class RoyalMountEntity extends TamableAnimal implements GeoEntit
super.readAdditionalSaveData(tag); super.readAdditionalSaveData(tag);
this.setSaddled(tag.getBoolean("Saddled")); this.setSaddled(tag.getBoolean("Saddled"));
this.setFlying(tag.getBoolean("Flying")); this.setFlying(tag.getBoolean("Flying"));
this.actionTicks = 0;
this.actionHit = false;
this.spitCooldown = 0;
this.beamCooldownTicks = Math.max(0, tag.getInt("BeamCooldown"));
this.commandCooldownTicks = Math.max(0, tag.getInt("CommandCooldown"));
this.timeFieldCooldownTicks = Math.max(0, tag.getInt("TimeFieldCooldown"));
this.setGrowthProgress(tag.contains("GrowthProgress") ? tag.getFloat("GrowthProgress") : 0.0F);
} }
public boolean isFlying() { public boolean isFlying() {
return this.entityData.get(FLYING); return this.entityData.get(FLYING);
} }
@Override
public void onSyncedDataUpdated(EntityDataAccessor<?> key) {
super.onSyncedDataUpdated(key);
if (FLYING.equals(key) && this.groundMoveControl != null) {
boolean flying = this.entityData.get(FLYING);
this.moveControl = flying ? this.flyingMoveControl : this.groundMoveControl;
this.navigation = flying ? this.flyingNavigation : this.groundNavigation;
this.setNoGravity(flying);
}
}
public void setFlying(boolean flying) { public void setFlying(boolean flying) {
if (this.entityData.get(FLYING) != flying) { if (this.entityData.get(FLYING) != flying) {
this.entityData.set(FLYING, flying); this.entityData.set(FLYING, flying);
@@ -274,12 +339,25 @@ public abstract class RoyalMountEntity extends TamableAnimal implements GeoEntit
this.entityData.set(ANIM_STATE, state); this.entityData.set(ANIM_STATE, state);
} }
public float getAgeScale() { public float getGrowthScale() {
if (!this.isBaby()) { return 1.0F + 14.0F * this.getGrowthProgress();
return 1.0F;
} }
float progress = 1.0F - Mth.clamp(-this.getAge() / (float) GROW_TICKS, 0.0F, 1.0F);
return 0.35F + 0.65F * progress; public float getGrowthProgress() {
return this.entityData.get(GROWTH_PROGRESS);
}
public void setGrowthProgress(float progress) {
float clamped = Mth.clamp(progress, 0.0F, 1.0F);
if (this.getGrowthProgress() != clamped) {
this.entityData.set(GROWTH_PROGRESS, clamped);
this.refreshDimensions();
}
}
@Override
public EntityDimensions getDefaultDimensions(Pose pose) {
return super.getDefaultDimensions(pose).scale(this.getGrowthScale());
} }
@Override @Override
@@ -297,9 +375,7 @@ public abstract class RoyalMountEntity extends TamableAnimal implements GeoEntit
stack.shrink(1); stack.shrink(1);
} }
this.heal(8.0F); this.heal(8.0F);
if (this.isBaby()) { this.setGrowthProgress(this.getGrowthProgress() + (1.0F - this.getGrowthProgress()) * 0.1F);
this.ageUp((int) (-this.getAge() * 0.1F), true);
}
if (!this.isTame() && this.random.nextInt(3) == 0) { if (!this.isTame() && this.random.nextInt(3) == 0) {
this.tame(player); this.tame(player);
this.level().broadcastEntityEvent(this, (byte) 7); this.level().broadcastEntityEvent(this, (byte) 7);
@@ -337,7 +413,7 @@ public abstract class RoyalMountEntity extends TamableAnimal implements GeoEntit
return InteractionResult.sidedSuccess(this.level().isClientSide); return InteractionResult.sidedSuccess(this.level().isClientSide);
} }
if (stack.isEmpty() && !this.isBaby() && !player.isPassenger()) { if (stack.isEmpty() && !player.isPassenger()) {
if (!this.level().isClientSide) { if (!this.level().isClientSide) {
this.setOrderedToSit(false); this.setOrderedToSit(false);
this.setInSittingPose(false); this.setInSittingPose(false);
@@ -365,10 +441,6 @@ public abstract class RoyalMountEntity extends TamableAnimal implements GeoEntit
public void tick() { public void tick() {
super.tick(); super.tick();
if (this.isBaby()) {
this.refreshDimensions();
}
if (this.level().isClientSide) { if (this.level().isClientSide) {
return; return;
} }
@@ -376,6 +448,7 @@ public abstract class RoyalMountEntity extends TamableAnimal implements GeoEntit
if (this.spitCooldown > 0) { if (this.spitCooldown > 0) {
this.spitCooldown--; this.spitCooldown--;
} }
this.tickRoyalAbilities();
if ((this.isFlying() || !this.onGround()) && --this.flapSoundTimer <= 0) { if ((this.isFlying() || !this.onGround()) && --this.flapSoundTimer <= 0) {
this.flapSoundTimer = 24 + this.random.nextInt(12); this.flapSoundTimer = 24 + this.random.nextInt(12);
@@ -406,6 +479,110 @@ public abstract class RoyalMountEntity extends TamableAnimal implements GeoEntit
} }
} }
private void tickRoyalAbilities() {
if (this.beamCooldownTicks > 0) this.beamCooldownTicks--;
if (this.commandCooldownTicks > 0) this.commandCooldownTicks--;
if (this.timeFieldCooldownTicks > 0) this.timeFieldCooldownTicks--;
if (this.beamWindupTicks > 0) {
this.beamWindupTicks--;
if (this.beamWindupTicks == 0) {
this.beamController.start(this.beamShootFrom(), this.beamTargetPosition(), BEAM_DURATION_TICKS);
this.entityData.set(BEAM_ACTIVE, true);
}
}
if (this.beamController.isFiring()) {
this.beamController.tick(this.beamShootFrom(), this.getTarget(), this.beamSettings(), RoyalBeamTerrainMode.NONE, this::setBeamEndPosition);
if (!this.beamController.isFiring()) {
this.entityData.set(BEAM_ACTIVE, false);
this.entityData.set(BEAM_HEAD, -1);
this.beamCooldownTicks = BEAM_COOLDOWN_TICKS;
}
return;
}
if (this.isOrderedToSit()) return;
LivingEntity target = this.getTarget();
if (target == null && this.getControllingPassenger() instanceof Player) {
target = this.frontTarget();
if (target != null) this.setTarget(target);
}
if (target == null || !target.isAlive() || !this.hasLineOfSight(target)) return;
double targetDistance = this.distanceTo(target);
if (this.beamCooldownTicks <= 0 && this.beamWindupTicks <= 0 && targetDistance > 7.0D && targetDistance < 28.0D && this.tickCount % 20 == 0) {
this.beamHead = (this.beamHead + 1) % 3;
this.entityData.set(BEAM_HEAD, this.beamHead);
this.beamWindupTicks = BEAM_WINDUP_TICKS;
this.setAnimState(this.isFlying() ? ANIM_FLY_SHOOT : ANIM_SHOOT);
this.triggerAnim("main", this.isFlying() ? "fly_shoot" : "shoot");
this.playSound(this.shootSound(), 0.9F, 0.75F);
}
if (this instanceof PrinceEntity && this.commandCooldownTicks <= 0 && this.tickCount % 20 == 0) {
this.performPrinceCommand();
}
if (this instanceof PrincessEntity && this.timeFieldCooldownTicks <= 0 && this.tickCount % 20 == 0) {
this.performPrincessTimeField(target);
}
}
private void performPrinceCommand() {
if (!(this.level() instanceof ServerLevel level) || RoyalEffectHooks.commandedHolder() == null) return;
int applied = 0;
for (Mob mob : level.getEntitiesOfClass(Mob.class, this.getBoundingBox().inflate(16.0D), mob ->
mob != this && mob.isAlive() && mob instanceof Enemy && !mob.isAlliedTo(this)
&& !(mob instanceof RoyalBossEntity) && RoyalEffectEligibility.canApplyCommanded(mob))) {
if (applied >= 5) break;
if (mob.addEffect(new MobEffectInstance(RoyalEffectHooks.commandedHolder(), 300), this)) {
applied++;
level.sendParticles(ParticleTypes.HAPPY_VILLAGER, mob.getX(), mob.getY() + mob.getBbHeight(), mob.getZ(), 4, 0.2D, 0.2D, 0.2D, 0.02D);
}
}
if (applied > 0) {
this.commandCooldownTicks = COMMAND_COOLDOWN_TICKS;
this.playSound(this.shootSound(), 1.0F, 0.65F);
}
}
private void performPrincessTimeField(@Nullable LivingEntity target) {
if (!(this.level() instanceof ServerLevel level)) return;
Vec3 center = target != null && target.isAlive() ? target.position() : this.position();
TimeDilationApi.createField(level, center, 8.0D, 0.45D, 100, this.getUUID());
this.timeFieldCooldownTicks = TIME_FIELD_COOLDOWN_TICKS;
level.sendParticles(ParticleTypes.END_ROD, center.x, center.y + 0.5D, center.z, 28, 1.2D, 0.6D, 1.2D, 0.03D);
}
private RoyalBeamSettings beamSettings() {
float damage = this instanceof PrincessEntity ? 10.0F : 12.0F;
return new RoyalBeamSettings(28.0D, 0.08D, 4.0D, BEAM_DURATION_TICKS, 6, BEAM_COOLDOWN_TICKS,
0.35F, 0.45F, damage, 0.45F, 3, 0.0D, 0.0F, 0.0F, 0, 0.0F, 0.0F, 0.0F, false, true);
}
private Vec3 beamTargetPosition() {
LivingEntity target = this.getTarget();
return target != null ? target.getEyePosition() : this.getEyePosition().add(this.getLookAngle().scale(20.0D));
}
public Vec3 beamShootFrom() {
float scale = this.getGrowthScale();
Vec3 forward = Vec3.directionFromRotation(0.0F, this.getYRot());
Vec3 side = new Vec3(-forward.z, 0.0D, forward.x);
return this.position().add(0.0D, 1.0D * scale, 0.0D).add(forward.scale(1.05D * scale)).add(side.scale((this.getBeamHead() - 1) * 0.72D * scale));
}
public int getBeamHead() { return this.entityData.get(BEAM_HEAD); }
public boolean isFiringBeam() { return this.entityData.get(BEAM_ACTIVE); }
public Vec3 getBeamEndPosition() {
return this.entityData.get(HAS_BEAM_END) ? new Vec3(this.entityData.get(BEAM_END_X), this.entityData.get(BEAM_END_Y), this.entityData.get(BEAM_END_Z)) : null;
}
public RoyalBeamElement getBeamElement() { return RoyalBeamElement.GENERIC; }
public RoyalBeamController beamController() { return this.beamController; }
private void setBeamEndPosition(@Nullable Vec3 position) {
this.entityData.set(HAS_BEAM_END, position != null);
if (position != null) {
this.entityData.set(BEAM_END_X, (float) position.x);
this.entityData.set(BEAM_END_Y, (float) position.y);
this.entityData.set(BEAM_END_Z, (float) position.z);
}
}
private void tickFlightState() { private void tickFlightState() {
if (this.isVehicle()) { if (this.isVehicle()) {
return; return;
@@ -540,6 +717,11 @@ public abstract class RoyalMountEntity extends TamableAnimal implements GeoEntit
public void setRiderAscend(boolean pressed) { public void setRiderAscend(boolean pressed) {
this.riderAscendPressed = pressed; this.riderAscendPressed = pressed;
if (pressed && !this.isFlying() && this.getControllingPassenger() != null) {
this.setFlying(true);
this.setDeltaMovement(this.getDeltaMovement().add(0.0D, 0.35D, 0.0D));
this.hasImpulse = true;
}
} }
public void setRiderDescend(boolean pressed) { public void setRiderDescend(boolean pressed) {
@@ -566,8 +748,8 @@ public abstract class RoyalMountEntity extends TamableAnimal implements GeoEntit
if (!this.hasPassenger(passenger)) { if (!this.hasPassenger(passenger)) {
return; return;
} }
double yOffset = (this.isFlying() ? 1.55D : 1.35D) * this.getAgeScale(); double yOffset = (this.isFlying() ? 1.55D : 1.35D) * this.getGrowthScale() - 1.0D;
Vec3 back = this.getLookAngle().scale(-0.35D * this.getAgeScale()); Vec3 back = this.getLookAngle().scale(-0.35D * this.getGrowthScale());
moveFunction.accept(passenger, this.getX() + back.x, this.getY() + yOffset, this.getZ() + back.z); moveFunction.accept(passenger, this.getX() + back.x, this.getY() + yOffset, this.getZ() + back.z);
} }
@@ -1,5 +1,6 @@
package com.craisinlord.antarchy.content.entity.royal; package com.craisinlord.antarchy.content.entity.royal;
import net.minecraft.core.BlockPos;
import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.ai.goal.Goal; import net.minecraft.world.entity.ai.goal.Goal;
import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.Vec3;
@@ -58,7 +59,7 @@ public class RoyalMountFollowOwnerGoal extends Goal {
double distanceSqr = this.mount.distanceToSqr(this.owner); double distanceSqr = this.mount.distanceToSqr(this.owner);
if (distanceSqr > TELEPORT_DISTANCE * TELEPORT_DISTANCE) { if (distanceSqr > TELEPORT_DISTANCE * TELEPORT_DISTANCE) {
this.mount.teleportTo(this.owner.getX(), this.owner.getY(), this.owner.getZ()); this.trySafeTeleport();
return; return;
} }
@@ -81,4 +82,29 @@ public class RoyalMountFollowOwnerGoal extends Goal {
public boolean requiresUpdateEveryTick() { public boolean requiresUpdateEveryTick() {
return true; return true;
} }
private void trySafeTeleport() {
Vec3 ownerPosition = this.owner.position();
BlockPos ownerBlock = this.owner.blockPosition();
for (int radius = 0; radius <= 2; radius++) {
for (int dx = -radius; dx <= radius; dx++) {
for (int dz = -radius; dz <= radius; dz++) {
for (int dy = -1; dy <= 2; dy++) {
double x = ownerPosition.x + dx + 0.5D;
double y = ownerBlock.getY() + dy;
double z = ownerPosition.z + dz + 0.5D;
BlockPos floor = BlockPos.containing(x, y - 0.1D, z);
var destination = this.mount.getBoundingBox().move(x - this.mount.getX(), y - this.mount.getY(), z - this.mount.getZ());
if (!this.mount.level().noCollision(this.mount, destination)) {
continue;
}
if (this.mount.isFlying() || !this.mount.level().getBlockState(floor).getCollisionShape(this.mount.level(), floor).isEmpty()) {
this.mount.teleportTo(x, y, z);
return;
}
}
}
}
}
}
} }
@@ -13,7 +13,7 @@ public class RoyalMountSpitGoal extends Goal {
@Override @Override
public boolean canUse() { public boolean canUse() {
if (this.mount.isBaby() || this.mount.isVehicle() || this.mount.isOrderedToSit()) { if (this.mount.isVehicle() || this.mount.isOrderedToSit()) {
return false; return false;
} }
LivingEntity target = this.mount.getTarget(); LivingEntity target = this.mount.getTarget();
@@ -8,7 +8,6 @@ import java.util.Map;
import java.util.function.ToIntFunction; import java.util.function.ToIntFunction;
import net.minecraft.util.RandomSource; import net.minecraft.util.RandomSource;
/** Shared server-side arbitration for Royal attack lifecycles. */
public final class RoyalAttackScheduler { public final class RoyalAttackScheduler {
public interface Action { public interface Action {
default void onStart() {} default void onStart() {}
@@ -3,7 +3,6 @@ package com.craisinlord.antarchy.content.entity.royal.attack;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
/** Owns persistent per-tick effects started by a Royal attack. */
public final class RoyalEffectController { public final class RoyalEffectController {
private record Effect(Runnable tick, Runnable end, int duration, int age) { private record Effect(Runnable tick, Runnable end, int duration, int age) {
private Effect advance() { private Effect advance() {
@@ -1,5 +1,6 @@
package com.craisinlord.antarchy.content.entity.royal.beam; package com.craisinlord.antarchy.content.entity.royal.beam;
import com.craisinlord.antarchy.content.entity.royal.RoyalBossEntity;
import java.util.List; import java.util.List;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
@@ -34,6 +35,7 @@ public final class RoyalBeamController {
private int beamAgeTicks; private int beamAgeTicks;
private int trackingDelayTicks; private int trackingDelayTicks;
private int terrainMutationsThisTick; private int terrainMutationsThisTick;
private final Set<Integer> damagedThisTick = new HashSet<>();
private static final int ICE_MUTATIONS_PER_TICK = 3; private static final int ICE_MUTATIONS_PER_TICK = 3;
private static final float ICE_IMPACT_RADIUS = 2.0F; private static final float ICE_IMPACT_RADIUS = 2.0F;
@@ -137,17 +139,17 @@ public final class RoyalBeamController {
Vec3 direction = beamEnd.subtract(shootFrom).normalize(); Vec3 direction = beamEnd.subtract(shootFrom).normalize();
double distance = shootFrom.distanceTo(beamEnd); double distance = shootFrom.distanceTo(beamEnd);
DamageSource damageSource = this.owner.damageSources().mobAttack(this.owner); DamageSource damageSource = this.owner.damageSources().mobAttack(this.owner);
Set<Integer> damagedThisTick = new HashSet<>(); this.damagedThisTick.clear();
boolean pathMutates = terrainMode == RoyalBeamTerrainMode.DESTROY; boolean pathMutates = terrainMode == RoyalBeamTerrainMode.DESTROY;
for (double walked = settings.pathStep(); walked < Math.min(distance, settings.range()); walked += settings.pathStep()) { for (double walked = settings.pathStep(); walked < Math.min(distance, settings.range()); walked += settings.pathStep()) {
Vec3 sample = shootFrom.add(direction.scale(walked)); Vec3 sample = shootFrom.add(direction.scale(walked));
hurtEntitiesAround(sample, settings.pathDamageRadius(), settings.damage(), settings.knockback(), damageSource, settings.requireLineOfSightForDamage(), damagedThisTick); hurtEntitiesAround(sample, settings.pathDamageRadius(), settings.damage(), settings.knockback(), damageSource, settings.requireLineOfSightForDamage(), this.damagedThisTick);
if (pathMutates && shouldMutateTerrain(settings, walked)) { if (pathMutates && shouldMutateTerrain(settings, walked)) {
mutateTerrainAround(sample, settings.pathTerrainRadius(), settings, terrainMode); mutateTerrainAround(sample, settings.pathTerrainRadius(), settings, terrainMode);
} }
} }
hurtEntitiesAround(beamEnd, settings.impactDamageRadius(), settings.damage(), settings.knockback(), damageSource, settings.requireLineOfSightForDamage(), damagedThisTick); hurtEntitiesAround(beamEnd, settings.impactDamageRadius(), settings.damage(), settings.knockback(), damageSource, settings.requireLineOfSightForDamage(), this.damagedThisTick);
if (!shouldMutateTerrain(settings, distance)) { if (!shouldMutateTerrain(settings, distance)) {
return; return;
@@ -193,6 +195,7 @@ public final class RoyalBeamController {
if (living.is(this.owner) if (living.is(this.owner)
|| this.owner.isAlliedTo(living) || this.owner.isAlliedTo(living)
|| living.getType() == this.owner.getType() || living.getType() == this.owner.getType()
|| this.owner instanceof RoyalBossEntity royalBoss && !royalBoss.canDamageWithRoyalAttack(living)
|| living.distanceToSqr(center) > radius * radius || living.distanceToSqr(center) > radius * radius
|| damagedThisTick.contains(living.getId()) || damagedThisTick.contains(living.getId())
|| requireLineOfSight && !canEntityBeHurtFrom(center, living)) { || requireLineOfSight && !canEntityBeHurtFrom(center, living)) {
@@ -4,5 +4,9 @@ public enum RoyalBeamElement {
GENERIC, GENERIC,
FIRE, FIRE,
LIGHTNING, LIGHTNING,
ICE ICE,
QUEEN_PURPLE,
QUEEN_RED,
QUEEN_BLACK,
DREAM_FIRE
} }
@@ -7,7 +7,7 @@ public final class AdvanceDecree implements RoyalDecree {
public String translationKey() { return "decree.antarchy.advance"; } public String translationKey() { return "decree.antarchy.advance"; }
public void apply(ServerLevel level, KingEntity king, LivingEntity target) { king.setDecreeRetreatPressure(true); } public void apply(ServerLevel level, KingEntity king, LivingEntity target) { king.setDecreeRetreatPressure(true); }
@Override public Evaluation evaluate(ServerLevel level, KingEntity king, LivingEntity target) { @Override public Evaluation evaluate(ServerLevel level, KingEntity king, LivingEntity target) {
return target.distanceTo(king) > REQUIRED_RANGE ? Evaluation.VIOLATED : Evaluation.COMPLIANT; return target.distanceToSqr(king) > REQUIRED_RANGE * REQUIRED_RANGE ? Evaluation.VIOLATED : Evaluation.COMPLIANT;
} }
@Override @Override
public int contextWeight(KingEntity king, LivingEntity target) { public int contextWeight(KingEntity king, LivingEntity target) {
@@ -6,7 +6,7 @@ public final class ComeNoCloserDecree implements RoyalDecree {
public String translationKey() { return "decree.antarchy.come_no_closer"; } public String translationKey() { return "decree.antarchy.come_no_closer"; }
public void apply(ServerLevel level, KingEntity king, LivingEntity target) { } public void apply(ServerLevel level, KingEntity king, LivingEntity target) { }
@Override public Evaluation evaluate(ServerLevel level, KingEntity king, LivingEntity target) { @Override public Evaluation evaluate(ServerLevel level, KingEntity king, LivingEntity target) {
return target.distanceTo(king) <= 16.0D ? Evaluation.VIOLATED : Evaluation.COMPLIANT; return target.distanceToSqr(king) <= 256.0D ? Evaluation.VIOLATED : Evaluation.COMPLIANT;
} }
@Override @Override
public int contextWeight(KingEntity king, LivingEntity target) { public int contextWeight(KingEntity king, LivingEntity target) {
@@ -7,7 +7,7 @@ public final class FightMeCowardDecree implements RoyalDecree {
public String name() { return "FIGHT ME, COWARD!"; } public String name() { return "FIGHT ME, COWARD!"; }
public String translationKey() { return "decree.antarchy.fight_me_coward"; } public String translationKey() { return "decree.antarchy.fight_me_coward"; }
public void apply(ServerLevel level, KingEntity king, LivingEntity target) { public void apply(ServerLevel level, KingEntity king, LivingEntity target) {
if (target.distanceTo(king) > 28.0D) { if (target.distanceToSqr(king) > 784.0D) {
Vec3 pull = king.position().subtract(target.position()).normalize().scale(0.35D); Vec3 pull = king.position().subtract(target.position()).normalize().scale(0.35D);
target.setDeltaMovement(target.getDeltaMovement().add(pull.x, 0.05D, pull.z)); target.setDeltaMovement(target.getDeltaMovement().add(pull.x, 0.05D, pull.z));
target.hasImpulse = true; target.hasImpulse = true;
@@ -15,6 +15,6 @@ public final class FightMeCowardDecree implements RoyalDecree {
} }
@Override @Override
public Evaluation evaluate(ServerLevel level, KingEntity king, LivingEntity target) { public Evaluation evaluate(ServerLevel level, KingEntity king, LivingEntity target) {
return target.distanceTo(king) > 28.0D ? Evaluation.VIOLATED : Evaluation.COMPLIANT; return target.distanceToSqr(king) > 784.0D ? Evaluation.VIOLATED : Evaluation.COMPLIANT;
} }
} }
@@ -7,7 +7,7 @@ public final class KeepYourDistanceDecree implements RoyalDecree {
public String translationKey() { return "decree.antarchy.keep_your_distance"; } public String translationKey() { return "decree.antarchy.keep_your_distance"; }
public void apply(ServerLevel level, KingEntity king, LivingEntity target) { } public void apply(ServerLevel level, KingEntity king, LivingEntity target) { }
@Override public Evaluation evaluate(ServerLevel level, KingEntity king, LivingEntity target) { @Override public Evaluation evaluate(ServerLevel level, KingEntity king, LivingEntity target) {
return target.distanceTo(king) <= INNER_RADIUS ? Evaluation.VIOLATED : Evaluation.COMPLIANT; return target.distanceToSqr(king) <= INNER_RADIUS * INNER_RADIUS ? Evaluation.VIOLATED : Evaluation.COMPLIANT;
} }
@Override @Override
public int contextWeight(KingEntity king, LivingEntity target) { public int contextWeight(KingEntity king, LivingEntity target) {
@@ -6,7 +6,7 @@ public final class NoRetreatDecree implements RoyalDecree {
private static final double ALLOWED_RANGE = 26.0D; private static final double ALLOWED_RANGE = 26.0D;
public String translationKey() { return "decree.antarchy.no_retreat"; } public String translationKey() { return "decree.antarchy.no_retreat"; }
public void apply(ServerLevel level, KingEntity king, LivingEntity target) { public void apply(ServerLevel level, KingEntity king, LivingEntity target) {
if (target.distanceTo(king) > ALLOWED_RANGE) { if (target.distanceToSqr(king) > ALLOWED_RANGE * ALLOWED_RANGE) {
king.invokeJudgment(target); king.invokeJudgment(target);
} }
} }
@@ -2,15 +2,10 @@ package com.craisinlord.antarchy.content.item;
import com.craisinlord.antarchy.content.block.AntimetalScaffoldingBlock; import com.craisinlord.antarchy.content.block.AntimetalScaffoldingBlock;
import com.craisinlord.antarchy.content.gravity.AntarchyGravityApi; import com.craisinlord.antarchy.content.gravity.AntarchyGravityApi;
import java.util.List;
import net.minecraft.ChatFormatting;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction; import net.minecraft.core.Direction;
import net.minecraft.network.chat.Component;
import net.minecraft.world.item.Item; import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.ScaffoldingBlockItem; import net.minecraft.world.item.ScaffoldingBlockItem;
import net.minecraft.world.item.TooltipFlag;
import net.minecraft.world.item.context.BlockPlaceContext; import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.world.level.Level; import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Block;
@@ -22,12 +17,6 @@ public class AntimetalScaffoldingItem extends ScaffoldingBlockItem {
super(block, properties); super(block, properties);
} }
// @Override
// public void appendHoverText(ItemStack stack, Item.TooltipContext context, List<Component> tooltipComponents, TooltipFlag tooltipFlag) {
// tooltipComponents.add(Component.translatable("tooltip.antarchy.antimetal_scaffolding").withStyle(ChatFormatting.DARK_AQUA));
// super.appendHoverText(stack, context, tooltipComponents, tooltipFlag);
// }
@Nullable @Nullable
@Override @Override
public BlockPlaceContext updatePlacementContext(BlockPlaceContext context) { public BlockPlaceContext updatePlacementContext(BlockPlaceContext context) {
@@ -6,9 +6,6 @@ import com.craisinlord.antarchy.content.client.model.ResourceBackedGeoItemModel;
import com.craisinlord.antarchy.content.client.renderer.AnimatedHeldItemRenderer; import com.craisinlord.antarchy.content.client.renderer.AnimatedHeldItemRenderer;
import com.craisinlord.antarchy.content.entity.vortex.WindVortexEntity; import com.craisinlord.antarchy.content.entity.vortex.WindVortexEntity;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer; import java.util.function.Consumer;
import net.minecraft.ChatFormatting; import net.minecraft.ChatFormatting;
import net.minecraft.core.Direction; import net.minecraft.core.Direction;
@@ -46,8 +43,6 @@ public class EyeOfTheStormItem extends Item implements GeoItem {
private static final ResourceLocation ANIMATION_LOCATION = ResourceLocation.fromNamespaceAndPath("antarchy", "animations/eye_of_the_storm.animation.json"); private static final ResourceLocation ANIMATION_LOCATION = ResourceLocation.fromNamespaceAndPath("antarchy", "animations/eye_of_the_storm.animation.json");
private static final String IDLE_CONTROLLER = "idle_controller"; private static final String IDLE_CONTROLLER = "idle_controller";
private static final String IDLE_ANIMATION = "spinning_idle"; private static final String IDLE_ANIMATION = "spinning_idle";
private static final Map<UUID, Long> LAST_UPDRAFT_TICK = new ConcurrentHashMap<>();
private static final Map<UUID, Long> LAST_SURGE_TICK = new ConcurrentHashMap<>();
private static final float SURGE_PROJECTILE_SPEED = 0.65F; private static final float SURGE_PROJECTILE_SPEED = 0.65F;
private final AnimatableInstanceCache geoCache = GeckoLibUtil.createInstanceCache(this); private final AnimatableInstanceCache geoCache = GeckoLibUtil.createInstanceCache(this);
@@ -67,12 +62,9 @@ public class EyeOfTheStormItem extends Item implements GeoItem {
return InteractionResultHolder.consume(stack); return InteractionResultHolder.consume(stack);
} }
long now = level.getGameTime(); if (player.getCooldowns().isOnCooldown(this)) {
Long last = LAST_UPDRAFT_TICK.get(player.getUUID());
if (last != null && now - last < AntarchySettings.eyeOfTheStormUpdraftCooldownTicks()) {
return InteractionResultHolder.fail(stack); return InteractionResultHolder.fail(stack);
} }
LAST_UPDRAFT_TICK.put(player.getUUID(), now);
ServerLevel serverLevel = (ServerLevel) level; ServerLevel serverLevel = (ServerLevel) level;
WindVortexEntity vortex = WindVortexEntity.create( WindVortexEntity vortex = WindVortexEntity.create(
@@ -92,6 +84,7 @@ public class EyeOfTheStormItem extends Item implements GeoItem {
player.setDeltaMovement(player.getDeltaMovement().add(0.0D, AntarchySettings.eyeOfTheStormUpdraftLaunchStrength(), 0.0D)); player.setDeltaMovement(player.getDeltaMovement().add(0.0D, AntarchySettings.eyeOfTheStormUpdraftLaunchStrength(), 0.0D));
player.hasImpulse = true; player.hasImpulse = true;
player.resetFallDistance(); player.resetFallDistance();
player.getCooldowns().addCooldown(this, AntarchySettings.eyeOfTheStormUpdraftCooldownTicks());
if (player instanceof ServerPlayer serverPlayer) { if (player instanceof ServerPlayer serverPlayer) {
serverPlayer.connection.send(new ClientboundSetEntityMotionPacket(serverPlayer)); serverPlayer.connection.send(new ClientboundSetEntityMotionPacket(serverPlayer));
} }
@@ -109,15 +102,13 @@ public class EyeOfTheStormItem extends Item implements GeoItem {
return; return;
} }
long now = level.getGameTime(); if (player.getCooldowns().isOnCooldown(this)) {
Long last = LAST_SURGE_TICK.get(player.getUUID());
if (last != null && now - last < AntarchySettings.eyeOfTheStormSurgeCooldownTicks()) {
return; return;
} }
LAST_SURGE_TICK.put(player.getUUID(), now);
Vec3 look = player.getViewVector(1.0F); Vec3 look = player.getViewVector(1.0F);
launchStormVortex(level, player.getEyePosition().add(look), look, player); launchStormVortex(level, player.getEyePosition().add(look), look, player);
player.getCooldowns().addCooldown(this, AntarchySettings.eyeOfTheStormSurgeCooldownTicks());
player.awardStat(Stats.ITEM_USED.get(this)); player.awardStat(Stats.ITEM_USED.get(this));
stack.hurtAndBreak(1, player, EquipmentSlot.MAINHAND); stack.hurtAndBreak(1, player, EquipmentSlot.MAINHAND);
@@ -1,13 +1,6 @@
package com.craisinlord.antarchy.content.item; package com.craisinlord.antarchy.content.item;
import java.util.List;
import net.minecraft.ChatFormatting;
import net.minecraft.network.chat.Component;
import net.minecraft.world.item.BlockItem; import net.minecraft.world.item.BlockItem;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.TooltipFlag;
import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Block;
public class MucusBlockItem extends BlockItem { public class MucusBlockItem extends BlockItem {
@@ -15,9 +8,4 @@ public class MucusBlockItem extends BlockItem {
super(block, properties); super(block, properties);
} }
@Override
public void appendHoverText(ItemStack stack, Item.TooltipContext context, List<Component> tooltipComponents, TooltipFlag tooltipFlag) {
// tooltipComponents.add(Component.translatable("tooltip.antarchy.mucus_slippery").withStyle(ChatFormatting.DARK_AQUA));
super.appendHoverText(stack, context, tooltipComponents, tooltipFlag);
}
} }
@@ -1,6 +1,5 @@
package com.craisinlord.antarchy.content.item; package com.craisinlord.antarchy.content.item;
import com.craisinlord.antarchy.Antarchy;
import com.craisinlord.antarchy.config.AntarchySettings; import com.craisinlord.antarchy.config.AntarchySettings;
import com.craisinlord.antarchy.content.AntarchyObjects; import com.craisinlord.antarchy.content.AntarchyObjects;
import com.craisinlord.antarchy.content.item.royal.RoyalGearHelper; import com.craisinlord.antarchy.content.item.royal.RoyalGearHelper;
@@ -143,18 +142,8 @@ public class RoyalAssailantArmorItem extends ArmorItem {
} }
MobEffectInstance existing = player.getEffect(AntarchyObjects.INVERTED_EFFECT.get()); MobEffectInstance existing = player.getEffect(AntarchyObjects.INVERTED_EFFECT.get());
if (existing != null && existing.isInfiniteDuration()) { if (existing != null && existing.isInfiniteDuration()) {
Antarchy.LOGGER.info(
"[antarchy-gravity] royal chestplate toggling inversion off player={} uuid={} pos=({}, {}, {}) dim={} existingDuration={} gameTime={}",
player.getGameProfile().getName(), player.getUUID(), player.getX(), player.getY(), player.getZ(),
player.level().dimension().location(), existing.getDuration(), player.level().getGameTime()
);
player.removeEffect(AntarchyObjects.INVERTED_EFFECT.get()); player.removeEffect(AntarchyObjects.INVERTED_EFFECT.get());
} else { } else {
Antarchy.LOGGER.info(
"[antarchy-gravity] royal chestplate toggling inversion on player={} uuid={} pos=({}, {}, {}) dim={} existingDuration={} gameTime={}",
player.getGameProfile().getName(), player.getUUID(), player.getX(), player.getY(), player.getZ(),
player.level().dimension().location(), existing != null ? existing.getDuration() : -1, player.level().getGameTime()
);
player.addEffect(new MobEffectInstance(AntarchyObjects.INVERTED_EFFECT.get(), -1, 0, false, false, true)); player.addEffect(new MobEffectInstance(AntarchyObjects.INVERTED_EFFECT.get(), -1, 0, false, false, true));
} }
} }
@@ -1,7 +1,7 @@
package com.craisinlord.antarchy.content.item; package com.craisinlord.antarchy.content.item;
import com.craisinlord.antarchy.content.effect.RoyalEffectHooks; import com.craisinlord.antarchy.content.effect.RoyalEffectHooks;
import com.craisinlord.antarchy.content.entity.royal.RoyalBlackHoleEntity; import com.craisinlord.antarchy.content.entity.royal.RoyalAssailantBlackHoleEntity;
import com.craisinlord.antarchy.config.AntarchySettings; import com.craisinlord.antarchy.config.AntarchySettings;
import java.util.List; import java.util.List;
import net.minecraft.ChatFormatting; import net.minecraft.ChatFormatting;
@@ -81,7 +81,7 @@ public class RoyalAssailantBattleAxeItem extends AxeItem {
return InteractionResultHolder.sidedSuccess(stack, true); return InteractionResultHolder.sidedSuccess(stack, true);
} }
if (level instanceof ServerLevel serverLevel) { if (level instanceof ServerLevel serverLevel) {
RoyalBlackHoleEntity blackHole = RoyalBlackHoleEntity.createAssailant( RoyalAssailantBlackHoleEntity blackHole = RoyalAssailantBlackHoleEntity.create(
serverLevel, serverLevel,
player.position().add(player.getLookAngle().normalize().scale(2.0D)).add(0.0D, 2.0D, 0.0D), player.position().add(player.getLookAngle().normalize().scale(2.0D)).add(0.0D, 2.0D, 0.0D),
player.getUUID()); player.getUUID());
@@ -13,15 +13,6 @@ import net.minecraft.util.Mth;
import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.Entity;
import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.Vec3;
/**
* Server-authoritative swing physics for the worm hook: pulls the holding player taut against
* a rope anchored to a stuck {@link WormHookProjectileEntity}, wrapping the rope around block
* corners via {@link WormHookRope} so the swing radius shortens realistically when the player
* swings around an obstacle.
*
* A tether entry is created the moment the hook is thrown (so the rope can be rendered following
* the flying projectile) and only gains real swing physics once the hook sticks into a block.
*/
public final class WormHookTetherManager { public final class WormHookTetherManager {
private static final double MIN_LEN = 2.0D; private static final double MIN_LEN = 2.0D;
private static final double REEL_SPEED = 0.12D; private static final double REEL_SPEED = 0.12D;
@@ -51,14 +42,12 @@ public final class WormHookTetherManager {
return hook instanceof WormHookProjectileEntity wormHook ? wormHook : null; return hook instanceof WormHookProjectileEntity wormHook ? wormHook : null;
} }
/** Registers the hook the moment it's thrown, purely so the rope can be rendered following it. */
public static void launch(ServerPlayer player, WormHookProjectileEntity hook) { public static void launch(ServerPlayer player, WormHookProjectileEntity hook) {
clear(player); clear(player);
TETHERS.put(player.getUUID(), new TetherState(hook.getId())); TETHERS.put(player.getUUID(), new TetherState(hook.getId()));
WormHookTetherSync.send(player, hook.getId()); WormHookTetherSync.send(player, hook.getId());
} }
/** Called once the hook sticks into a block; turns the cosmetic tether into real swing physics. */
public static boolean attach(ServerPlayer player, WormHookProjectileEntity hook) { public static boolean attach(ServerPlayer player, WormHookProjectileEntity hook) {
TetherState state = TETHERS.get(player.getUUID()); TetherState state = TETHERS.get(player.getUUID());
if (state == null || state.hookEntityId != hook.getId()) { if (state == null || state.hookEntityId != hook.getId()) {
@@ -108,7 +97,6 @@ public final class WormHookTetherManager {
} }
if (state.rope == null) { if (state.rope == null) {
// Hook is still flying; nothing to simulate yet, just tracking it for the render sync.
return; return;
} }
@@ -135,8 +123,6 @@ public final class WormHookTetherManager {
Vec3 towardPivot = toPlayer.scale(1.0D / dist); Vec3 towardPivot = toPlayer.scale(1.0D / dist);
// Sneaking actively winds the player in toward the anchor, not just shortening
// the max length (which alone has no effect until the rope goes taut).
if (reeling && dist > MIN_LEN) { if (reeling && dist > MIN_LEN) {
Vec3 pull = towardPivot.scale(-REEL_PULL_STRENGTH); Vec3 pull = towardPivot.scale(-REEL_PULL_STRENGTH);
Vec3 reelMotion = player.getDeltaMovement().add(pull); Vec3 reelMotion = player.getDeltaMovement().add(pull);
@@ -63,7 +63,7 @@ public final class PermanentPortalTeleporter {
return active.center(); return active.center();
} }
Vec3 safe = findSafeArrivalPosition(entity, destination, preferredPos); Vec3 safe = findSafeArrivalPosition(entity, destination, preferredPos, type);
if (safe != null) { if (safe != null) {
PermanentPortalShape nearbyActive = findActiveNearby(destination, BlockPos.containing(safe), type); PermanentPortalShape nearbyActive = findActiveNearby(destination, BlockPos.containing(safe), type);
if (nearbyActive != null) { if (nearbyActive != null) {
@@ -77,7 +77,11 @@ public final class PermanentPortalTeleporter {
return createFallbackPortal(destination, BlockPos.containing(safe), type); return createFallbackPortal(destination, BlockPos.containing(safe), type);
} }
return createFallbackPortal(destination, preferredPos, type); BlockPos fallbackPos = preferredPos;
if (type == PermanentPortalType.ELYTHIA && destination.hasChunkAt(preferredPos)) {
fallbackPos = destination.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, preferredPos);
}
return createFallbackPortal(destination, fallbackPos, type);
} }
@Nullable @Nullable
@@ -108,14 +112,16 @@ public final class PermanentPortalTeleporter {
} }
@Nullable @Nullable
private static Vec3 findSafeArrivalPosition(Entity entity, ServerLevel destination, BlockPos preferredPos) { private static Vec3 findSafeArrivalPosition(Entity entity, ServerLevel destination, BlockPos preferredPos, PermanentPortalType type) {
int[] yRange = getDimensionYRange(destination); int[] yRange = getDimensionYRange(destination, type);
if (yRange != null) { if (yRange != null) {
return findSafeArrivalPositionInYRange(entity, destination, preferredPos, yRange[0], yRange[1]); return findSafeArrivalPositionInYRange(entity, destination, preferredPos, yRange[0], yRange[1]);
} }
Set<BlockPos> candidates = new LinkedHashSet<>(); Set<BlockPos> candidates = new LinkedHashSet<>();
if (type != PermanentPortalType.ELYTHIA) {
addCandidate(candidates, preferredPos); addCandidate(candidates, preferredPos);
}
for (int radius = 0; radius <= SEARCH_RADIUS; radius++) { for (int radius = 0; radius <= SEARCH_RADIUS; radius++) {
for (int xOff = -radius; xOff <= radius; xOff++) { for (int xOff = -radius; xOff <= radius; xOff++) {
@@ -139,6 +145,10 @@ public final class PermanentPortalTeleporter {
} }
} }
if (type == PermanentPortalType.ELYTHIA) {
addCandidate(candidates, preferredPos);
}
for (BlockPos candidate : candidates) { for (BlockPos candidate : candidates) {
Vec3 safe = tryFindSafePosition(entity, destination, candidate); Vec3 safe = tryFindSafePosition(entity, destination, candidate);
if (safe != null) { if (safe != null) {
@@ -277,12 +287,12 @@ public final class PermanentPortalTeleporter {
} }
@Nullable @Nullable
private static int[] getDimensionYRange(ServerLevel destination) { private static int[] getDimensionYRange(ServerLevel destination, PermanentPortalType type) {
ResourceKey<Level> dim = destination.dimension(); ResourceKey<Level> dim = destination.dimension();
if (dim == AntarchySettings.termiteDestinationDimension()) { if (dim == AntarchySettings.termiteDestinationDimension()) {
return new int[]{100, 200}; return new int[]{100, 200};
} }
if (dim == AntarchySettings.brownAntDestinationDimension()) { if (type != PermanentPortalType.ELYTHIA && dim == AntarchySettings.brownAntDestinationDimension()) {
return new int[]{75, 120}; return new int[]{75, 120};
} }
return null; return null;
@@ -97,7 +97,6 @@ public final class TimeDilationApi {
SYNCED_CLIENT_RATES.put(entityUuid, clampedRate); SYNCED_CLIENT_RATES.put(entityUuid, clampedRate);
} }
/** Clears both the authoritative entity state and any client-side UUID cache. */
public static void clearRate(Entity entity) { public static void clearRate(Entity entity) {
if (entity instanceof TimeDilationEntityAccess access) { if (entity instanceof TimeDilationEntityAccess access) {
boolean wasDilated = Math.abs(access.antarchy$getTimeDilationRate() - TimeDilationMath.NORMAL_RATE) > 0.001D; boolean wasDilated = Math.abs(access.antarchy$getTimeDilationRate() - TimeDilationMath.NORMAL_RATE) > 0.001D;
@@ -1,6 +1,10 @@
package com.craisinlord.antarchy.content.time; package com.craisinlord.antarchy.content.time;
import com.craisinlord.antarchy.content.AntarchyObjects; import com.craisinlord.antarchy.content.AntarchyObjects;
import com.craisinlord.antarchy.content.AntarchyTags;
import com.craisinlord.antarchy.content.entity.royal.QueenEntity;
import com.craisinlord.antarchy.content.entity.royal.RoyalBlackHoleEntity;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.syncher.EntityDataAccessor; import net.minecraft.network.syncher.EntityDataAccessor;
import net.minecraft.network.syncher.EntityDataSerializers; import net.minecraft.network.syncher.EntityDataSerializers;
@@ -25,6 +29,8 @@ public class TimeDilationFieldEntity extends Entity {
private int durationTicks = -1; private int durationTicks = -1;
private int age; private int age;
private UUID ownerId; private UUID ownerId;
private UUID anchorId;
private int missingAnchorTicks;
private boolean visual = true; private boolean visual = true;
public TimeDilationFieldEntity(EntityType<? extends TimeDilationFieldEntity> entityType, Level level) { public TimeDilationFieldEntity(EntityType<? extends TimeDilationFieldEntity> entityType, Level level) {
@@ -56,6 +62,23 @@ public class TimeDilationFieldEntity extends Entity {
return this.ownerId != null && entity != null && this.ownerId.equals(entity.getUUID()); return this.ownerId != null && entity != null && this.ownerId.equals(entity.getUUID());
} }
public void attachTo(Entity anchor) {
this.anchorId = anchor == null ? null : anchor.getUUID();
this.missingAnchorTicks = 0;
}
public boolean affects(Entity entity) {
if (entity instanceof RoyalBlackHoleEntity || this.isOwnedBy(entity)
|| this.anchorId != null && this.anchorId.equals(entity.getUUID())) {
return false;
}
if (this.ownerId != null && this.level() instanceof ServerLevel level
&& level.getEntity(this.ownerId) instanceof QueenEntity) {
return !entity.getType().is(AntarchyTags.Entities.QUEEN_DOES_NOT_ATTACK);
}
return true;
}
public boolean isVisual() { public boolean isVisual() {
return this.visual; return this.visual;
} }
@@ -72,6 +95,19 @@ public class TimeDilationFieldEntity extends Entity {
if (this.level().isClientSide) { if (this.level().isClientSide) {
return; return;
} }
if (this.anchorId != null && this.level() instanceof ServerLevel level) {
Entity anchor = level.getEntity(this.anchorId);
if (anchor != null && anchor.isAlive()) {
Vec3 center = anchor instanceof RoyalBlackHoleEntity blackHole
? blackHole.effectCenter()
: anchor.position();
this.setPos(center.x, center.y, center.z);
this.missingAnchorTicks = 0;
} else if (++this.missingAnchorTicks > 20) {
this.discard();
return;
}
}
this.age++; this.age++;
if (this.durationTicks >= 0 && this.age >= this.durationTicks) { if (this.durationTicks >= 0 && this.age >= this.durationTicks) {
this.discard(); this.discard();
@@ -127,6 +163,7 @@ public class TimeDilationFieldEntity extends Entity {
this.durationTicks = tag.contains(DURATION_KEY) ? tag.getInt(DURATION_KEY) : -1; this.durationTicks = tag.contains(DURATION_KEY) ? tag.getInt(DURATION_KEY) : -1;
this.age = tag.getInt(AGE_KEY); this.age = tag.getInt(AGE_KEY);
this.ownerId = tag.hasUUID("OwnerUuid") ? tag.getUUID("OwnerUuid") : null; this.ownerId = tag.hasUUID("OwnerUuid") ? tag.getUUID("OwnerUuid") : null;
this.anchorId = tag.hasUUID("AnchorUuid") ? tag.getUUID("AnchorUuid") : null;
this.visual = !tag.contains("Visual") || tag.getBoolean("Visual"); this.visual = !tag.contains("Visual") || tag.getBoolean("Visual");
} }
@@ -139,6 +176,9 @@ public class TimeDilationFieldEntity extends Entity {
if (this.ownerId != null) { if (this.ownerId != null) {
tag.putUUID("OwnerUuid", this.ownerId); tag.putUUID("OwnerUuid", this.ownerId);
} }
if (this.anchorId != null) {
tag.putUUID("AnchorUuid", this.anchorId);
}
tag.putBoolean("Visual", this.visual); tag.putBoolean("Visual", this.visual);
} }
} }
@@ -32,7 +32,7 @@ public final class TimeDilationFieldSampler {
public static double sample(List<TimeDilationFieldEntity> fields, Entity entity) { public static double sample(List<TimeDilationFieldEntity> fields, Entity entity) {
double combinedRate = TimeDilationMath.NORMAL_RATE; double combinedRate = TimeDilationMath.NORMAL_RATE;
for (TimeDilationFieldEntity field : fields) { for (TimeDilationFieldEntity field : fields) {
if (field.isOwnedBy(entity)) { if (!field.affects(entity)) {
continue; continue;
} }
Vec3 center = field.position(); Vec3 center = field.position();
@@ -1,7 +1,12 @@
package com.craisinlord.antarchy.content.time; package com.craisinlord.antarchy.content.time;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.IdentityHashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
import net.minecraft.server.MinecraftServer; import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.level.ServerPlayer;
@@ -9,33 +14,57 @@ import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.projectile.Projectile; import net.minecraft.world.entity.projectile.Projectile;
import com.craisinlord.antarchy.content.effect.DilatedMobEffect; import com.craisinlord.antarchy.content.effect.DilatedMobEffect;
import com.craisinlord.antarchy.content.effect.ContractedMobEffect; import com.craisinlord.antarchy.content.effect.ContractedMobEffect;
import com.craisinlord.antarchy.content.effect.ContractedMobEffect;
import com.craisinlord.antarchy.content.effect.ContractedMobEffect;
import com.craisinlord.antarchy.content.effect.RoyalEffectEligibility; import com.craisinlord.antarchy.content.effect.RoyalEffectEligibility;
import com.craisinlord.antarchy.content.effect.RoyalEffectHooks; import com.craisinlord.antarchy.content.effect.RoyalEffectHooks;
public final class TimeDilationManager { public final class TimeDilationManager {
private static final Map<ServerLevel, TrackingState> TRACKING = new WeakHashMap<>();
private TimeDilationManager() { private TimeDilationManager() {
} }
public static void tickServer(MinecraftServer server) { public static void tickServer(MinecraftServer server) {
for (ServerLevel level : server.getAllLevels()) { for (ServerLevel level : server.getAllLevels()) {
List<TimeDilationFieldEntity> fields = collectFields(level); List<TimeDilationFieldEntity> fields = collectFields(level);
syncFieldSnapshots(level, fields); TrackingState tracking = TRACKING.computeIfAbsent(level, ignored -> new TrackingState());
updateEntities(level, fields); discoverFieldEntities(level, fields, tracking);
syncFieldSnapshots(level, fields, tracking);
updateEntities(level, fields, tracking);
if (fields.isEmpty() && tracking.entities.isEmpty()) {
TRACKING.remove(level);
}
if (!fields.isEmpty()) { if (!fields.isEmpty()) {
TimeDilationParticles.spawnFieldBorders(level, fields.stream().filter(TimeDilationFieldEntity::isVisual).toList()); TimeDilationParticles.spawnFieldBorders(level, fields.stream().filter(TimeDilationFieldEntity::isVisual).toList());
} }
} }
} }
private static void syncFieldSnapshots(ServerLevel level, List<TimeDilationFieldEntity> fields) { public static void trackPotentiallyAffected(Entity entity) {
if (!(entity.level() instanceof ServerLevel level) || entity instanceof TimeDilationFieldEntity) {
return;
}
if (entity instanceof TimeDilationEntityAccess access
&& Math.abs(access.antarchy$getTimeDilationRate() - TimeDilationMath.NORMAL_RATE) < 0.001D
&& Math.abs(access.antarchy$getInheritedTimeDilationRate() - TimeDilationMath.NORMAL_RATE) < 0.001D
&& (!(entity instanceof net.minecraft.world.entity.LivingEntity living)
|| ((RoyalEffectHooks.dilatedHolder() == null || !living.hasEffect(RoyalEffectHooks.dilatedHolder()))
&& (RoyalEffectHooks.contractedHolder() == null || !living.hasEffect(RoyalEffectHooks.contractedHolder()))))) {
return;
}
TRACKING.computeIfAbsent(level, ignored -> new TrackingState()).entities.add(entity);
}
private static void syncFieldSnapshots(ServerLevel level, List<TimeDilationFieldEntity> fields, TrackingState tracking) {
List<TimeDilationFieldSnapshot> snapshots = fields.stream() List<TimeDilationFieldSnapshot> snapshots = fields.stream()
.map(field -> new TimeDilationFieldSnapshot( .map(field -> new TimeDilationFieldSnapshot(
field.getX(), field.getY(), field.getZ(), field.fieldRadius(), field.fieldRate(), field.getX(), field.getY(), field.getZ(), field.fieldRadius(), field.fieldRate(),
field.fieldAge(), field.fieldDurationTicks())) field.fieldAge(), field.fieldDurationTicks()))
.limit(128) .limit(128)
.toList(); .toList();
if (level.getGameTime() % 4L != 0L && snapshots.size() == tracking.lastSnapshots.size()) {
return;
}
tracking.lastSnapshots = snapshots;
for (ServerPlayer player : level.players()) { for (ServerPlayer player : level.players()) {
TimeDilationApi.syncFields(player, snapshots); TimeDilationApi.syncFields(player, snapshots);
} }
@@ -51,8 +80,23 @@ public final class TimeDilationManager {
return fields; return fields;
} }
private static void updateEntities(ServerLevel level, List<TimeDilationFieldEntity> fields) { private static void discoverFieldEntities(ServerLevel level, List<TimeDilationFieldEntity> fields, TrackingState tracking) {
for (Entity entity : level.getAllEntities()) { for (TimeDilationFieldEntity field : fields) {
double radius = field.fieldRadius();
var center = field.position();
var area = new net.minecraft.world.phys.AABB(
center.x - radius, center.y - radius, center.z - radius,
center.x + radius, center.y + radius, center.z + radius);
tracking.entities.addAll(level.getEntities(field, area,
entity -> entity.isAlive() && !(entity instanceof TimeDilationFieldEntity)
&& entity instanceof TimeDilationEntityAccess));
}
}
private static void updateEntities(ServerLevel level, List<TimeDilationFieldEntity> fields, TrackingState tracking) {
var iterator = tracking.entities.iterator();
while (iterator.hasNext()) {
Entity entity = iterator.next();
if (!(entity instanceof TimeDilationEntityAccess access) || entity instanceof TimeDilationFieldEntity) { if (!(entity instanceof TimeDilationEntityAccess access) || entity instanceof TimeDilationFieldEntity) {
continue; continue;
} }
@@ -62,6 +106,7 @@ public final class TimeDilationManager {
living.removeEffect(RoyalEffectHooks.dilatedHolder()); living.removeEffect(RoyalEffectHooks.dilatedHolder());
} }
TimeDilationApi.clearRate(entity); TimeDilationApi.clearRate(entity);
iterator.remove();
continue; continue;
} }
double previousRate = access.antarchy$getTimeDilationRate(); double previousRate = access.antarchy$getTimeDilationRate();
@@ -116,7 +161,23 @@ public final class TimeDilationManager {
if (Math.abs(previousRate - rate) > 0.001D) { if (Math.abs(previousRate - rate) > 0.001D) {
TimeDilationApi.syncEntityRate(entity, rate); TimeDilationApi.syncEntityRate(entity, rate);
} }
boolean hasDilationEffect = entity instanceof net.minecraft.world.entity.LivingEntity living
&& ((RoyalEffectHooks.dilatedHolder() != null && living.hasEffect(RoyalEffectHooks.dilatedHolder()))
|| (RoyalEffectHooks.contractedHolder() != null && living.hasEffect(RoyalEffectHooks.contractedHolder())));
boolean hasInheritedRate = Math.abs(access.antarchy$getInheritedTimeDilationRate() - TimeDilationMath.NORMAL_RATE) >= 0.001D;
boolean hasActiveRate = Math.abs(access.antarchy$getTimeDilationRate() - TimeDilationMath.NORMAL_RATE) >= 0.001D;
if (!hasDilationEffect && !hasInheritedRate && !hasActiveRate
&& Math.abs(targetRate - TimeDilationMath.NORMAL_RATE) < 0.001D) {
iterator.remove();
} }
} }
tracking.entities.removeIf(entity -> !entity.isAlive() || entity.isRemoved());
}
private static final class TrackingState {
private final Set<Entity> entities = Collections.newSetFromMap(new IdentityHashMap<>());
private List<TimeDilationFieldSnapshot> lastSnapshots = List.of();
}
} }
@@ -18,7 +18,6 @@ public final class TimeDilationMath {
return Math.max(MIN_RATE, Math.min(MAX_RATE, rate)); return Math.max(MIN_RATE, Math.min(MAX_RATE, rate));
} }
/** Moves an entity's effective rate toward a new effect/field target without a hard step. */
public static double transitionRate(double currentRate, double targetRate) { public static double transitionRate(double currentRate, double targetRate) {
double current = clampRate(currentRate); double current = clampRate(currentRate);
double target = clampRate(targetRate); double target = clampRate(targetRate);
@@ -9,8 +9,10 @@ import com.mojang.serialization.Codec;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.ArrayDeque; import java.util.ArrayDeque;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction; import net.minecraft.core.Direction;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
@@ -33,6 +35,13 @@ import net.minecraft.world.level.block.state.properties.IntegerProperty;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
public class OuranwoodTreeFeature extends Feature<OuranwoodTreeConfiguration> { public class OuranwoodTreeFeature extends Feature<OuranwoodTreeConfiguration> {
private static final ThreadLocal<TreePlacement> ACTIVE_PLACEMENT = new ThreadLocal<>();
private static final class TreePlacement {
private final Set<BlockPos> logPositions = new LinkedHashSet<>();
private final Set<BlockPos> leafPositions = new LinkedHashSet<>();
}
public OuranwoodTreeFeature(Codec<OuranwoodTreeConfiguration> codec) { public OuranwoodTreeFeature(Codec<OuranwoodTreeConfiguration> codec) {
super(codec); super(codec);
} }
@@ -44,6 +53,9 @@ public class OuranwoodTreeFeature extends Feature<OuranwoodTreeConfiguration> {
OuranwoodTreeConfiguration config = context.config(); OuranwoodTreeConfiguration config = context.config();
RandomSource random = context.random(); RandomSource random = context.random();
TreePlacement placement = new TreePlacement();
ACTIVE_PLACEMENT.set(placement);
try {
if (!canGrowOn(level, origin.below())) { if (!canGrowOn(level, origin.below())) {
return false; return false;
} }
@@ -87,6 +99,9 @@ public class OuranwoodTreeFeature extends Feature<OuranwoodTreeConfiguration> {
this.placeFlyingSquirrelNest(level, origin, config, random, trunkRadius, height); this.placeFlyingSquirrelNest(level, origin, config, random, trunkRadius, height);
this.placeCaterpillarChrysalis(level, origin, config, random, canopyCenter, canopyRadius, canopyDepth); this.placeCaterpillarChrysalis(level, origin, config, random, canopyCenter, canopyRadius, canopyDepth);
return true; return true;
} finally {
ACTIVE_PLACEMENT.remove();
}
} }
protected BlockPos resolveCanopyCenter(BlockPos origin, int height, int canopyDepth) { protected BlockPos resolveCanopyCenter(BlockPos origin, int height, int canopyDepth) {
@@ -1044,6 +1059,28 @@ public class OuranwoodTreeFeature extends Feature<OuranwoodTreeConfiguration> {
} }
protected void pruneExposedTopLogs(WorldGenLevel level, BlockPos origin, BlockPos canopyCenter, OuranwoodTreeConfiguration config, RandomSource random, int trunkRadius, int canopyRadius, int canopyDepth, int height) { protected void pruneExposedTopLogs(WorldGenLevel level, BlockPos origin, BlockPos canopyCenter, OuranwoodTreeConfiguration config, RandomSource random, int trunkRadius, int canopyRadius, int canopyDepth, int height) {
TreePlacement placement = ACTIVE_PLACEMENT.get();
if (placement != null) {
for (BlockPos pos : placement.logPositions) {
if (this.isWithinTrunkCore(origin, pos, trunkRadius, canopyCenter.getY())) {
continue;
}
BlockState state = level.getBlockState(pos);
if (!state.is(BlockTags.LOGS)) {
continue;
}
int leafNeighbors = this.countLeafNeighbors(level, pos);
int openFaces = this.countOpenFaces(level, pos);
boolean topExposed = level.getBlockState(pos.above()).isAir();
boolean lowerCanopyStray = pos.getY() <= canopyCenter.getY()
&& leafNeighbors >= 3
&& openFaces >= 2;
if ((topExposed && leafNeighbors >= 1) || lowerCanopyStray) {
this.setLeaf(level, pos, config, random);
}
}
return;
}
int horizontalRadius = canopyRadius + 6; int horizontalRadius = canopyRadius + 6;
int minY = canopyCenter.getY() - Math.max(4, canopyDepth / 2 + 1); int minY = canopyCenter.getY() - Math.max(4, canopyDepth / 2 + 1);
int maxY = origin.getY() + height + canopyDepth + 6; int maxY = origin.getY() + height + canopyDepth + 6;
@@ -1077,6 +1114,23 @@ public class OuranwoodTreeFeature extends Feature<OuranwoodTreeConfiguration> {
} }
protected void coverExposedBranchSides(WorldGenLevel level, BlockPos origin, BlockPos canopyCenter, OuranwoodTreeConfiguration config, RandomSource random, int trunkRadius, int canopyRadius, int canopyDepth, int height) { protected void coverExposedBranchSides(WorldGenLevel level, BlockPos origin, BlockPos canopyCenter, OuranwoodTreeConfiguration config, RandomSource random, int trunkRadius, int canopyRadius, int canopyDepth, int height) {
TreePlacement placement = ACTIVE_PLACEMENT.get();
if (placement != null) {
for (BlockPos pos : placement.logPositions) {
if (!level.getBlockState(pos).is(BlockTags.LOGS)
|| this.isWithinTrunkCore(origin, pos, trunkRadius, canopyCenter.getY())
|| this.countLeafNeighbors(level, pos) < 1) {
continue;
}
for (Direction direction : Direction.Plane.HORIZONTAL) {
BlockPos sidePos = pos.relative(direction);
if (level.getBlockState(sidePos).isAir() && this.canWrapBranchSide(level, pos, direction)) {
this.tryPlaceLeaf(level, sidePos, config, random);
}
}
}
return;
}
int horizontalRadius = canopyRadius + 6; int horizontalRadius = canopyRadius + 6;
int minY = Math.max(origin.getY() + Math.max(4, height / 4), canopyCenter.getY() - canopyDepth - 3); int minY = Math.max(origin.getY() + Math.max(4, height / 4), canopyCenter.getY() - canopyDepth - 3);
int maxY = origin.getY() + height + canopyDepth + 4; int maxY = origin.getY() + height + canopyDepth + 4;
@@ -1112,6 +1166,11 @@ public class OuranwoodTreeFeature extends Feature<OuranwoodTreeConfiguration> {
} }
protected void updateLeafDistances(WorldGenLevel level, BlockPos origin, int height, int canopyRadius, int canopyDepth) { protected void updateLeafDistances(WorldGenLevel level, BlockPos origin, int height, int canopyRadius, int canopyDepth) {
TreePlacement placement = ACTIVE_PLACEMENT.get();
if (placement != null) {
this.updateLeafDistances(level, placement.leafPositions);
return;
}
int horizontalRadius = canopyRadius + 2; int horizontalRadius = canopyRadius + 2;
int minY = Math.max(level.getMinBuildHeight(), origin.getY() - 2); int minY = Math.max(level.getMinBuildHeight(), origin.getY() - 2);
int maxY = Math.min(level.getMaxBuildHeight() - 1, origin.getY() + height + canopyDepth + 8); int maxY = Math.min(level.getMaxBuildHeight() - 1, origin.getY() + height + canopyDepth + 8);
@@ -1200,6 +1259,78 @@ public class OuranwoodTreeFeature extends Feature<OuranwoodTreeConfiguration> {
} }
} }
private void updateLeafDistances(WorldGenLevel level, Set<BlockPos> leafPositions) {
Map<BlockPos, Integer> distances = new HashMap<>();
ArrayDeque<BlockPos> queue = new ArrayDeque<>();
for (BlockPos pos : leafPositions) {
BlockState state = level.getBlockState(pos);
IntegerProperty property = this.getLeafDistanceProperty(state);
if (property != null) {
distances.put(pos, this.getLeafDistanceLimit(state));
}
}
for (BlockPos pos : leafPositions) {
BlockState state = level.getBlockState(pos);
IntegerProperty property = this.getLeafDistanceProperty(state);
if (property == null) {
continue;
}
int bestDistance = this.getLeafDistanceLimit(state);
for (Direction direction : Direction.values()) {
BlockPos neighborPos = pos.relative(direction);
BlockState neighbor = level.getBlockState(neighborPos);
if (neighbor.is(BlockTags.LOGS)) {
bestDistance = 1;
break;
}
IntegerProperty neighborProperty = this.getLeafDistanceProperty(neighbor);
if (neighborProperty != null && !distances.containsKey(neighborPos)) {
bestDistance = Math.min(bestDistance, neighbor.getValue(neighborProperty) + 1);
}
}
if (bestDistance < distances.get(pos)) {
distances.put(pos, bestDistance);
queue.add(pos);
}
}
while (!queue.isEmpty()) {
BlockPos pos = queue.removeFirst();
int distance = distances.get(pos);
for (Direction direction : Direction.values()) {
BlockPos neighborPos = pos.relative(direction);
Integer neighborDistance = distances.get(neighborPos);
if (neighborDistance == null) {
continue;
}
BlockState neighborState = level.getBlockState(neighborPos);
int candidate = Math.min(this.getLeafDistanceLimit(neighborState), distance + 1);
if (candidate < neighborDistance) {
distances.put(neighborPos, candidate);
queue.addLast(neighborPos);
}
}
}
for (BlockPos pos : leafPositions) {
BlockState state = level.getBlockState(pos);
IntegerProperty property = this.getLeafDistanceProperty(state);
if (property == null) {
continue;
}
int computedDistance = distances.get(pos);
if (computedDistance == state.getValue(property)) {
continue;
}
BlockState updatedState = state.getBlock() instanceof OuranwoodLeavesBlock
? OuranwoodLeavesBlock.setOuranwoodDistanceForWorldgen(state, computedDistance)
: state.setValue(property, computedDistance);
setBlock(level, pos, updatedState);
}
}
protected void placeHangingAcorns(WorldGenLevel level, BlockPos canopyCenter, OuranwoodTreeConfiguration config, RandomSource random, int canopyRadius, int canopyDepth) { protected void placeHangingAcorns(WorldGenLevel level, BlockPos canopyCenter, OuranwoodTreeConfiguration config, RandomSource random, int canopyRadius, int canopyDepth) {
int maxAcorns = config.juvenile() ? 12 + random.nextInt(6) : 40 + random.nextInt(20); int maxAcorns = config.juvenile() ? 12 + random.nextInt(6) : 40 + random.nextInt(20);
int attempts = maxAcorns * 12; int attempts = maxAcorns * 12;
@@ -1530,6 +1661,15 @@ public class OuranwoodTreeFeature extends Feature<OuranwoodTreeConfiguration> {
protected void setBlock(WorldGenLevel level, BlockPos pos, BlockState state) { protected void setBlock(WorldGenLevel level, BlockPos pos, BlockState state) {
if (canWriteAt(level, pos)) { if (canWriteAt(level, pos)) {
setBlock((LevelWriter) level, pos, state); setBlock((LevelWriter) level, pos, state);
TreePlacement placement = ACTIVE_PLACEMENT.get();
if (placement != null) {
BlockPos immutablePos = pos.immutable();
if (state.is(BlockTags.LOGS)) {
placement.logPositions.add(immutablePos);
} else if (state.is(BlockTags.LEAVES)) {
placement.leafPositions.add(immutablePos);
}
}
} }
} }
@@ -2,6 +2,7 @@ package com.craisinlord.antarchy.content.worldgen.thoraxis;
import com.craisinlord.antarchy.content.AntarchyObjects; import com.craisinlord.antarchy.content.AntarchyObjects;
import com.mojang.serialization.Codec; import com.mojang.serialization.Codec;
import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.util.Mth; import net.minecraft.util.Mth;
import net.minecraft.util.RandomSource; import net.minecraft.util.RandomSource;
@@ -34,6 +35,7 @@ public final class LucidAntiwaterPoolFeature extends Feature<ThoraxisAntiwaterPo
int sourceCount = Math.max(2, randomBetween(random, 2, 4)); int sourceCount = Math.max(2, randomBetween(random, 2, 4));
BlockState antiwaterSourceState = config.state().getState(random, origin); BlockState antiwaterSourceState = config.state().getState(random, origin);
BlockState basaltState = AntarchyObjects.ANTIMETAL.get().defaultBlockState(); BlockState basaltState = AntarchyObjects.ANTIMETAL.get().defaultBlockState();
Long2IntOpenHashMap ceilingCache = new Long2IntOpenHashMap();
int floorY = findFloorY(level, origin.getX(), origin.getZ()); int floorY = findFloorY(level, origin.getX(), origin.getZ());
if (floorY <= level.getMinBuildHeight()) { if (floorY <= level.getMinBuildHeight()) {
@@ -55,7 +57,7 @@ public final class LucidAntiwaterPoolFeature extends Feature<ThoraxisAntiwaterPo
int ventBudget = sourceCount + random.nextInt(2); int ventBudget = sourceCount + random.nextInt(2);
BasinProfile profile = BasinProfile.create(random); BasinProfile profile = BasinProfile.create(random);
placedAny |= placeCeilingBasin(level, random, offsetX, floorY, offsetZ, lobeRadius, ceilingY, profile, basaltState, antiwaterSourceState); placedAny |= placeCeilingBasin(level, random, offsetX, floorY, offsetZ, lobeRadius, ceilingY, profile, basaltState, antiwaterSourceState, ceilingCache);
placedAny |= placeAntiwaterSources(level, random, offsetX, floorY, offsetZ, Math.max(2, lobeRadius - 1), ventBudget, antiwaterSourceState); placedAny |= placeAntiwaterSources(level, random, offsetX, floorY, offsetZ, Math.max(2, lobeRadius - 1), ventBudget, antiwaterSourceState);
} }
@@ -72,7 +74,8 @@ public final class LucidAntiwaterPoolFeature extends Feature<ThoraxisAntiwaterPo
int maxCeilingY, int maxCeilingY,
BasinProfile profile, BasinProfile profile,
BlockState basaltState, BlockState basaltState,
BlockState antiwaterSourceState BlockState antiwaterSourceState,
Long2IntOpenHashMap ceilingCache
) { ) {
boolean placedAny = false; boolean placedAny = false;
BlockPos.MutableBlockPos mutable = new BlockPos.MutableBlockPos(); BlockPos.MutableBlockPos mutable = new BlockPos.MutableBlockPos();
@@ -87,7 +90,7 @@ public final class LucidAntiwaterPoolFeature extends Feature<ThoraxisAntiwaterPo
int worldX = centerX + dx; int worldX = centerX + dx;
int worldZ = centerZ + dz; int worldZ = centerZ + dz;
int localCeilingY = findCeilingY(level, worldX, worldZ, floorY); int localCeilingY = cachedCeilingY(level, worldX, worldZ, floorY, ceilingCache);
if (localCeilingY <= floorY + MIN_CAVITY_HEIGHT || localCeilingY > maxCeilingY + 3) { if (localCeilingY <= floorY + MIN_CAVITY_HEIGHT || localCeilingY > maxCeilingY + 3) {
continue; continue;
} }
@@ -113,7 +116,7 @@ public final class LucidAntiwaterPoolFeature extends Feature<ThoraxisAntiwaterPo
placedAny = true; placedAny = true;
} }
placedAny |= placeOuterLip(level, random, worldX, worldZ, floorY, basinTopY, profile, dx, dz, radius, basaltState); placedAny |= placeOuterLip(level, random, worldX, worldZ, floorY, basinTopY, profile, dx, dz, radius, basaltState, ceilingCache);
continue; continue;
} }
@@ -145,7 +148,8 @@ public final class LucidAntiwaterPoolFeature extends Feature<ThoraxisAntiwaterPo
int dx, int dx,
int dz, int dz,
int radius, int radius,
BlockState basaltState BlockState basaltState,
Long2IntOpenHashMap ceilingCache
) { ) {
boolean placedAny = false; boolean placedAny = false;
BlockPos.MutableBlockPos mutable = new BlockPos.MutableBlockPos(); BlockPos.MutableBlockPos mutable = new BlockPos.MutableBlockPos();
@@ -166,7 +170,7 @@ public final class LucidAntiwaterPoolFeature extends Feature<ThoraxisAntiwaterPo
int x = centerX + stepX; int x = centerX + stepX;
int z = centerZ + stepZ; int z = centerZ + stepZ;
int localCeilingY = findCeilingY(level, x, z, floorY); int localCeilingY = cachedCeilingY(level, x, z, floorY, ceilingCache);
if (localCeilingY <= floorY + MIN_CAVITY_HEIGHT) { if (localCeilingY <= floorY + MIN_CAVITY_HEIGHT) {
continue; continue;
} }
@@ -300,6 +304,23 @@ public final class LucidAntiwaterPoolFeature extends Feature<ThoraxisAntiwaterPo
return Integer.MIN_VALUE; return Integer.MIN_VALUE;
} }
private static int cachedCeilingY(
WorldGenLevel level,
int x,
int z,
int floorY,
Long2IntOpenHashMap cache
) {
long key = BlockPos.asLong(x, 0, z);
if (cache.containsKey(key)) {
return cache.get(key);
}
int ceilingY = findCeilingY(level, x, z, floorY);
cache.put(key, ceilingY);
return ceilingY;
}
private static boolean isSolidSurface(BlockState state) { private static boolean isSolidSurface(BlockState state) {
return !state.isAir() && state.blocksMotion() && state.getFluidState().isEmpty(); return !state.isAir() && state.blocksMotion() && state.getFluidState().isEmpty();
} }
@@ -16,7 +16,6 @@ import net.minecraft.world.level.levelgen.feature.Feature;
import net.minecraft.world.level.levelgen.feature.FeaturePlaceContext; import net.minecraft.world.level.levelgen.feature.FeaturePlaceContext;
import net.minecraft.world.level.levelgen.feature.configurations.NoneFeatureConfiguration; import net.minecraft.world.level.levelgen.feature.configurations.NoneFeatureConfiguration;
/** Places the complete natural-grown shape so every segment gets the body state. */
public final class NaturalDirectionalVineFeature extends Feature<NoneFeatureConfiguration> { public final class NaturalDirectionalVineFeature extends Feature<NoneFeatureConfiguration> {
private final ResourceLocation blockId; private final ResourceLocation blockId;
private final Direction direction; private final Direction direction;
@@ -11,10 +11,6 @@ import net.minecraft.world.level.levelgen.feature.Feature;
import net.minecraft.world.level.levelgen.feature.FeaturePlaceContext; import net.minecraft.world.level.levelgen.feature.FeaturePlaceContext;
import net.minecraft.world.level.levelgen.feature.configurations.NoneFeatureConfiguration; import net.minecraft.world.level.levelgen.feature.configurations.NoneFeatureConfiguration;
/**
* Fills the low, open main-side Thoraxis basin with source ichor during chunk generation.
* The feature works one chunk at a time so it does not depend on runtime fluid spreading.
*/
public final class ThoraxisIchorLakeFeature extends Feature<NoneFeatureConfiguration> { public final class ThoraxisIchorLakeFeature extends Feature<NoneFeatureConfiguration> {
private static final int LAKE_FLOOR_Y = 1; private static final int LAKE_FLOOR_Y = 1;
private static final int LAKE_SURFACE_Y = 8; private static final int LAKE_SURFACE_Y = 8;
@@ -71,6 +71,7 @@ public final class ThoraxisUndersideManager {
int refreshedLiving = 0; int refreshedLiving = 0;
int flippedItems = 0; int flippedItems = 0;
int restoredItems = 0; int restoredItems = 0;
Set<Entity> evicted = Collections.newSetFromMap(new IdentityHashMap<>());
for (Entity entity : tracking.entities) { for (Entity entity : tracking.entities) {
if (!entity.isAlive() || entity.isSpectator()) { if (!entity.isAlive() || entity.isSpectator()) {
continue; continue;
@@ -82,6 +83,8 @@ public final class ThoraxisUndersideManager {
if (living.getY() < threshold) { if (living.getY() < threshold) {
refreshInvertedEffect(living); refreshInvertedEffect(living);
refreshedLiving++; refreshedLiving++;
} else if (!hasEffect) {
evicted.add(entity);
} }
continue; continue;
} }
@@ -122,9 +125,12 @@ public final class ThoraxisUndersideManager {
AntarchyGravityApi.setGravityDirection(entity, AntarchyGravityDirection.DOWN, false, TRANSITION); AntarchyGravityApi.setGravityDirection(entity, AntarchyGravityDirection.DOWN, false, TRANSITION);
restoredItems++; restoredItems++;
} }
evicted.add(entity);
} }
} }
tracking.entities.removeAll(evicted);
tracking.forcedItems.removeAll(evicted);
tracking.entities.removeIf(entity -> !entity.isAlive() || entity.isRemoved()); tracking.entities.removeIf(entity -> !entity.isAlive() || entity.isRemoved());
tracking.forcedItems.removeIf(entity -> !entity.isAlive() || entity.isRemoved()); tracking.forcedItems.removeIf(entity -> !entity.isAlive() || entity.isRemoved());
tracking.lastFlipTick.entrySet().removeIf(entry -> now - entry.getValue() > FLIP_COOLDOWN_TICKS * 4L); tracking.lastFlipTick.entrySet().removeIf(entry -> now - entry.getValue() > FLIP_COOLDOWN_TICKS * 4L);
@@ -222,4 +228,11 @@ public final class ThoraxisUndersideManager {
public static boolean isAboveUndersideExit(Entity entity) { public static boolean isAboveUndersideExit(Entity entity) {
return isThoraxis(entity.level()) && entity.getY() >= EXIT_UNDERSIDE_Y; return isThoraxis(entity.level()) && entity.getY() >= EXIT_UNDERSIDE_Y;
} }
/** True while an entity is in the region where the underside gravity rule applies. */
public static boolean shouldInvertInUnderside(Entity entity) {
return AntarchyGravityApi.isGravityInverted(entity)
&& isThoraxis(entity.level())
&& entity.getY() < GRAVITY_FLIP_Y;
}
} }
@@ -1,16 +1,15 @@
package com.craisinlord.antarchy.mixins.gravity; package com.craisinlord.antarchy.mixins.gravity;
import com.craisinlord.antarchy.content.gravity.AntarchyGravityApi; import com.craisinlord.antarchy.content.gravity.AntarchyGravityApi;
import com.craisinlord.antarchy.content.gravity.AntarchyGravityDirection;
import com.craisinlord.antarchy.content.gravity.AntarchyGravityRotationUtil;
import com.craisinlord.antarchy.config.AntarchySettings; import com.craisinlord.antarchy.config.AntarchySettings;
import com.craisinlord.antarchy.content.worldgen.thoraxis.ThoraxisUndersideManager;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.projectile.AbstractArrow; import net.minecraft.world.entity.projectile.AbstractArrow;
import net.minecraft.world.phys.Vec3;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.ModifyVariable;
@Mixin(AbstractArrow.class) @Mixin(AbstractArrow.class)
/* /*
@@ -18,20 +17,24 @@ import org.spongepowered.asm.mixin.injection.ModifyVariable;
*/ */
public abstract class AbstractArrowGravityMixin { public abstract class AbstractArrowGravityMixin {
@ModifyVariable(method = "tick", at = @At(value = "STORE"), ordinal = 0) @WrapOperation(method = "tick", at = @At(
private Vec3 antarchy$fixArrowGravityDroop(Vec3 velocity) { value = "INVOKE",
AbstractArrow self = (AbstractArrow) (Object) this; target = "Lnet/minecraft/world/entity/projectile/AbstractArrow;applyGravity()V"))
private void antarchy$applyArrowGravityUpward(AbstractArrow self, Operation<Void> original) {
Entity owner = self.getOwner(); Entity owner = self.getOwner();
if (!(owner instanceof LivingEntity living)) return velocity; if (!(owner instanceof LivingEntity living)) {
if (owner instanceof net.minecraft.world.entity.player.Player && !AntarchySettings.invertProjectilesFromInvertedPlayers()) { original.call(self);
return velocity; return;
} }
AntarchyGravityDirection direction = AntarchyGravityApi.getGravityDirection(living); if (owner instanceof net.minecraft.world.entity.player.Player && !AntarchySettings.invertProjectilesFromInvertedPlayers()) {
if (!direction.isInverted()) return velocity; original.call(self);
velocity = new Vec3(velocity.x, velocity.y + 0.05D, velocity.z); return;
velocity = AntarchyGravityRotationUtil.vecWorldToPlayer(velocity, direction); }
velocity = new Vec3(velocity.x, velocity.y - 0.05D, velocity.z); if (!ThoraxisUndersideManager.shouldInvertInUnderside(living)) {
velocity = AntarchyGravityRotationUtil.vecPlayerToWorld(velocity, direction); original.call(self);
return velocity; return;
}
double gravity = self.getGravity();
self.setDeltaMovement(self.getDeltaMovement().add(0.0D, gravity, 0.0D));
} }
} }
@@ -68,8 +68,6 @@ public abstract class BehaviorUtilsGravityMixin {
} }
private static boolean antarchy$shouldInvertThrownItem(LivingEntity entity) { private static boolean antarchy$shouldInvertThrownItem(LivingEntity entity) {
return AntarchyGravityApi.isGravityInverted(entity) return ThoraxisUndersideManager.shouldInvertInUnderside(entity);
&& ThoraxisUndersideManager.isThoraxis(entity.level())
&& entity.getY() < ThoraxisUndersideManager.GRAVITY_FLIP_Y;
} }
} }
@@ -54,10 +54,6 @@ public abstract class BucketItemGravityMixin {
} }
Direction face = blockHitResult.getDirection(); Direction face = blockHitResult.getDirection();
if (face.getAxis() != Direction.Axis.Y) {
return;
}
BlockPos clickedPos = blockHitResult.getBlockPos(); BlockPos clickedPos = blockHitResult.getBlockPos();
BlockState clickedState = level.getBlockState(clickedPos); BlockState clickedState = level.getBlockState(clickedPos);
Block clickedBlock = clickedState.getBlock(); Block clickedBlock = clickedState.getBlock();
@@ -4,6 +4,7 @@ import com.craisinlord.antarchy.content.gravity.AntarchyGravityApi;
import com.craisinlord.antarchy.content.gravity.AntarchyGravityDirection; import com.craisinlord.antarchy.content.gravity.AntarchyGravityDirection;
import com.craisinlord.antarchy.content.gravity.AntarchyGravityRotationUtil; import com.craisinlord.antarchy.content.gravity.AntarchyGravityRotationUtil;
import com.craisinlord.antarchy.config.AntarchySettings; import com.craisinlord.antarchy.config.AntarchySettings;
import com.craisinlord.antarchy.content.worldgen.thoraxis.ThoraxisUndersideManager;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation; import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.LivingEntity;
@@ -54,7 +55,8 @@ public abstract class CrossbowItemGravityMixin {
} }
private static boolean antarchy$shouldFixProjectileSpawn(LivingEntity shooter) { private static boolean antarchy$shouldFixProjectileSpawn(LivingEntity shooter) {
return !(shooter instanceof net.minecraft.world.entity.player.Player) return ThoraxisUndersideManager.shouldInvertInUnderside(shooter)
|| AntarchySettings.invertProjectilesFromInvertedPlayers(); && (!(shooter instanceof net.minecraft.world.entity.player.Player)
|| AntarchySettings.invertProjectilesFromInvertedPlayers());
} }
} }
@@ -1,19 +1,41 @@
package com.craisinlord.antarchy.mixins.gravity; package com.craisinlord.antarchy.mixins.gravity;
import com.craisinlord.antarchy.content.gravity.AntarchyGravityApi; import com.craisinlord.antarchy.content.gravity.AntarchyGravityApi;
import com.craisinlord.antarchy.content.gravity.AntarchyGravityDirection;
import com.craisinlord.antarchy.config.AntarchySettings; import com.craisinlord.antarchy.config.AntarchySettings;
import com.craisinlord.antarchy.content.worldgen.thoraxis.ThoraxisUndersideManager;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.player.Player;
import net.minecraft.world.entity.projectile.ThrowableProjectile; import net.minecraft.world.entity.projectile.ThrowableProjectile;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(ThrowableProjectile.class) @Mixin(ThrowableProjectile.class)
/* /*
* Flips thrown projectile gravity for inverted shooters. * Flips thrown projectile gravity for inverted shooters.
*/ */
public abstract class ThrowableProjectileGravityMixin { public abstract class ThrowableProjectileGravityMixin {
@WrapOperation(method = "tick", at = @At(
value = "INVOKE",
target = "Lnet/minecraft/world/entity/projectile/ThrowableProjectile;applyGravity()V"))
private void antarchy$applyThrownProjectileGravityUpward(ThrowableProjectile self, Operation<Void> original) {
Entity owner = self.getOwner();
if (!(owner instanceof LivingEntity living)) {
original.call(self);
return;
}
if (owner instanceof Player && !AntarchySettings.invertProjectilesFromInvertedPlayers()) {
original.call(self);
return;
}
if (!ThoraxisUndersideManager.shouldInvertInUnderside(living)) {
original.call(self);
return;
}
double gravity = self.getGravity();
self.setDeltaMovement(self.getDeltaMovement().add(0.0D, gravity, 0.0D));
}
} }
@@ -21,7 +21,7 @@ public abstract class EntityDeltaMovementTimeDilationMixin {
} }
double rate = TimeDilationApi.getRate(entity); double rate = TimeDilationApi.getRate(entity);
if (Math.abs(rate - TimeDilationMath.NORMAL_RATE) < 0.001D) { if (rate >= TimeDilationMath.NORMAL_RATE - 0.001D) {
return nextMovement; return nextMovement;
} }
@@ -58,6 +58,13 @@ public abstract class LivingEntityRendererAnimationTimeDilationMixin {
return entity == null ? amount : ClientTimeDilationTicker.dilateWalkAmount(entity, amount); return entity == null ? amount : ClientTimeDilationTicker.dilateWalkAmount(entity, amount);
} }
@Redirect(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/WalkAnimationState;position(F)F"))
private float antarchy$scaleWalkPosition(net.minecraft.world.entity.WalkAnimationState state, float partialTick) {
LivingEntity entity = antarchy$renderingEntity.get();
float position = state.position(partialTick);
return entity == null ? position : ClientTimeDilationTicker.dilateWalkPosition(entity, position);
}
@org.spongepowered.asm.mixin.injection.Inject(method = "getOverlayCoords", at = @At("HEAD"), cancellable = true) @org.spongepowered.asm.mixin.injection.Inject(method = "getOverlayCoords", at = @At("HEAD"), cancellable = true)
private static void antarchy$slowHurtOverlay(LivingEntity entity, float partialTick, CallbackInfoReturnable<Integer> cir) { private static void antarchy$slowHurtOverlay(LivingEntity entity, float partialTick, CallbackInfoReturnable<Integer> cir) {
if (com.craisinlord.antarchy.content.time.TimeDilationApi.getRate(entity) < 1.0D if (com.craisinlord.antarchy.content.time.TimeDilationApi.getRate(entity) < 1.0D
@@ -1,6 +1,7 @@
package com.craisinlord.antarchy.mixins.time; package com.craisinlord.antarchy.mixins.time;
import com.craisinlord.antarchy.content.time.TimeDilationApi; import com.craisinlord.antarchy.content.time.TimeDilationApi;
import com.craisinlord.antarchy.content.time.TimeDilationManager;
import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionHand;
import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.LivingEntity;
@@ -11,6 +12,11 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(LivingEntity.class) @Mixin(LivingEntity.class)
public abstract class LivingEntityTimeDilationMixin { public abstract class LivingEntityTimeDilationMixin {
@Inject(method = "tickEffects", at = @At("HEAD"))
private void antarchy$trackTemporalEffects(CallbackInfo ci) {
TimeDilationManager.trackPotentiallyAffected((Entity) (Object) this);
}
@Inject(method = "updateSwingTime", at = @At("HEAD"), cancellable = true) @Inject(method = "updateSwingTime", at = @At("HEAD"), cancellable = true)
private void antarchy$slowSwingTime(CallbackInfo ci) { private void antarchy$slowSwingTime(CallbackInfo ci) {
Entity entity = (Entity) (Object) this; Entity entity = (Entity) (Object) this;
@@ -543,7 +543,7 @@
"item.antarchy.royal_assailant_battleaxe": "Royal Assailant Battle Axe", "item.antarchy.royal_assailant_battleaxe": "Royal Assailant Battle Axe",
"item.antarchy.royal_assailant_staff": "Royal Assailant Staff", "item.antarchy.royal_assailant_staff": "Royal Assailant Staff",
"tooltip.antarchy.royal_assailant_staff": "Opens paired tears that summon four allied Manticores for 30 seconds.", "tooltip.antarchy.royal_assailant_staff": "Opens paired tears that summon four allied Manticores for 30 seconds.",
"tooltip.antarchy.royal_assailant_battleaxe.black_hole": "Use to create a Royal Black Hole", "tooltip.antarchy.royal_assailant_battleaxe.black_hole": "Use to create a Black Hole",
"tooltip.antarchy.royal_assailant_battleaxe.dilation": "Hits apply Temporal Dilation", "tooltip.antarchy.royal_assailant_battleaxe.dilation": "Hits apply Temporal Dilation",
"tooltip.antarchy.royal_guardian_shield.command": "Use to command the nearest mob", "tooltip.antarchy.royal_guardian_shield.command": "Use to command the nearest mob",
"tooltip.antarchy.royal_assailant_chestplate.invert": "Press the inversion key to toggle inverted gravity on and off.", "tooltip.antarchy.royal_assailant_chestplate.invert": "Press the inversion key to toggle inverted gravity on and off.",
@@ -1047,6 +1047,9 @@
"subtitles.antarchy.king_beam_shoot": "The King fires a beam", "subtitles.antarchy.king_beam_shoot": "The King fires a beam",
"subtitles.antarchy.king_fireball_shoot": "The King breathes fire", "subtitles.antarchy.king_fireball_shoot": "The King breathes fire",
"subtitles.antarchy.king_decree_cast": "The King pronounces a Decree", "subtitles.antarchy.king_decree_cast": "The King pronounces a Decree",
"subtitles.antarchy.king_decree": "The King issues a Decree",
"subtitles.antarchy.king_success": "The King acknowledges compliance",
"subtitles.antarchy.king_judgement": "The King passes Judgement",
"subtitles.antarchy.queen_idle": "The Queen murmurs", "subtitles.antarchy.queen_idle": "The Queen murmurs",
"subtitles.antarchy.queen_walk": "The Queen's footfalls", "subtitles.antarchy.queen_walk": "The Queen's footfalls",
"subtitles.antarchy.queen_fly_loop": "The Queen's wings beat", "subtitles.antarchy.queen_fly_loop": "The Queen's wings beat",
@@ -2825,6 +2825,30 @@
} }
] ]
}, },
"king_decree": {
"subtitle": "subtitles.antarchy.king_decree",
"sounds": [
{
"name": "antarchy:mob/king/decree"
}
]
},
"king_success": {
"subtitle": "subtitles.antarchy.king_success",
"sounds": [
{
"name": "antarchy:mob/king/success"
}
]
},
"king_judgement": {
"subtitle": "subtitles.antarchy.king_judgement",
"sounds": [
{
"name": "antarchy:mob/king/judgement"
}
]
},
"queen_idle": { "queen_idle": {
"subtitle": "subtitles.antarchy.queen_idle", "subtitle": "subtitles.antarchy.queen_idle",
"sounds": [ "sounds": [
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 314 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 393 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 227 B

@@ -7,6 +7,15 @@
{ {
"type": "minecraft:item", "type": "minecraft:item",
"name": "antarchy:worm_tooth", "name": "antarchy:worm_tooth",
"conditions": [
{
"condition": "minecraft:entity_properties",
"entity": "this",
"predicate": {
"nbt": "{GrowthStage:2}"
}
}
],
"functions": [ "functions": [
{ {
"function": "minecraft:set_count", "function": "minecraft:set_count",
@@ -5,7 +5,18 @@
"minecraft:enchanted_golden_apple", "minecraft:enchanted_golden_apple",
"minecraft:golden_carrot", "minecraft:golden_carrot",
"minecraft:glistering_melon_slice", "minecraft:glistering_melon_slice",
"antarchy:king_scale", "minecraft:gold_ingot",
"antarchy:queen_scale" "minecraft:gold_nugget",
"minecraft:gold_block",
"minecraft:golden_sword",
"minecraft:golden_pickaxe",
"minecraft:golden_axe",
"minecraft:golden_shovel",
"minecraft:golden_hoe",
"minecraft:golden_helmet",
"minecraft:golden_chestplate",
"minecraft:golden_leggings",
"minecraft:golden_boots",
"minecraft:golden_horse_armor"
] ]
} }
@@ -8,6 +8,7 @@
"location": "antarchy:kings_tree/kings_tree1", "location": "antarchy:kings_tree/kings_tree1",
"processors": "antarchy:generic_processor", "processors": "antarchy:generic_processor",
"projection": "rigid", "projection": "rigid",
"ignore_bounds": true,
"enhanced_terrain_adaptation": { "enhanced_terrain_adaptation": {
"type": "integrated_api:custom", "type": "integrated_api:custom",
"carves": true, "carves": true,
@@ -8,6 +8,7 @@
"location": "antarchy:kings_tree/kings_tree2", "location": "antarchy:kings_tree/kings_tree2",
"processors": "antarchy:generic_processor", "processors": "antarchy:generic_processor",
"projection": "rigid", "projection": "rigid",
"ignore_bounds": true,
"enhanced_terrain_adaptation": { "enhanced_terrain_adaptation": {
"type": "integrated_api:custom", "type": "integrated_api:custom",
"carves": true, "carves": true,
@@ -8,6 +8,7 @@
"location": "antarchy:kings_tree/kings_tree3", "location": "antarchy:kings_tree/kings_tree3",
"processors": "antarchy:generic_processor", "processors": "antarchy:generic_processor",
"projection": "rigid", "projection": "rigid",
"ignore_bounds": true,
"enhanced_terrain_adaptation": { "enhanced_terrain_adaptation": {
"type": "integrated_api:custom", "type": "integrated_api:custom",
"carves": true, "carves": true,
@@ -8,6 +8,7 @@
"location": "antarchy:kings_tree/kings_tree4", "location": "antarchy:kings_tree/kings_tree4",
"processors": "antarchy:generic_processor", "processors": "antarchy:generic_processor",
"projection": "rigid", "projection": "rigid",
"ignore_bounds": true,
"enhanced_terrain_adaptation": { "enhanced_terrain_adaptation": {
"type": "integrated_api:custom", "type": "integrated_api:custom",
"carves": true, "carves": true,
@@ -8,10 +8,11 @@
"location": "antarchy:kings_tree/kings_tree_bottom", "location": "antarchy:kings_tree/kings_tree_bottom",
"processors": "antarchy:generic_processor", "processors": "antarchy:generic_processor",
"projection": "rigid", "projection": "rigid",
"ignore_bounds": true,
"enhanced_terrain_adaptation": { "enhanced_terrain_adaptation": {
"type": "integrated_api:custom", "type": "integrated_api:custom",
"carves": true, "carves": false,
"beards": true, "beards": false,
"kernel_size": 35, "kernel_size": 35,
"kernel_distance": 35 "kernel_distance": 35
}, },
@@ -340,7 +340,10 @@ public final class AntarchyFabricContent {
AntarchyFabricSounds.PRINCESS_BITE, AntarchyFabricSounds.PRINCESS_BITE,
AntarchyFabricSounds.PRINCESS_SHOOT, AntarchyFabricSounds.PRINCESS_SHOOT,
AntarchyFabricSounds.PRINCESS_FLY, AntarchyFabricSounds.PRINCESS_FLY,
AntarchyFabricSounds.PRINCESS_STEP AntarchyFabricSounds.PRINCESS_STEP,
AntarchyFabricSounds.KING_DECREE,
AntarchyFabricSounds.KING_SUCCESS,
AntarchyFabricSounds.KING_JUDGEMENT
); );
AntarchySoundEvents.bindDimensionalTearSounds( AntarchySoundEvents.bindDimensionalTearSounds(
AntarchyFabricSounds.DIMENSIONAL_TEAR_OPEN, AntarchyFabricSounds.DIMENSIONAL_TEAR_OPEN,
@@ -360,6 +363,7 @@ public final class AntarchyFabricContent {
AntarchyObjects.setWindVortex(() -> AntarchyFabricEntities.WIND_VORTEX.get()); AntarchyObjects.setWindVortex(() -> AntarchyFabricEntities.WIND_VORTEX.get());
AntarchyObjects.setTimeDilationField(() -> AntarchyFabricEntities.TIME_DILATION_FIELD.get()); AntarchyObjects.setTimeDilationField(() -> AntarchyFabricEntities.TIME_DILATION_FIELD.get());
AntarchyObjects.setRoyalBlackHole(() -> AntarchyFabricEntities.ROYAL_BLACK_HOLE.get()); AntarchyObjects.setRoyalBlackHole(() -> AntarchyFabricEntities.ROYAL_BLACK_HOLE.get());
AntarchyObjects.setRoyalAssailantBlackHole(() -> AntarchyFabricEntities.ROYAL_ASSAILANT_BLACK_HOLE.get());
AntarchyObjects.setVortexChargeProjectile(() -> AntarchyFabricEntities.VORTEX_CHARGE_PROJECTILE.get()); AntarchyObjects.setVortexChargeProjectile(() -> AntarchyFabricEntities.VORTEX_CHARGE_PROJECTILE.get());
AntarchyObjects.setPrince(() -> AntarchyFabricEntities.PRINCE.get()); AntarchyObjects.setPrince(() -> AntarchyFabricEntities.PRINCE.get());
AntarchyObjects.setPrincess(() -> AntarchyFabricEntities.PRINCESS.get()); AntarchyObjects.setPrincess(() -> AntarchyFabricEntities.PRINCESS.get());
@@ -148,6 +148,7 @@ public final class AntarchyFabricClientBootstrap {
EntityRendererRegistry.register(AntarchyFabricEntities.WIND_VORTEX.get(), WindVortexRenderer::new); EntityRendererRegistry.register(AntarchyFabricEntities.WIND_VORTEX.get(), WindVortexRenderer::new);
EntityRendererRegistry.register(AntarchyFabricEntities.TIME_DILATION_FIELD.get(), TimeDilationFieldRenderer::new); EntityRendererRegistry.register(AntarchyFabricEntities.TIME_DILATION_FIELD.get(), TimeDilationFieldRenderer::new);
EntityRendererRegistry.register(AntarchyFabricEntities.ROYAL_BLACK_HOLE.get(), com.craisinlord.antarchy.content.client.renderer.RoyalBlackHoleRenderer::new); EntityRendererRegistry.register(AntarchyFabricEntities.ROYAL_BLACK_HOLE.get(), com.craisinlord.antarchy.content.client.renderer.RoyalBlackHoleRenderer::new);
EntityRendererRegistry.register(AntarchyFabricEntities.ROYAL_ASSAILANT_BLACK_HOLE.get(), com.craisinlord.antarchy.content.client.renderer.RoyalAssailantBlackHoleRenderer::new);
EntityRendererRegistry.register(AntarchyFabricEntities.CRITTER_CAGE_PROJECTILE.get(), ThrownItemRenderer::new); EntityRendererRegistry.register(AntarchyFabricEntities.CRITTER_CAGE_PROJECTILE.get(), ThrownItemRenderer::new);
EntityRendererRegistry.register(AntarchyFabricEntities.WORM_HOOK_PROJECTILE.get(), ThrownItemRenderer::new); EntityRendererRegistry.register(AntarchyFabricEntities.WORM_HOOK_PROJECTILE.get(), ThrownItemRenderer::new);
EntityRendererRegistry.register(AntarchyFabricEntities.HUSH_PROJECTILE.get(), HushProjectileRenderer::new); EntityRendererRegistry.register(AntarchyFabricEntities.HUSH_PROJECTILE.get(), HushProjectileRenderer::new);
@@ -1,4 +1,5 @@
package com.craisinlord.antarchy.fabric.content.fluid; package com.craisinlord.antarchy.fabric.content.fluid;
import com.craisinlord.antarchy.content.fluid.AntiwaterBubbleColumnUtil;
import com.craisinlord.antarchy.fabric.registry.AntarchyFabricBlocks; import com.craisinlord.antarchy.fabric.registry.AntarchyFabricBlocks;
import com.craisinlord.antarchy.fabric.registry.AntarchyFabricItems; import com.craisinlord.antarchy.fabric.registry.AntarchyFabricItems;
import com.craisinlord.antarchy.fabric.registry.AntarchyFabricMisc; import com.craisinlord.antarchy.fabric.registry.AntarchyFabricMisc;
@@ -169,6 +170,8 @@ public abstract class AntiwaterFluid extends FlowingFluid {
return; return;
} }
AntiwaterBubbleColumnUtil.update(level, pos, state);
if (!state.isSource() && !this.isFlowingStateValid(level, pos, state)) { if (!state.isSource() && !this.isFlowingStateValid(level, pos, state)) {
level.setBlockAndUpdate(pos, net.minecraft.world.level.block.Blocks.AIR.defaultBlockState()); level.setBlockAndUpdate(pos, net.minecraft.world.level.block.Blocks.AIR.defaultBlockState());
this.scheduleCleanup(level, pos); this.scheduleCleanup(level, pos);
@@ -484,6 +484,13 @@ public final class AntarchyFabricEntities {
.updateInterval(2) .updateInterval(2)
.fireImmune() .fireImmune()
.build("royal_black_hole")); .build("royal_black_hole"));
public static final DeferredHolder<EntityType<?>, EntityType<com.craisinlord.antarchy.content.entity.royal.RoyalAssailantBlackHoleEntity>> ROYAL_ASSAILANT_BLACK_HOLE = ENTITY_TYPES.register("royal_assailant_black_hole",
() -> EntityType.Builder.<com.craisinlord.antarchy.content.entity.royal.RoyalAssailantBlackHoleEntity>of(com.craisinlord.antarchy.content.entity.royal.RoyalAssailantBlackHoleEntity::new, MobCategory.MISC)
.sized(1.0F, 1.0F)
.clientTrackingRange(16)
.updateInterval(2)
.fireImmune()
.build("royal_assailant_black_hole"));
public static final DeferredHolder<EntityType<?>, EntityType<LucidBoltEntity>> LUCID_BOLT = ENTITY_TYPES.register("lucid_bolt", public static final DeferredHolder<EntityType<?>, EntityType<LucidBoltEntity>> LUCID_BOLT = ENTITY_TYPES.register("lucid_bolt",
@@ -543,6 +543,9 @@ public final class AntarchyFabricSounds {
public static final DeferredHolder<SoundEvent, SoundEvent> KING_BEAM_SHOOT = registerSoundEvent("king_beam_shoot"); public static final DeferredHolder<SoundEvent, SoundEvent> KING_BEAM_SHOOT = registerSoundEvent("king_beam_shoot");
public static final DeferredHolder<SoundEvent, SoundEvent> KING_FIREBALL_SHOOT = registerSoundEvent("king_fireball_shoot"); public static final DeferredHolder<SoundEvent, SoundEvent> KING_FIREBALL_SHOOT = registerSoundEvent("king_fireball_shoot");
public static final DeferredHolder<SoundEvent, SoundEvent> KING_DECREE_CAST = registerSoundEvent("king_decree_cast"); public static final DeferredHolder<SoundEvent, SoundEvent> KING_DECREE_CAST = registerSoundEvent("king_decree_cast");
public static final DeferredHolder<SoundEvent, SoundEvent> KING_DECREE = registerSoundEvent("king_decree");
public static final DeferredHolder<SoundEvent, SoundEvent> KING_SUCCESS = registerSoundEvent("king_success");
public static final DeferredHolder<SoundEvent, SoundEvent> KING_JUDGEMENT = registerSoundEvent("king_judgement");
public static final DeferredHolder<SoundEvent, SoundEvent> QUEEN_IDLE = registerSoundEvent("queen_idle"); public static final DeferredHolder<SoundEvent, SoundEvent> QUEEN_IDLE = registerSoundEvent("queen_idle");
public static final DeferredHolder<SoundEvent, SoundEvent> QUEEN_WALK = registerSoundEvent("queen_walk"); public static final DeferredHolder<SoundEvent, SoundEvent> QUEEN_WALK = registerSoundEvent("queen_walk");
@@ -337,9 +337,9 @@ public final class AntarchyForgeMisc {
// Biome sources // Biome sources
public static final RegistryObject<com.mojang.serialization.Codec<ElythiaBiomeSource>> ELYTHIA_BIOME_SOURCE = BIOME_SOURCES.register("elythia_biome_source", public static final RegistryObject<com.mojang.serialization.Codec<ElythiaBiomeSource>> ELYTHIA_BIOME_SOURCE = BIOME_SOURCES.register("elythia_biome_source",
() -> ElythiaBiomeSource.CODEC.codec()); () -> ElythiaBiomeSource.CODEC);
public static final RegistryObject<com.mojang.serialization.Codec<ThoraxisBiomeSource>> THORAXIS_BIOME_SOURCE = BIOME_SOURCES.register("thoraxis_biome_source", public static final RegistryObject<com.mojang.serialization.Codec<ThoraxisBiomeSource>> THORAXIS_BIOME_SOURCE = BIOME_SOURCES.register("thoraxis_biome_source",
() -> ThoraxisBiomeSource.CODEC.codec()); () -> ThoraxisBiomeSource.CODEC);
// Density function // Density function
public static final RegistryObject<com.mojang.serialization.Codec<ElythiaRiverCarveFunction>> ELYTHIA_RIVER_CARVE = DENSITY_FUNCTION_TYPES.register("elythia_river_carve", public static final RegistryObject<com.mojang.serialization.Codec<ElythiaRiverCarveFunction>> ELYTHIA_RIVER_CARVE = DENSITY_FUNCTION_TYPES.register("elythia_river_carve",
@@ -159,6 +159,10 @@ public final class AntarchyConfigModuleNeoforge {
AntarchySettings.setQueenBeamTracking(AntarchyMobsConfig.queenBeamTracking()); AntarchySettings.setQueenBeamTracking(AntarchyMobsConfig.queenBeamTracking());
AntarchySettings.setQueenBeamTerrainRadius(AntarchyMobsConfig.queenBeamTerrainRadius()); AntarchySettings.setQueenBeamTerrainRadius(AntarchyMobsConfig.queenBeamTerrainRadius());
AntarchySettings.setQueenBeamTerrainCap(AntarchyMobsConfig.queenBeamTerrainCap()); AntarchySettings.setQueenBeamTerrainCap(AntarchyMobsConfig.queenBeamTerrainCap());
AntarchySettings.setQueenBeamPhaseOneCap(AntarchyMobsConfig.queenBeamPhaseOneCap());
AntarchySettings.setQueenBeamPhaseTwoCap(AntarchyMobsConfig.queenBeamPhaseTwoCap());
AntarchySettings.setQueenBeamPhaseThreeCap(AntarchyMobsConfig.queenBeamPhaseThreeCap());
AntarchySettings.setQueenBeamPhaseThreeMaxVolleyChance(AntarchyMobsConfig.queenBeamPhaseThreeMaxVolleyChance());
AntarchySettings.setKingBeamDamage(AntarchyMobsConfig.kingBeamDamage()); AntarchySettings.setKingBeamDamage(AntarchyMobsConfig.kingBeamDamage());
AntarchySettings.setKingBeamRange(AntarchyMobsConfig.kingBeamRange()); AntarchySettings.setKingBeamRange(AntarchyMobsConfig.kingBeamRange());
AntarchySettings.setKingBeamMinimumRange(AntarchyMobsConfig.kingBeamMinimumRange()); AntarchySettings.setKingBeamMinimumRange(AntarchyMobsConfig.kingBeamMinimumRange());
@@ -178,6 +182,7 @@ public final class AntarchyConfigModuleNeoforge {
AntarchySettings.setQueenBlackHoleRadius(AntarchyMobsConfig.queenBlackHoleRadius()); AntarchySettings.setQueenBlackHoleRadius(AntarchyMobsConfig.queenBlackHoleRadius());
AntarchySettings.setQueenBlackHoleActiveTicks(AntarchyMobsConfig.queenBlackHoleActiveTicks()); AntarchySettings.setQueenBlackHoleActiveTicks(AntarchyMobsConfig.queenBlackHoleActiveTicks());
AntarchySettings.setQueenBlackHolePullStrength(AntarchyMobsConfig.queenBlackHolePullStrength()); AntarchySettings.setQueenBlackHolePullStrength(AntarchyMobsConfig.queenBlackHolePullStrength());
AntarchySettings.setQueenBlackHoleBlockSuctionCap(AntarchyMobsConfig.queenBlackHoleBlockSuctionCap());
AntarchySettings.setQueenFinalTimeFieldRadius(AntarchyMobsConfig.queenFinalTimeFieldRadius()); AntarchySettings.setQueenFinalTimeFieldRadius(AntarchyMobsConfig.queenFinalTimeFieldRadius());
AntarchySettings.setQueenFinalTimeFieldRate(AntarchyMobsConfig.queenFinalTimeFieldRate()); AntarchySettings.setQueenFinalTimeFieldRate(AntarchyMobsConfig.queenFinalTimeFieldRate());
AntarchySettings.setQueenFinalTimeFieldDurationTicks(AntarchyMobsConfig.queenFinalTimeFieldDurationTicks()); AntarchySettings.setQueenFinalTimeFieldDurationTicks(AntarchyMobsConfig.queenFinalTimeFieldDurationTicks());
@@ -125,7 +125,9 @@ public final class AntarchyMobsConfig {
private static final ModConfigSpec.DoubleValue ROYAL_BOSS_BITE_DAMAGE_MULTIPLIER; private static final ModConfigSpec.DoubleValue ROYAL_BOSS_BITE_DAMAGE_MULTIPLIER;
private static final ModConfigSpec.IntValue ROYAL_BOSS_BITE_COOLDOWN_TICKS; private static final ModConfigSpec.IntValue ROYAL_BOSS_BITE_COOLDOWN_TICKS;
private static final ModConfigSpec.DoubleValue QUEEN_BEAM_DAMAGE, QUEEN_BEAM_RANGE, QUEEN_BEAM_MINIMUM_RANGE, QUEEN_BEAM_TRACKING, QUEEN_BEAM_TERRAIN_RADIUS; private static final ModConfigSpec.DoubleValue QUEEN_BEAM_DAMAGE, QUEEN_BEAM_RANGE, QUEEN_BEAM_MINIMUM_RANGE, QUEEN_BEAM_TRACKING, QUEEN_BEAM_TERRAIN_RADIUS;
private static final ModConfigSpec.IntValue QUEEN_BEAM_DURATION_TICKS, QUEEN_BEAM_WINDUP_TICKS, QUEEN_BEAM_TRAVEL_TICKS, QUEEN_BEAM_COOLDOWN_TICKS, QUEEN_BEAM_TERRAIN_CAP; private static final ModConfigSpec.IntValue QUEEN_BEAM_DURATION_TICKS, QUEEN_BEAM_WINDUP_TICKS, QUEEN_BEAM_TRAVEL_TICKS, QUEEN_BEAM_COOLDOWN_TICKS, QUEEN_BEAM_TERRAIN_CAP,
QUEEN_BEAM_PHASE_ONE_CAP, QUEEN_BEAM_PHASE_TWO_CAP, QUEEN_BEAM_PHASE_THREE_CAP;
private static final ModConfigSpec.DoubleValue QUEEN_BEAM_PHASE_THREE_MAX_VOLLEY_CHANCE;
private static final ModConfigSpec.DoubleValue KING_BEAM_DAMAGE, KING_BEAM_RANGE, KING_BEAM_MINIMUM_RANGE, KING_BEAM_TRACKING, KING_BEAM_TERRAIN_RADIUS; private static final ModConfigSpec.DoubleValue KING_BEAM_DAMAGE, KING_BEAM_RANGE, KING_BEAM_MINIMUM_RANGE, KING_BEAM_TRACKING, KING_BEAM_TERRAIN_RADIUS;
private static final ModConfigSpec.IntValue KING_BEAM_DURATION_TICKS, KING_BEAM_WINDUP_TICKS, KING_BEAM_TRAVEL_TICKS, KING_BEAM_COOLDOWN_TICKS, KING_BEAM_TERRAIN_CAP; private static final ModConfigSpec.IntValue KING_BEAM_DURATION_TICKS, KING_BEAM_WINDUP_TICKS, KING_BEAM_TRAVEL_TICKS, KING_BEAM_COOLDOWN_TICKS, KING_BEAM_TERRAIN_CAP;
private static final ModConfigSpec.IntValue ROYAL_DECREE_COOLDOWN_TICKS; private static final ModConfigSpec.IntValue ROYAL_DECREE_COOLDOWN_TICKS;
@@ -134,7 +136,7 @@ public final class AntarchyMobsConfig {
KING_ROYAL_MUSTER_DURATION_TICKS, KING_ROYAL_MUSTER_CAP; KING_ROYAL_MUSTER_DURATION_TICKS, KING_ROYAL_MUSTER_CAP;
private static final ModConfigSpec.DoubleValue QUEEN_BLACK_HOLE_RADIUS, QUEEN_BLACK_HOLE_PULL_STRENGTH, private static final ModConfigSpec.DoubleValue QUEEN_BLACK_HOLE_RADIUS, QUEEN_BLACK_HOLE_PULL_STRENGTH,
QUEEN_FINAL_TIME_FIELD_RADIUS, QUEEN_FINAL_TIME_FIELD_RATE; QUEEN_FINAL_TIME_FIELD_RADIUS, QUEEN_FINAL_TIME_FIELD_RATE;
private static final ModConfigSpec.IntValue QUEEN_BLACK_HOLE_ACTIVE_TICKS, private static final ModConfigSpec.IntValue QUEEN_BLACK_HOLE_ACTIVE_TICKS, QUEEN_BLACK_HOLE_BLOCK_SUCTION_CAP,
QUEEN_FINAL_TIME_FIELD_DURATION_TICKS, QUEEN_FINAL_TIME_FIELD_COOLDOWN_TICKS; QUEEN_FINAL_TIME_FIELD_DURATION_TICKS, QUEEN_FINAL_TIME_FIELD_COOLDOWN_TICKS;
private static final ModConfigSpec.DoubleValue ROYAL_BOSS_SOUND_VOLUME; private static final ModConfigSpec.DoubleValue ROYAL_BOSS_SOUND_VOLUME;
private static final ModConfigSpec.DoubleValue KING_FIREBALL_DAMAGE, KING_FIREBALL_RADIUS, KING_ICEBALL_DAMAGE, KING_ICEBALL_RADIUS, KING_ICE_SPIKE_DAMAGE; private static final ModConfigSpec.DoubleValue KING_FIREBALL_DAMAGE, KING_FIREBALL_RADIUS, KING_ICEBALL_DAMAGE, KING_ICEBALL_RADIUS, KING_ICE_SPIKE_DAMAGE;
@@ -583,7 +585,7 @@ public final class AntarchyMobsConfig {
ROYAL_BOSS_KNOCKBACK_RESISTANCE = b.comment("Base knockback resistance shared by King and Queen.").defineInRange("knockbackResistance", 1.0D, 0.0D, 1.0D); ROYAL_BOSS_KNOCKBACK_RESISTANCE = b.comment("Base knockback resistance shared by King and Queen.").defineInRange("knockbackResistance", 1.0D, 0.0D, 1.0D);
ROYAL_BOSS_STEP_HEIGHT = b.comment("Base step height shared by King and Queen.").defineInRange("stepHeight", 3.0D, 0.0D, 32.0D); ROYAL_BOSS_STEP_HEIGHT = b.comment("Base step height shared by King and Queen.").defineInRange("stepHeight", 3.0D, 0.0D, 32.0D);
ROYAL_BOSS_MAX_SINGLE_HIT_DAMAGE = b.comment("Maximum damage a single hit can deal to a Royal boss before later anti-burst systems are added.").defineInRange("maxSingleHitDamage", 250.0D, 1.0D, 32768.0D); ROYAL_BOSS_MAX_SINGLE_HIT_DAMAGE = b.comment("Maximum damage a single hit can deal to a Royal boss before later anti-burst systems are added.").defineInRange("maxSingleHitDamage", 250.0D, 1.0D, 32768.0D);
ROYAL_BOSS_BITE_REACH = b.comment("Radius around each head anchor that a King or Queen bite can hit.").defineInRange("biteReach", 10.0D, 1.0D, 64.0D); ROYAL_BOSS_BITE_REACH = b.comment("Radius around each head anchor that a King or Queen bite can hit.").defineInRange("biteReach", 12.0D, 1.0D, 64.0D);
ROYAL_BOSS_BITE_DAMAGE_MULTIPLIER = b.comment("Multiplier applied to base attack damage for King and Queen head bites.").defineInRange("biteDamageMultiplier", 1.6D, 0.0D, 32.0D); ROYAL_BOSS_BITE_DAMAGE_MULTIPLIER = b.comment("Multiplier applied to base attack damage for King and Queen head bites.").defineInRange("biteDamageMultiplier", 1.6D, 0.0D, 32.0D);
ROYAL_BOSS_BITE_COOLDOWN_TICKS = b.comment("Base cooldown in ticks between bites for a single King or Queen head (scaled down at later phases).").defineInRange("biteCooldownTicks", 45, 1, 1200); ROYAL_BOSS_BITE_COOLDOWN_TICKS = b.comment("Base cooldown in ticks between bites for a single King or Queen head (scaled down at later phases).").defineInRange("biteCooldownTicks", 45, 1, 1200);
QUEEN_BEAM_DAMAGE = b.defineInRange("queenBeamDamage", 45.0D, 0.0D, 1024.0D); QUEEN_BEAM_DAMAGE = b.defineInRange("queenBeamDamage", 45.0D, 0.0D, 1024.0D);
@@ -596,6 +598,10 @@ public final class AntarchyMobsConfig {
QUEEN_BEAM_TRACKING = b.defineInRange("queenBeamTracking", 0.1D, 0.0D, 1.0D); QUEEN_BEAM_TRACKING = b.defineInRange("queenBeamTracking", 0.1D, 0.0D, 1.0D);
QUEEN_BEAM_TERRAIN_RADIUS = b.defineInRange("queenBeamTerrainRadius", 5.0D, 0.0D, 32.0D); QUEEN_BEAM_TERRAIN_RADIUS = b.defineInRange("queenBeamTerrainRadius", 5.0D, 0.0D, 32.0D);
QUEEN_BEAM_TERRAIN_CAP = b.defineInRange("queenBeamTerrainCap", 192, 0, 4096); QUEEN_BEAM_TERRAIN_CAP = b.defineInRange("queenBeamTerrainCap", 192, 0, 4096);
QUEEN_BEAM_PHASE_ONE_CAP = b.comment("Maximum simultaneous Queen beams in phase one.").defineInRange("queenBeamPhaseOneCap", 1, 1, 3);
QUEEN_BEAM_PHASE_TWO_CAP = b.comment("Maximum simultaneous Queen beams in phase two.").defineInRange("queenBeamPhaseTwoCap", 2, 1, 3);
QUEEN_BEAM_PHASE_THREE_CAP = b.comment("Maximum simultaneous Queen beams when phase three rolls a full volley.").defineInRange("queenBeamPhaseThreeCap", 3, 1, 3);
QUEEN_BEAM_PHASE_THREE_MAX_VOLLEY_CHANCE = b.comment("Chance that a new phase-three Queen beam volley may use its full configured cap; otherwise it is limited to two beams.").defineInRange("queenBeamPhaseThreeMaxVolleyChance", 0.2D, 0.0D, 1.0D);
KING_BEAM_DAMAGE = b.defineInRange("kingBeamDamage", 50.0D, 0.0D, 1024.0D); KING_BEAM_DAMAGE = b.defineInRange("kingBeamDamage", 50.0D, 0.0D, 1024.0D);
KING_BEAM_RANGE = b.defineInRange("kingBeamRange", 100.0D, 1.0D, 256.0D); KING_BEAM_RANGE = b.defineInRange("kingBeamRange", 100.0D, 1.0D, 256.0D);
KING_BEAM_MINIMUM_RANGE = b.comment("Minimum distance from the King before he may begin or maintain a beam.").defineInRange("kingBeamMinimumRange", 32.0D, 0.0D, 256.0D); KING_BEAM_MINIMUM_RANGE = b.comment("Minimum distance from the King before he may begin or maintain a beam.").defineInRange("kingBeamMinimumRange", 32.0D, 0.0D, 256.0D);
@@ -613,8 +619,9 @@ public final class AntarchyMobsConfig {
KING_ROYAL_MUSTER_RADIUS = b.comment("Radius of the King's command aura.").defineInRange("kingRoyalMusterRadius", 32.0D, 1.0D, 128.0D); KING_ROYAL_MUSTER_RADIUS = b.comment("Radius of the King's command aura.").defineInRange("kingRoyalMusterRadius", 32.0D, 1.0D, 128.0D);
KING_ROYAL_MUSTER_CAP = b.comment("Maximum hostile mobs affected by one command pulse.").defineInRange("kingRoyalMusterCap", 12, 1, 128); KING_ROYAL_MUSTER_CAP = b.comment("Maximum hostile mobs affected by one command pulse.").defineInRange("kingRoyalMusterCap", 12, 1, 128);
QUEEN_BLACK_HOLE_RADIUS = b.defineInRange("queenBlackHoleRadius", 14.0D, 1.0D, 64.0D); QUEEN_BLACK_HOLE_RADIUS = b.defineInRange("queenBlackHoleRadius", 14.0D, 1.0D, 64.0D);
QUEEN_BLACK_HOLE_ACTIVE_TICKS = b.defineInRange("queenBlackHoleActiveTicks", 120, 20, 1200); QUEEN_BLACK_HOLE_ACTIVE_TICKS = b.comment("Active duration for Royal Assailant axe black holes. Queen black holes always follow their 20.125-second animation.").defineInRange("queenBlackHoleActiveTicks", 120, 20, 1200);
QUEEN_BLACK_HOLE_PULL_STRENGTH = b.defineInRange("queenBlackHolePullStrength", 0.16D, 0.0D, 2.0D); QUEEN_BLACK_HOLE_PULL_STRENGTH = b.defineInRange("queenBlackHolePullStrength", 0.16D, 0.0D, 2.0D);
QUEEN_BLACK_HOLE_BLOCK_SUCTION_CAP = b.comment("Maximum terrain blocks a single Queen black hole may pull into itself.").defineInRange("queenBlackHoleBlockSuctionCap", 160, 0, 4096);
QUEEN_FINAL_TIME_FIELD_RADIUS = b.defineInRange("queenFinalTimeFieldRadius", 20.0D, 1.0D, 64.0D); QUEEN_FINAL_TIME_FIELD_RADIUS = b.defineInRange("queenFinalTimeFieldRadius", 20.0D, 1.0D, 64.0D);
QUEEN_FINAL_TIME_FIELD_RATE = b.defineInRange("queenFinalTimeFieldRate", 0.05D, 0.05D, 1.0D); QUEEN_FINAL_TIME_FIELD_RATE = b.defineInRange("queenFinalTimeFieldRate", 0.05D, 0.05D, 1.0D);
QUEEN_FINAL_TIME_FIELD_DURATION_TICKS = b.defineInRange("queenFinalTimeFieldDurationTicks", 45, 1, 1200); QUEEN_FINAL_TIME_FIELD_DURATION_TICKS = b.defineInRange("queenFinalTimeFieldDurationTicks", 45, 1, 1200);
@@ -1040,6 +1047,10 @@ public final class AntarchyMobsConfig {
static double queenBeamTracking() { return QUEEN_BEAM_TRACKING.get(); } static double queenBeamTracking() { return QUEEN_BEAM_TRACKING.get(); }
static double queenBeamTerrainRadius() { return QUEEN_BEAM_TERRAIN_RADIUS.get(); } static double queenBeamTerrainRadius() { return QUEEN_BEAM_TERRAIN_RADIUS.get(); }
static int queenBeamTerrainCap() { return QUEEN_BEAM_TERRAIN_CAP.get(); } static int queenBeamTerrainCap() { return QUEEN_BEAM_TERRAIN_CAP.get(); }
static int queenBeamPhaseOneCap() { return QUEEN_BEAM_PHASE_ONE_CAP.get(); }
static int queenBeamPhaseTwoCap() { return QUEEN_BEAM_PHASE_TWO_CAP.get(); }
static int queenBeamPhaseThreeCap() { return QUEEN_BEAM_PHASE_THREE_CAP.get(); }
static double queenBeamPhaseThreeMaxVolleyChance() { return QUEEN_BEAM_PHASE_THREE_MAX_VOLLEY_CHANCE.get(); }
static double kingBeamDamage() { return KING_BEAM_DAMAGE.get(); } static double kingBeamDamage() { return KING_BEAM_DAMAGE.get(); }
static double kingBeamRange() { return KING_BEAM_RANGE.get(); } static double kingBeamRange() { return KING_BEAM_RANGE.get(); }
static double kingBeamMinimumRange() { return KING_BEAM_MINIMUM_RANGE.get(); } static double kingBeamMinimumRange() { return KING_BEAM_MINIMUM_RANGE.get(); }
@@ -1059,6 +1070,7 @@ public final class AntarchyMobsConfig {
static double queenBlackHoleRadius() { return QUEEN_BLACK_HOLE_RADIUS.get(); } static double queenBlackHoleRadius() { return QUEEN_BLACK_HOLE_RADIUS.get(); }
static int queenBlackHoleActiveTicks() { return QUEEN_BLACK_HOLE_ACTIVE_TICKS.get(); } static int queenBlackHoleActiveTicks() { return QUEEN_BLACK_HOLE_ACTIVE_TICKS.get(); }
static double queenBlackHolePullStrength() { return QUEEN_BLACK_HOLE_PULL_STRENGTH.get(); } static double queenBlackHolePullStrength() { return QUEEN_BLACK_HOLE_PULL_STRENGTH.get(); }
static int queenBlackHoleBlockSuctionCap() { return QUEEN_BLACK_HOLE_BLOCK_SUCTION_CAP.get(); }
static double queenFinalTimeFieldRadius() { return QUEEN_FINAL_TIME_FIELD_RADIUS.get(); } static double queenFinalTimeFieldRadius() { return QUEEN_FINAL_TIME_FIELD_RADIUS.get(); }
static double queenFinalTimeFieldRate() { return QUEEN_FINAL_TIME_FIELD_RATE.get(); } static double queenFinalTimeFieldRate() { return QUEEN_FINAL_TIME_FIELD_RATE.get(); }
static int queenFinalTimeFieldDurationTicks() { return QUEEN_FINAL_TIME_FIELD_DURATION_TICKS.get(); } static int queenFinalTimeFieldDurationTicks() { return QUEEN_FINAL_TIME_FIELD_DURATION_TICKS.get(); }
@@ -231,7 +231,10 @@ public class AntarchyNeoforge {
AntarchyNeoforgeSounds.PRINCESS_BITE, AntarchyNeoforgeSounds.PRINCESS_BITE,
AntarchyNeoforgeSounds.PRINCESS_SHOOT, AntarchyNeoforgeSounds.PRINCESS_SHOOT,
AntarchyNeoforgeSounds.PRINCESS_FLY, AntarchyNeoforgeSounds.PRINCESS_FLY,
AntarchyNeoforgeSounds.PRINCESS_STEP AntarchyNeoforgeSounds.PRINCESS_STEP,
AntarchyNeoforgeSounds.KING_DECREE,
AntarchyNeoforgeSounds.KING_SUCCESS,
AntarchyNeoforgeSounds.KING_JUDGEMENT
); );
AntarchySoundEvents.bindDimensionalTearSounds( AntarchySoundEvents.bindDimensionalTearSounds(
AntarchyNeoforgeSounds.DIMENSIONAL_TEAR_OPEN, AntarchyNeoforgeSounds.DIMENSIONAL_TEAR_OPEN,
@@ -430,6 +433,7 @@ public class AntarchyNeoforge {
AntarchyObjects.setWindVortex(() -> AntarchyNeoforgeEntites.WIND_VORTEX.get()); AntarchyObjects.setWindVortex(() -> AntarchyNeoforgeEntites.WIND_VORTEX.get());
AntarchyObjects.setTimeDilationField(() -> AntarchyNeoforgeEntites.TIME_DILATION_FIELD.get()); AntarchyObjects.setTimeDilationField(() -> AntarchyNeoforgeEntites.TIME_DILATION_FIELD.get());
AntarchyObjects.setRoyalBlackHole(() -> AntarchyNeoforgeEntites.ROYAL_BLACK_HOLE.get()); AntarchyObjects.setRoyalBlackHole(() -> AntarchyNeoforgeEntites.ROYAL_BLACK_HOLE.get());
AntarchyObjects.setRoyalAssailantBlackHole(() -> AntarchyNeoforgeEntites.ROYAL_ASSAILANT_BLACK_HOLE.get());
AntarchyObjects.setVortexChargeProjectile(() -> AntarchyNeoforgeEntites.VORTEX_CHARGE_PROJECTILE.get()); AntarchyObjects.setVortexChargeProjectile(() -> AntarchyNeoforgeEntites.VORTEX_CHARGE_PROJECTILE.get());
AntarchyObjects.setPrince(() -> AntarchyNeoforgeEntites.PRINCE.get()); AntarchyObjects.setPrince(() -> AntarchyNeoforgeEntites.PRINCE.get());
AntarchyObjects.setPrincess(() -> AntarchyNeoforgeEntites.PRINCESS.get()); AntarchyObjects.setPrincess(() -> AntarchyNeoforgeEntites.PRINCESS.get());
@@ -151,6 +151,7 @@ public final class AntarchyNeoForgeClient {
event.registerEntityRenderer(AntarchyNeoforgeEntites.WIND_VORTEX.get(), WindVortexRenderer::new); event.registerEntityRenderer(AntarchyNeoforgeEntites.WIND_VORTEX.get(), WindVortexRenderer::new);
event.registerEntityRenderer(AntarchyNeoforgeEntites.TIME_DILATION_FIELD.get(), TimeDilationFieldRenderer::new); event.registerEntityRenderer(AntarchyNeoforgeEntites.TIME_DILATION_FIELD.get(), TimeDilationFieldRenderer::new);
event.registerEntityRenderer(AntarchyNeoforgeEntites.ROYAL_BLACK_HOLE.get(), com.craisinlord.antarchy.content.client.renderer.RoyalBlackHoleRenderer::new); event.registerEntityRenderer(AntarchyNeoforgeEntites.ROYAL_BLACK_HOLE.get(), com.craisinlord.antarchy.content.client.renderer.RoyalBlackHoleRenderer::new);
event.registerEntityRenderer(AntarchyNeoforgeEntites.ROYAL_ASSAILANT_BLACK_HOLE.get(), com.craisinlord.antarchy.content.client.renderer.RoyalAssailantBlackHoleRenderer::new);
event.registerEntityRenderer(AntarchyNeoforgeEntites.CRITTER_CAGE_PROJECTILE.get(), ThrownItemRenderer::new); event.registerEntityRenderer(AntarchyNeoforgeEntites.CRITTER_CAGE_PROJECTILE.get(), ThrownItemRenderer::new);
event.registerEntityRenderer(AntarchyNeoforgeEntites.WORM_HOOK_PROJECTILE.get(), ThrownItemRenderer::new); event.registerEntityRenderer(AntarchyNeoforgeEntites.WORM_HOOK_PROJECTILE.get(), ThrownItemRenderer::new);
event.registerEntityRenderer(AntarchyNeoforgeEntites.HUSH_PROJECTILE.get(), HushProjectileRenderer::new); event.registerEntityRenderer(AntarchyNeoforgeEntites.HUSH_PROJECTILE.get(), HushProjectileRenderer::new);
@@ -161,10 +162,10 @@ public final class AntarchyNeoForgeClient {
event.registerEntityRenderer(AntarchyNeoforgeEntites.TORETERROR.get(), ToreterrorRenderer::new); event.registerEntityRenderer(AntarchyNeoforgeEntites.TORETERROR.get(), ToreterrorRenderer::new);
event.registerEntityRenderer(AntarchyNeoforgeEntites.WATER_BOMB.get(), WaterBombRenderer::new); event.registerEntityRenderer(AntarchyNeoforgeEntites.WATER_BOMB.get(), WaterBombRenderer::new);
event.registerEntityRenderer(AntarchyNeoforgeEntites.CHEEP.get(), context -> withParalyzedGeoLayer(new com.craisinlord.antarchy.content.client.renderer.CheepRenderer(context))); event.registerEntityRenderer(AntarchyNeoforgeEntites.CHEEP.get(), context -> withParalyzedGeoLayer(new com.craisinlord.antarchy.content.client.renderer.CheepRenderer(context)));
event.registerEntityRenderer(AntarchyNeoforgeEntites.DORRIE.get(), context -> new com.craisinlord.antarchy.content.client.renderer.DorrieRenderer(context)); event.registerEntityRenderer(AntarchyNeoforgeEntites.DORRIE.get(), context -> withParalyzedGeoLayer(new com.craisinlord.antarchy.content.client.renderer.DorrieRenderer(context)));
event.registerEntityRenderer(AntarchyNeoforgeEntites.OURANWOOD_DEER.get(), context -> new com.craisinlord.antarchy.content.client.renderer.OuranwoodDeerRenderer(context)); event.registerEntityRenderer(AntarchyNeoforgeEntites.OURANWOOD_DEER.get(), context -> withParalyzedGeoLayer(new com.craisinlord.antarchy.content.client.renderer.OuranwoodDeerRenderer(context)));
event.registerEntityRenderer(AntarchyNeoforgeEntites.GLIMMER.get(), context -> withParalyzedGeoLayer(new com.craisinlord.antarchy.content.client.renderer.glimmer.GlimmerRenderer(context))); event.registerEntityRenderer(AntarchyNeoforgeEntites.GLIMMER.get(), context -> withParalyzedGeoLayer(new com.craisinlord.antarchy.content.client.renderer.glimmer.GlimmerRenderer(context)));
event.registerEntityRenderer(AntarchyNeoforgeEntites.ELKA.get(), context -> new com.craisinlord.antarchy.content.client.renderer.ElkaRenderer(context)); event.registerEntityRenderer(AntarchyNeoforgeEntites.ELKA.get(), context -> withParalyzedGeoLayer(new com.craisinlord.antarchy.content.client.renderer.ElkaRenderer(context)));
event.registerEntityRenderer(AntarchyNeoforgeEntites.ROLLY_POLLY.get(), context -> withParalyzedGeoLayer(new com.craisinlord.antarchy.content.client.renderer.RollyPollyRenderer(context))); event.registerEntityRenderer(AntarchyNeoforgeEntites.ROLLY_POLLY.get(), context -> withParalyzedGeoLayer(new com.craisinlord.antarchy.content.client.renderer.RollyPollyRenderer(context)));
event.registerEntityRenderer(AntarchyNeoforgeEntites.TERMITE.get(), context -> withParalyzedGeoLayer(new com.craisinlord.antarchy.content.client.renderer.TermiteRenderer(context))); event.registerEntityRenderer(AntarchyNeoforgeEntites.TERMITE.get(), context -> withParalyzedGeoLayer(new com.craisinlord.antarchy.content.client.renderer.TermiteRenderer(context)));
event.registerEntityRenderer(AntarchyNeoforgeEntites.CREEPING_HORROR.get(), context -> withParalyzedGeoLayer(new com.craisinlord.antarchy.content.client.renderer.CreepingHorrorRenderer(context))); event.registerEntityRenderer(AntarchyNeoforgeEntites.CREEPING_HORROR.get(), context -> withParalyzedGeoLayer(new com.craisinlord.antarchy.content.client.renderer.CreepingHorrorRenderer(context)));
@@ -196,6 +197,8 @@ public final class AntarchyNeoForgeClient {
renderer.addLayer(new TigerEyeCamouflagePlayerLayer(renderer)); renderer.addLayer(new TigerEyeCamouflagePlayerLayer(renderer));
renderer.addLayer(new GoopedLivingLayer(renderer)); renderer.addLayer(new GoopedLivingLayer(renderer));
renderer.addLayer(new GlimmeringLivingLayer(renderer)); renderer.addLayer(new GlimmeringLivingLayer(renderer));
renderer.addLayer(new TemporalContractionAfterimageLayer<>(renderer));
renderer.addLayer(new RoyalAssailantAxeAfterimageLayer(renderer));
renderer.addLayer(new BrutalflyElytraLayer(renderer)); renderer.addLayer(new BrutalflyElytraLayer(renderer));
renderer.addLayer(new com.craisinlord.antarchy.content.client.renderer.ManticoreWingsLayer(renderer)); renderer.addLayer(new com.craisinlord.antarchy.content.client.renderer.ManticoreWingsLayer(renderer));
renderer.addLayer(new FallenKingCrownLayer(renderer)); renderer.addLayer(new FallenKingCrownLayer(renderer));
@@ -235,11 +238,13 @@ public final class AntarchyNeoForgeClient {
((LivingEntityRenderer) livingRenderer).addLayer(new ParalyzedStoneLivingLayer<>(livingRenderer)); ((LivingEntityRenderer) livingRenderer).addLayer(new ParalyzedStoneLivingLayer<>(livingRenderer));
((LivingEntityRenderer) livingRenderer).addLayer(new GoopedLivingLayer<>(livingRenderer)); ((LivingEntityRenderer) livingRenderer).addLayer(new GoopedLivingLayer<>(livingRenderer));
((LivingEntityRenderer) livingRenderer).addLayer(new GlimmeringLivingLayer<>(livingRenderer)); ((LivingEntityRenderer) livingRenderer).addLayer(new GlimmeringLivingLayer<>(livingRenderer));
((LivingEntityRenderer) livingRenderer).addLayer(new TemporalContractionAfterimageLayer<>(livingRenderer));
} }
private static <T extends LivingEntity & GeoAnimatable> GeoEntityRenderer<T> withParalyzedGeoLayer(GeoEntityRenderer<T> renderer) { private static <T extends LivingEntity & GeoAnimatable> GeoEntityRenderer<T> withParalyzedGeoLayer(GeoEntityRenderer<T> renderer) {
renderer.addRenderLayer(new ParalyzedStoneGeoLayer<>(renderer)); renderer.addRenderLayer(new ParalyzedStoneGeoLayer<>(renderer));
renderer.addRenderLayer(new GoopedGeoLayer<>(renderer)); renderer.addRenderLayer(new GoopedGeoLayer<>(renderer));
renderer.addRenderLayer(new TemporalContractionGeoLayer<>(renderer));
return renderer; return renderer;
} }
@@ -1,5 +1,6 @@
package com.craisinlord.antarchy.neoforge.content.fluid; package com.craisinlord.antarchy.neoforge.content.fluid;
import com.craisinlord.antarchy.content.fluid.AntiwaterBubbleColumnUtil;
import com.craisinlord.antarchy.neoforge.registry.AntarchyNeoforgeBlocks; import com.craisinlord.antarchy.neoforge.registry.AntarchyNeoforgeBlocks;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
@@ -173,6 +174,8 @@ public abstract class AntiwaterFluid extends BaseFlowingFluid {
return; return;
} }
AntiwaterBubbleColumnUtil.update(level, pos, state);
if (!state.isSource() && !this.isFlowingStateValid(level, pos, state)) { if (!state.isSource() && !this.isFlowingStateValid(level, pos, state)) {
level.setBlockAndUpdate(pos, net.minecraft.world.level.block.Blocks.AIR.defaultBlockState()); level.setBlockAndUpdate(pos, net.minecraft.world.level.block.Blocks.AIR.defaultBlockState());
this.scheduleCleanup(level, pos); this.scheduleCleanup(level, pos);
@@ -393,6 +393,13 @@ public final class AntarchyNeoforgeEntites {
.updateInterval(2) .updateInterval(2)
.fireImmune() .fireImmune()
.build("royal_black_hole")); .build("royal_black_hole"));
public static final DeferredHolder<EntityType<?>, EntityType<com.craisinlord.antarchy.content.entity.royal.RoyalAssailantBlackHoleEntity>> ROYAL_ASSAILANT_BLACK_HOLE = ENTITY_TYPES.register("royal_assailant_black_hole",
() -> EntityType.Builder.<com.craisinlord.antarchy.content.entity.royal.RoyalAssailantBlackHoleEntity>of(com.craisinlord.antarchy.content.entity.royal.RoyalAssailantBlackHoleEntity::new, MobCategory.MISC)
.sized(1.0F, 1.0F)
.clientTrackingRange(16)
.updateInterval(2)
.fireImmune()
.build("royal_assailant_black_hole"));
public static final DeferredHolder<EntityType<?>, EntityType<TimeDilationFieldEntity>> TIME_DILATION_FIELD = ENTITY_TYPES.register("time_dilation_field", public static final DeferredHolder<EntityType<?>, EntityType<TimeDilationFieldEntity>> TIME_DILATION_FIELD = ENTITY_TYPES.register("time_dilation_field",
() -> EntityType.Builder.<TimeDilationFieldEntity>of(TimeDilationFieldEntity::new, MobCategory.MISC) () -> EntityType.Builder.<TimeDilationFieldEntity>of(TimeDilationFieldEntity::new, MobCategory.MISC)
.sized(0.5F, 0.5F) .sized(0.5F, 0.5F)
@@ -497,7 +504,7 @@ public final class AntarchyNeoforgeEntites {
.build("lurking_terror")); .build("lurking_terror"));
public static final DeferredHolder<EntityType<?>, EntityType<ManticoreEntity>> MANTICORE = ENTITY_TYPES.register("manticore", public static final DeferredHolder<EntityType<?>, EntityType<ManticoreEntity>> MANTICORE = ENTITY_TYPES.register("manticore",
() -> EntityType.Builder.of(ManticoreEntity::new, MobCategory.MONSTER) () -> EntityType.Builder.of(ManticoreEntity::new, MobCategory.MONSTER)
.sized(1.95F, 2.0F) .sized(1.95F, 2.25F)
.clientTrackingRange(10) .clientTrackingRange(10)
.fireImmune() .fireImmune()
.build("manticore")); .build("manticore"));
@@ -222,6 +222,9 @@ public final class AntarchyNeoforgeSounds {
public static final DeferredHolder<SoundEvent, SoundEvent> KING_BEAM_SHOOT = registerSoundEvent("king_beam_shoot"); public static final DeferredHolder<SoundEvent, SoundEvent> KING_BEAM_SHOOT = registerSoundEvent("king_beam_shoot");
public static final DeferredHolder<SoundEvent, SoundEvent> KING_FIREBALL_SHOOT = registerSoundEvent("king_fireball_shoot"); public static final DeferredHolder<SoundEvent, SoundEvent> KING_FIREBALL_SHOOT = registerSoundEvent("king_fireball_shoot");
public static final DeferredHolder<SoundEvent, SoundEvent> KING_DECREE_CAST = registerSoundEvent("king_decree_cast"); public static final DeferredHolder<SoundEvent, SoundEvent> KING_DECREE_CAST = registerSoundEvent("king_decree_cast");
public static final DeferredHolder<SoundEvent, SoundEvent> KING_DECREE = registerSoundEvent("king_decree");
public static final DeferredHolder<SoundEvent, SoundEvent> KING_SUCCESS = registerSoundEvent("king_success");
public static final DeferredHolder<SoundEvent, SoundEvent> KING_JUDGEMENT = registerSoundEvent("king_judgement");
public static final DeferredHolder<SoundEvent, SoundEvent> QUEEN_IDLE = registerSoundEvent("queen_idle"); public static final DeferredHolder<SoundEvent, SoundEvent> QUEEN_IDLE = registerSoundEvent("queen_idle");
public static final DeferredHolder<SoundEvent, SoundEvent> QUEEN_WALK = registerSoundEvent("queen_walk"); public static final DeferredHolder<SoundEvent, SoundEvent> QUEEN_WALK = registerSoundEvent("queen_walk");