From f4f4e0c7172ced4f880765519fa4701120fbd822 Mon Sep 17 00:00:00 2001 From: craisinlord Date: Wed, 9 Sep 2026 22:55:40 -0500 Subject: [PATCH] new king sounds worm sizes performance improvements --- .../antarchy/config/AntarchySettings.java | 27 +- .../entity/portal/DimensionalTearEntity.java | 11 + .../content/item/BloodCrystalKatanaItem.java | 19 + .../worldgen/elythia/ElythiaBiomeSource.java | 8 + .../thoraxis/ThoraxisBiomeSource.java | 6 + .../antarchy/config/AntarchySettings.java | 17 +- .../antarchy/content/AntarchyObjects.java | 6 + .../antarchy/content/AntarchySoundEvents.java | 11 +- .../antarchy/content/AntarchyTags.java | 1 + .../antarchy/content/block/RoyalEggBlock.java | 2 +- .../client/ClientTimeDilationTicker.java | 20 + .../client/model/RoyalBlackHoleModel.java | 8 +- .../content/client/model/RoyalBossModel.java | 46 +- .../model/RoyalElementalProjectileModel.java | 3 + .../content/client/model/WormModel.java | 30 +- .../renderer/RoyalBlackHoleRenderer.java | 4 + .../client/renderer/RoyalBossRenderer.java | 101 +++- .../RoyalElementalProjectileRenderer.java | 16 +- .../client/renderer/RoyalMountRenderer.java | 75 ++- .../content/effect/InvertedMobEffect.java | 16 - .../content/entity/ManticoreEntity.java | 2 +- .../content/entity/ScorpionEntity.java | 10 + .../antarchy/content/entity/WormEntity.java | 119 +++++ .../entity/portal/DimensionalTearEntity.java | 13 +- .../content/entity/royal/KingEntity.java | 22 +- .../content/entity/royal/PrinceEntity.java | 9 + .../content/entity/royal/QueenEntity.java | 185 +++++-- .../entity/royal/RoyalBlackHoleEntity.java | 290 ++++++++-- .../content/entity/royal/RoyalBoltEntity.java | 2 +- .../content/entity/royal/RoyalBossEntity.java | 497 ++++++++++++------ .../royal/RoyalElementalProjectileEntity.java | 59 ++- .../entity/royal/RoyalIceSpikeEntity.java | 1 - .../entity/royal/RoyalMountEntity.java | 228 +++++++- .../royal/RoyalMountFollowOwnerGoal.java | 28 +- .../entity/royal/RoyalMountSpitGoal.java | 2 +- .../royal/attack/RoyalAttackScheduler.java | 1 - .../royal/attack/RoyalEffectController.java | 1 - .../royal/beam/RoyalBeamController.java | 9 +- .../entity/royal/beam/RoyalBeamElement.java | 6 +- .../entity/royal/decree/AdvanceDecree.java | 2 +- .../royal/decree/ComeNoCloserDecree.java | 2 +- .../royal/decree/FightMeCowardDecree.java | 4 +- .../royal/decree/KeepYourDistanceDecree.java | 2 +- .../entity/royal/decree/NoRetreatDecree.java | 2 +- .../item/AntimetalScaffoldingItem.java | 11 - .../content/item/EyeOfTheStormItem.java | 17 +- .../antarchy/content/item/MucusBlockItem.java | 12 - .../content/item/RoyalAssailantArmorItem.java | 11 - .../item/RoyalAssailantBattleAxeItem.java | 4 +- .../content/item/WormHookTetherManager.java | 14 - .../portal/PermanentPortalTeleporter.java | 24 +- .../content/time/TimeDilationApi.java | 1 - .../content/time/TimeDilationFieldEntity.java | 40 ++ .../time/TimeDilationFieldSampler.java | 2 +- .../content/time/TimeDilationManager.java | 75 ++- .../content/time/TimeDilationMath.java | 1 - .../elythia/OuranwoodTreeFeature.java | 148 +++++- .../thoraxis/LucidAntiwaterPoolFeature.java | 33 +- .../NaturalDirectionalVineFeature.java | 1 - .../thoraxis/ThoraxisIchorLakeFeature.java | 4 - .../thoraxis/ThoraxisUndersideManager.java | 13 + .../gravity/AbstractArrowGravityMixin.java | 37 +- .../gravity/BehaviorUtilsGravityMixin.java | 4 +- .../gravity/BucketItemGravityMixin.java | 4 - .../gravity/CrossbowItemGravityMixin.java | 6 +- .../ThrowableProjectileGravityMixin.java | 28 +- .../EntityDeltaMovementTimeDilationMixin.java | 2 +- ...ityRendererAnimationTimeDilationMixin.java | 7 + .../time/LivingEntityTimeDilationMixin.java | 6 + .../resources/assets/antarchy/lang/en_us.json | 5 +- .../resources/assets/antarchy/sounds.json | 24 + .../entity/queen/queen_beam_end_1.png | Bin 1907 -> 0 bytes .../entity/queen/queen_beam_end_2.png | Bin 1843 -> 0 bytes .../entity/queen/queen_beam_inner.png | Bin 314 -> 0 bytes .../entity/queen/queen_beam_outer.png | Bin 393 -> 0 bytes .../textures/mob_effect/contracted.png | Bin 0 -> 227 bytes .../antarchy/loot_table/entities/worm.json | 9 + .../antarchy/tags/item/royal_mount_food.json | 15 +- .../template_pool/kings_tree/kings_tree.json | 1 + .../template_pool/kings_tree/kings_tree1.json | 1 + .../template_pool/kings_tree/kings_tree3.json | 1 + .../template_pool/kings_tree/kings_tree4.json | 1 + .../kings_tree/kings_tree_bottom.json | 5 +- .../fabric/AntarchyFabricContent.java | 6 +- .../client/AntarchyFabricClientBootstrap.java | 1 + .../fabric/content/fluid/AntiwaterFluid.java | 3 + .../registry/AntarchyFabricEntities.java | 7 + .../fabric/registry/AntarchyFabricSounds.java | 3 + .../forge/registry/AntarchyForgeMisc.java | 4 +- .../AntarchyConfigModuleNeoforge.java | 5 + .../antarchy/neoforge/AntarchyMobsConfig.java | 20 +- .../antarchy/neoforge/AntarchyNeoforge.java | 6 +- .../client/AntarchyNeoForgeClient.java | 11 +- .../content/fluid/AntiwaterFluid.java | 3 + .../registry/AntarchyNeoforgeEntites.java | 9 +- .../registry/AntarchyNeoforgeSounds.java | 3 + 96 files changed, 2070 insertions(+), 501 deletions(-) delete mode 100644 common-1.21.1/src/main/resources/assets/antarchy/textures/entity/queen/queen_beam_end_1.png delete mode 100644 common-1.21.1/src/main/resources/assets/antarchy/textures/entity/queen/queen_beam_end_2.png delete mode 100644 common-1.21.1/src/main/resources/assets/antarchy/textures/entity/queen/queen_beam_inner.png delete mode 100644 common-1.21.1/src/main/resources/assets/antarchy/textures/entity/queen/queen_beam_outer.png create mode 100644 common-1.21.1/src/main/resources/assets/antarchy/textures/mob_effect/contracted.png diff --git a/common-1.20.1/src/main/java/com/craisinlord/antarchy/config/AntarchySettings.java b/common-1.20.1/src/main/java/com/craisinlord/antarchy/config/AntarchySettings.java index f3723f71..d4449786 100644 --- a/common-1.20.1/src/main/java/com/craisinlord/antarchy/config/AntarchySettings.java +++ b/common-1.20.1/src/main/java/com/craisinlord/antarchy/config/AntarchySettings.java @@ -468,6 +468,9 @@ public final class AntarchySettings { private static double molevoreBreakRange = 1.8D; private static double molevoreBreakVerticalRange = 2.2D; 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 nightmareAttackDamage = 16.0D; private static double nightmareMovementSpeed = 0.28D; @@ -2715,6 +2718,21 @@ public final class AntarchySettings { public static double molevoreAttackDamage() { return molevoreAttackDamage; } 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 void setMolevoreSpinTicks(int value) { molevoreSpinTicks = value; } @@ -2985,7 +3003,10 @@ public final class AntarchySettings { public static int bossMagicWardDurationTicks() { return bossMagicWardDurationTicks; } public static int bossMagicWardTriggerBreaches() { return bossMagicWardTriggerBreaches; } public static java.util.List magicBurstDamageNamespaces() { return magicBurstDamageNamespaces; } - public static double jumpyBugLatchDamage() { return 2.0D; } - public static double waterCannonCooldownSeconds() { return 4.0D; } - public static double toreterrorRangedWaterBombChance() { return 0.35D; } + public static double jumpyBugLatchDamage() { return jumpyBugLatchDamage; } + public static void setJumpyBugLatchDamage(double value) { jumpyBugLatchDamage = value; } + 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; } } diff --git a/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/entity/portal/DimensionalTearEntity.java b/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/entity/portal/DimensionalTearEntity.java index 38972539..78c6179b 100644 --- a/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/entity/portal/DimensionalTearEntity.java +++ b/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/entity/portal/DimensionalTearEntity.java @@ -60,6 +60,7 @@ public class DimensionalTearEntity extends Entity implements GeoEntity { private static final double PORTAL_RADIUS = 1.35D; private static final double EXIT_OFFSET = 2.4D; private static final Map TELEPORT_COOLDOWNS = new HashMap<>(); + private static long lastTeleportCooldownCleanupTick = Long.MIN_VALUE; private final AnimatableInstanceCache geoCache = GeckoLibUtil.createInstanceCache(this); private UUID linkedTearId; @@ -118,6 +119,7 @@ public class DimensionalTearEntity extends Entity implements GeoEntity { } this.ageTicks++; + cleanupTeleportCooldowns(this.level().getGameTime()); if (this.ageTicks >= this.lifetimeTicks - COLLAPSE_WARNING_TICKS) { 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); } + 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() { Vec3 facing = this.getViewVector(1.0F); if (facing.lengthSqr() < 1.0E-4D) { diff --git a/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/item/BloodCrystalKatanaItem.java b/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/item/BloodCrystalKatanaItem.java index 0c017a98..5db4bcde 100644 --- a/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/item/BloodCrystalKatanaItem.java +++ b/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/item/BloodCrystalKatanaItem.java @@ -1,11 +1,15 @@ package com.craisinlord.antarchy.content.item; 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.ChatFormatting; import net.minecraft.network.chat.Component; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.Entity; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.SwordItem; @@ -21,6 +25,7 @@ public class BloodCrystalKatanaItem extends SwordItem { } private static TrailCallback trailCallback = (player, durationTicks) -> {}; + private static final Map DASH_UNTIL_TICK = new ConcurrentHashMap<>(); private final Tier tier; private final int attackDamage; @@ -56,9 +61,23 @@ public class BloodCrystalKatanaItem extends SwordItem { player.hasImpulse = true; player.hurtMarked = true; trailCallback.trigger(player, AntarchySettings.bloodCrystalKatanaTrailDurationTicks()); + long now = player.level().getGameTime(); + DASH_UNTIL_TICK.put(player.getUUID(), now + AntarchySettings.bloodCrystalKatanaTrailDurationTicks()); 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 public void appendHoverText(ItemStack stack, Level level, List tooltipComponents, TooltipFlag tooltipFlag) { tooltipComponents.add(Component.translatable("tooltip.antarchy.blood_crystal_katana.launch").withStyle(ChatFormatting.RED)); diff --git a/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/worldgen/elythia/ElythiaBiomeSource.java b/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/worldgen/elythia/ElythiaBiomeSource.java index 2a316092..ae71430b 100644 --- a/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/worldgen/elythia/ElythiaBiomeSource.java +++ b/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/worldgen/elythia/ElythiaBiomeSource.java @@ -16,6 +16,14 @@ import net.minecraft.world.level.biome.Climate; import net.minecraft.world.level.biome.MultiNoiseBiomeSource; public class ElythiaBiomeSource extends BiomeSource { + public static final Codec 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 MOLEWORM_CAVES = ResourceKey.create( Registries.BIOME, new ResourceLocation(Antarchy.MODID, "moleworm_caves") diff --git a/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/worldgen/thoraxis/ThoraxisBiomeSource.java b/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/worldgen/thoraxis/ThoraxisBiomeSource.java index a81cb6f5..aea535b8 100644 --- a/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/worldgen/thoraxis/ThoraxisBiomeSource.java +++ b/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/worldgen/thoraxis/ThoraxisBiomeSource.java @@ -17,6 +17,12 @@ import net.minecraft.world.level.biome.Climate; import net.minecraft.world.level.biome.MultiNoiseBiomeSource; public final class ThoraxisBiomeSource extends BiomeSource { + public static final Codec 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_DETAIL_SCALE = 41; private static final int VERTICAL_NOISE_SCALE = 8; diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/config/AntarchySettings.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/config/AntarchySettings.java index d5453375..e829813f 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/config/AntarchySettings.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/config/AntarchySettings.java @@ -254,6 +254,7 @@ public final class AntarchySettings { private static double queenBlackHoleRadius = 14.0D; private static int queenBlackHoleActiveTicks = 120; private static double queenBlackHolePullStrength = 0.16D; + private static int queenBlackHoleBlockSuctionCap = 160; private static double queenFinalTimeFieldRadius = 20.0D; private static double queenFinalTimeFieldRate = 0.05D; private static int queenFinalTimeFieldDurationTicks = 45; @@ -273,7 +274,7 @@ public final class AntarchySettings { private static double royalBossKnockbackResistance = 1.0D; private static double royalBossStepHeight = 3.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 int royalBossBiteCooldownTicks = 45; private static boolean basiliskPetrifyingGazeEnabled = true; @@ -558,6 +559,10 @@ public final class AntarchySettings { private static double queenBeamTracking = 0.1D; private static double queenBeamTerrainRadius = 5.0D; 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 kingBeamRange = 100.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 double queenBlackHolePullStrength() { return queenBlackHolePullStrength; } 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 void setQueenFinalTimeFieldRadius(double v) { queenFinalTimeFieldRadius = v; } public static double queenFinalTimeFieldRate() { return queenFinalTimeFieldRate; } @@ -2910,6 +2917,14 @@ public final class AntarchySettings { public static void setQueenBeamTerrainRadius(double v) { queenBeamTerrainRadius = v; } public static int queenBeamTerrainCap() { return queenBeamTerrainCap; } 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 void setKingBeamDamage(double v) { kingBeamDamage = v; } public static double kingBeamRange() { return kingBeamRange; } diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/AntarchyObjects.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/AntarchyObjects.java index a0d95a26..4e87790d 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/AntarchyObjects.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/AntarchyObjects.java @@ -101,6 +101,8 @@ public final class AntarchyObjects { @SuppressWarnings("unchecked") public static Supplier> ROYAL_BLACK_HOLE = (Supplier>) UNBOUND; @SuppressWarnings("unchecked") + public static Supplier> ROYAL_ASSAILANT_BLACK_HOLE = (Supplier>) UNBOUND; + @SuppressWarnings("unchecked") public static Supplier VORTEX_LENS = (Supplier) UNBOUND; @SuppressWarnings("unchecked") public static Supplier> VORTEX_LENS_BLOCK_ENTITY = (Supplier>) UNBOUND; @@ -691,6 +693,10 @@ public final class AntarchyObjects { ROYAL_BLACK_HOLE = supplier; } + public static void setRoyalAssailantBlackHole(Supplier> supplier) { + ROYAL_ASSAILANT_BLACK_HOLE = supplier; + } + public static void setVortexLens(Supplier supplier) { VORTEX_LENS = supplier; } diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/AntarchySoundEvents.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/AntarchySoundEvents.java index 4849b8e9..79d49a64 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/AntarchySoundEvents.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/AntarchySoundEvents.java @@ -213,6 +213,9 @@ public final class AntarchySoundEvents { public static Supplier PRINCESS_SHOOT = (Supplier) UNBOUND; public static Supplier PRINCESS_FLY = (Supplier) UNBOUND; public static Supplier PRINCESS_STEP = (Supplier) UNBOUND; + public static Supplier KING_DECREE = (Supplier) UNBOUND; + public static Supplier KING_SUCCESS = (Supplier) UNBOUND; + public static Supplier KING_JUDGEMENT = (Supplier) UNBOUND; private AntarchySoundEvents() { } @@ -417,7 +420,10 @@ public final class AntarchySoundEvents { Supplier princessBite, Supplier princessShoot, Supplier princessFly, - Supplier princessStep + Supplier princessStep, + Supplier kingDecree, + Supplier kingSuccess, + Supplier kingJudgement ) { SQUIDZOOKA_FIRE = squidzookaFire; SHRINK_RAY_CHARGE = shrinkRayCharge; @@ -619,6 +625,9 @@ public final class AntarchySoundEvents { PRINCESS_SHOOT = princessShoot; PRINCESS_FLY = princessFly; PRINCESS_STEP = princessStep; + KING_DECREE = kingDecree; + KING_SUCCESS = kingSuccess; + KING_JUDGEMENT = kingJudgement; } public static void bindDimensionalTearSounds( diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/AntarchyTags.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/AntarchyTags.java index d8f19501..f72bf3c1 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/AntarchyTags.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/AntarchyTags.java @@ -85,6 +85,7 @@ public final class AntarchyTags { public static final TagKey> DIMENSIONAL_TEAR_COMMON_SPAWNS = TagKey.create(Registries.ENTITY_TYPE, id("dimensional_tear_common_spawns")); public static final TagKey> DIMENSIONAL_TEAR_RARE_SPAWNS = TagKey.create(Registries.ENTITY_TYPE, id("dimensional_tear_rare_spawns")); public static final TagKey> TIME_DILATION_IMMUNE = TagKey.create(Registries.ENTITY_TYPE, id("time_dilation_immune")); + public static final TagKey> QUEEN_DOES_NOT_ATTACK = TagKey.create(Registries.ENTITY_TYPE, id("queen_does_not_attack")); public static final TagKey> COMMANDED_BLACKLIST = TagKey.create(Registries.ENTITY_TYPE, id("commanded_blacklist")); public static final TagKey> DILATED_BLACKLIST = TagKey.create(Registries.ENTITY_TYPE, id("dilated_blacklist")); diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/block/RoyalEggBlock.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/block/RoyalEggBlock.java index d9f38170..05e78ab6 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/block/RoyalEggBlock.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/block/RoyalEggBlock.java @@ -170,7 +170,7 @@ public abstract class RoyalEggBlock extends BaseEntityBlock { return; } 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); if (placer != null) { mount.setTame(true, true); diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/ClientTimeDilationTicker.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/ClientTimeDilationTicker.java index ae2c4a96..25d91b6c 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/ClientTimeDilationTicker.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/ClientTimeDilationTicker.java @@ -17,6 +17,7 @@ public final class ClientTimeDilationTicker { private static ClientLevel activeLevel; private static final Map ITEM_SPIN_STATES = new WeakHashMap<>(); private static final Map ATTACK_ANIMATION_STATES = new WeakHashMap<>(); + private static final Map WALK_ANIMATION_STATES = new WeakHashMap<>(); private static final Map ANIMATION_CLOCKS = new WeakHashMap<>(); private ClientTimeDilationTicker() { @@ -28,9 +29,12 @@ public final class ClientTimeDilationTicker { activeFields = List.of(); ITEM_SPIN_STATES.clear(); ATTACK_ANIMATION_STATES.clear(); + WALK_ANIMATION_STATES.clear(); ANIMATION_CLOCKS.clear(); + ContractionAfterimages.clear(); com.craisinlord.antarchy.content.time.TimeDilationApi.clearSyncedClientRates(); } + ContractionAfterimages.tick(level); } 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; } + 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) { double rate = com.craisinlord.antarchy.content.time.TimeDilationApi.getRate(item); if (rate >= TimeDilationMath.NORMAL_RATE) { diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/model/RoyalBlackHoleModel.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/model/RoyalBlackHoleModel.java index a0d8d665..f010f3b8 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/model/RoyalBlackHoleModel.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/model/RoyalBlackHoleModel.java @@ -5,19 +5,19 @@ import com.craisinlord.antarchy.content.entity.royal.RoyalBlackHoleEntity; import net.minecraft.resources.ResourceLocation; import software.bernie.geckolib.model.GeoModel; -public class RoyalBlackHoleModel extends GeoModel { +public class RoyalBlackHoleModel extends GeoModel { @Override - public ResourceLocation getModelResource(RoyalBlackHoleEntity animatable) { + public ResourceLocation getModelResource(T animatable) { return ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "geo/black_hole.geo.json"); } @Override - public ResourceLocation getTextureResource(RoyalBlackHoleEntity animatable) { + public ResourceLocation getTextureResource(T animatable) { return ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "textures/entity/queen/black_hole.png"); } @Override - public ResourceLocation getAnimationResource(RoyalBlackHoleEntity animatable) { + public ResourceLocation getAnimationResource(T animatable) { return ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "animations/black_hole.animation.json"); } } diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/model/RoyalBossModel.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/model/RoyalBossModel.java index 57df6dc7..36748ae1 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/model/RoyalBossModel.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/model/RoyalBossModel.java @@ -45,34 +45,30 @@ public class RoyalBossModel extends GeoModel { 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(); - Vec3 origin = animatable.getRoyalBeamShootFrom(partialTick); - Vec3 direction = beamEnd.subtract(origin); - double horizontal = Math.sqrt(direction.x * direction.x + direction.z * direction.z); - if (direction.lengthSqr() < 1.0E-6D || horizontal < 1.0E-4D) { - return; - } + 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); + double horizontal = Math.sqrt(direction.x * direction.x + direction.z * direction.z); + if (direction.lengthSqr() < 1.0E-6D || horizontal < 1.0E-4D) continue; - float desiredYaw = (float) (Mth.atan2(-direction.x, direction.z) * Mth.RAD_TO_DEG); - float bodyYaw = Mth.rotLerp(partialTick, animatable.yRotO, animatable.getYRot()); - float relativeYaw = Mth.clamp(Mth.wrapDegrees(desiredYaw - bodyYaw), -55.0F, 55.0F) * Mth.DEG_TO_RAD; - float pitch = Mth.clamp((float) -Mth.atan2(direction.y, horizontal) * Mth.RAD_TO_DEG, - -45.0F, 45.0F) * Mth.DEG_TO_RAD; + float desiredYaw = (float) (Mth.atan2(-direction.x, direction.z) * Mth.RAD_TO_DEG); + float bodyYaw = Mth.rotLerp(partialTick, animatable.yRotO, animatable.getYRot()); + float relativeYaw = Mth.clamp(Mth.wrapDegrees(desiredYaw - bodyYaw), -55.0F, 55.0F) * Mth.DEG_TO_RAD; + float pitch = Mth.clamp((float) -Mth.atan2(direction.y, horizontal) * Mth.RAD_TO_DEG, + -45.0F, 45.0F) * Mth.DEG_TO_RAD; - String[] chain = AIM_CHAINS[slot.ordinal()]; - float yawPerBone = relativeYaw / chain.length; - float pitchPerBone = pitch / chain.length; - for (String boneName : chain) { - this.getBone(boneName).ifPresent(bone -> { - bone.setRotY(bone.getRotY() + yawPerBone); - bone.setRotX(bone.getRotX() + pitchPerBone); - }); + String[] chain = AIM_CHAINS[slot.ordinal()]; + float yawPerBone = relativeYaw / chain.length; + float pitchPerBone = pitch / chain.length; + for (String boneName : chain) { + this.getBone(boneName).ifPresent(bone -> { + bone.setRotY(bone.getRotY() + yawPerBone); + bone.setRotX(bone.getRotX() + pitchPerBone); + }); + } } } diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/model/RoyalElementalProjectileModel.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/model/RoyalElementalProjectileModel.java index 04785e5d..fba18fa8 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/model/RoyalElementalProjectileModel.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/model/RoyalElementalProjectileModel.java @@ -14,6 +14,9 @@ public class RoyalElementalProjectileModel extends GeoModel { - private static final ResourceLocation MODEL = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "geo/worm_large.geo.json"); - private static final ResourceLocation TEXTURE = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "textures/entity/worm/worm_large.png"); - private static final ResourceLocation ANIMATION = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "animations/worm_large.animation.json"); + private static final ResourceLocation SMALL_MODEL = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "geo/worm_small.geo.json"); + private static final ResourceLocation MEDIUM_MODEL = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "geo/worm_medium.geo.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 public ResourceLocation getModelResource(WormEntity animatable) { - return MODEL; + return switch (animatable.getGrowthStage()) { + case SMALL -> SMALL_MODEL; + case MEDIUM -> MEDIUM_MODEL; + case LARGE -> LARGE_MODEL; + }; } @Override public ResourceLocation getTextureResource(WormEntity animatable) { - return TEXTURE; + return switch (animatable.getGrowthStage()) { + case SMALL -> SMALL_TEXTURE; + case MEDIUM -> MEDIUM_TEXTURE; + case LARGE -> LARGE_TEXTURE; + }; } @Override public ResourceLocation getAnimationResource(WormEntity animatable) { - return ANIMATION; + return switch (animatable.getGrowthStage()) { + case SMALL -> SMALL_ANIMATION; + case MEDIUM -> MEDIUM_ANIMATION; + case LARGE -> LARGE_ANIMATION; + }; } } diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/RoyalBlackHoleRenderer.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/RoyalBlackHoleRenderer.java index bd25a3ed..367638bc 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/RoyalBlackHoleRenderer.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/RoyalBlackHoleRenderer.java @@ -19,7 +19,11 @@ public class RoyalBlackHoleRenderer extends GeoEntityRenderer { - private static final ResourceLocation QUEEN_BEAM_OUTER = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/queen/queen_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_BEAM_END = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/queen/queen_beam_end_1.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_PURPLE_BEAM_INNER = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/queen/queen_purple_beam_inner.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_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"); @@ -34,6 +43,45 @@ public class RoyalBossRenderer extends GeoEntityRenderer { public RoyalBossRenderer(EntityRendererProvider.Context context) { super(context, new RoyalBossModel()); this.shadowRadius = 8.0F; + this.addRenderLayer(new RoyalAfterimageLayer(this)); + this.addRenderLayer(new TemporalContractionGeoLayer<>(this)); + } + + private static final class RoyalAfterimageLayer extends GeoRenderLayer { + 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 @@ -68,18 +116,22 @@ public class RoyalBossRenderer extends GeoEntityRenderer { public void render(RoyalBossEntity entity, float entityYaw, float partialTick, PoseStack poseStack, MultiBufferSource bufferSource, int packedLight) { super.render(entity, entityYaw, partialTick, poseStack, bufferSource, packedLight); - Vec3 end = entity.getRoyalBeamEndPosition(); - if (!entity.isFiringRoyalBeam() || end == null) { - return; + for (RoyalHead.Slot slot : RoyalHead.Slot.values()) { + if (entity.isFiringRoyalBeam(slot) && entity.getRoyalBeamEndPosition(slot) != null) { + 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 y = Mth.lerp(partialTick, entity.yo, entity.getY()); double z = Mth.lerp(partialTick, entity.zo, entity.getZ()); - RoyalHead.Slot beamHead = entity.getRoyalBeamHeadSlot(); - Vec3 trackedAnchor = beamHead == null - ? null - : ((RoyalBossModel) this.getGeoModel()).getTrackedBeamAnchor(beamHead); - Vec3 start = (trackedAnchor != null ? trackedAnchor : entity.getRoyalBeamShootFrom(partialTick)) + Vec3 trackedAnchor = ((RoyalBossModel) this.getGeoModel()).getTrackedBeamAnchor(beamHead); + Vec3 start = (trackedAnchor != null ? trackedAnchor : entity.getRoyalBeamShootFrom(beamHead, partialTick)) .subtract(x, y, z); Vec3 finish = end.subtract(x, y, z); Vec3 axis = finish.subtract(start); @@ -95,9 +147,9 @@ public class RoyalBossRenderer extends GeoEntityRenderer { side1 = side1.normalize(); Vec3 side2 = dir.cross(side1).normalize(); - ResourceLocation outer = QUEEN_BEAM_OUTER; - ResourceLocation inner = QUEEN_BEAM_INNER; - ResourceLocation endTexture = QUEEN_BEAM_END; + ResourceLocation outer = QUEEN_PURPLE_BEAM_OUTER; + ResourceLocation inner = QUEEN_PURPLE_BEAM_INNER; + ResourceLocation endTexture = QUEEN_PURPLE_BEAM_END; if (entity instanceof KingEntity) { switch (entity.getRoyalBeamElement()) { case FIRE -> { @@ -118,6 +170,21 @@ public class RoyalBossRenderer extends GeoEntityRenderer { 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; @@ -125,19 +192,19 @@ public class RoyalBossRenderer extends GeoEntityRenderer { float scroll = -time * BEAM_SCROLL_SPEED; 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, side2.scale(BEAM_OUTER_RADIUS), scroll, scroll + tiles); 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, side2.scale(BEAM_INNER_RADIUS), innerScroll, innerScroll + tiles); float impactSize = 1.1F + 0.35F * Mth.sin(time * 0.6F); Vec3 camLeft = new Vec3(this.entityRenderDispatcher.camera.getLeftVector()); 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)); } diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/RoyalElementalProjectileRenderer.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/RoyalElementalProjectileRenderer.java index 52056457..c66d6987 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/RoyalElementalProjectileRenderer.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/RoyalElementalProjectileRenderer.java @@ -6,8 +6,11 @@ import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.VertexConsumer; import com.mojang.math.Axis; import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.entity.EntityRendererProvider; +import net.minecraft.resources.ResourceLocation; import net.minecraft.util.Mth; +import org.jetbrains.annotations.Nullable; import software.bernie.geckolib.cache.object.BakedGeoModel; import software.bernie.geckolib.renderer.GeoEntityRenderer; @@ -17,13 +20,21 @@ public class RoyalElementalProjectileRenderer extends GeoEntityRenderer { + 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) { super(context, new RoyalMountModel()); this.shadowRadius = 1.1F; + this.addRenderLayer(new TemporalContractionGeoLayer<>(this)); } @Override @@ -27,8 +44,64 @@ public class RoyalMountRenderer extends GeoEntityRenderer { 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) { 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); 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); + } } diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/effect/InvertedMobEffect.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/effect/InvertedMobEffect.java index b0e5da9f..b3bb5f15 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/effect/InvertedMobEffect.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/effect/InvertedMobEffect.java @@ -1,14 +1,11 @@ package com.craisinlord.antarchy.content.effect; -import com.craisinlord.antarchy.Antarchy; import com.craisinlord.antarchy.content.gravity.AntarchyGravityApi; import com.craisinlord.antarchy.content.gravity.AntarchyGravityDirection; import com.craisinlord.antarchy.content.gravity.AntarchyGravityTransition; -import net.minecraft.world.effect.MobEffectInstance; import net.minecraft.world.effect.MobEffect; import net.minecraft.world.effect.MobEffectCategory; import net.minecraft.world.entity.LivingEntity; -import net.minecraft.world.entity.player.Player; public final class InvertedMobEffect extends MobEffect { 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) { if (!entity.level().isClientSide()) { 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); } } else { diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/ManticoreEntity.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/ManticoreEntity.java index 0fcdb4a4..32d4f6c8 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/ManticoreEntity.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/ManticoreEntity.java @@ -101,7 +101,7 @@ public class ManticoreEntity extends Monster implements GeoEntity { return Mob.createMobAttributes() .add(Attributes.MAX_HEALTH, AntarchySettings.manticoreHealth()) .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.FOLLOW_RANGE, 28.0D) .add(Attributes.KNOCKBACK_RESISTANCE, 0.2D); diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/ScorpionEntity.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/ScorpionEntity.java index 4d42ccc7..fc0b1ce7 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/ScorpionEntity.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/ScorpionEntity.java @@ -351,6 +351,16 @@ public class ScorpionEntity extends Monster implements GeoEntity { ScorpionEntity.this.getNavigation().stop(); 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(); } diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/WormEntity.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/WormEntity.java index eaf0c76f..85b900c6 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/WormEntity.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/WormEntity.java @@ -37,7 +37,12 @@ import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.MultifaceBlock; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.core.BlockPos; +import net.minecraft.nbt.CompoundTag; 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.instance.AnimatableInstanceCache; import software.bernie.geckolib.animation.AnimatableManager; @@ -48,6 +53,42 @@ import software.bernie.geckolib.animation.RawAnimation; import software.bernie.geckolib.util.GeckoLibUtil; 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 WORMY_CAVES = ResourceKey.create( Registries.BIOME, 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 double MUCUS_TRAIL_MIN_DISTANCE_SQR = 0.36D; private static final EntityDataAccessor ANIMATION_STATE = SynchedEntityData.defineId(WormEntity.class, EntityDataSerializers.INT); + private static final EntityDataAccessor GROWTH_STAGE = SynchedEntityData.defineId(WormEntity.class, EntityDataSerializers.INT); private static final int ANIM_INGROUND = 0; private static final int ANIM_CRAWL = 1; private static final int ANIM_ATTACK = 2; @@ -67,6 +109,7 @@ public class WormEntity extends Monster implements GeoEntity { private int attackAnimationTicks; private int mucusTrailCooldown; private Vec3 lastMucusTrailPosition; + private int growthAge; public WormEntity(EntityType entityType, Level level) { super(entityType, level); @@ -103,6 +146,67 @@ public class WormEntity extends Monster implements GeoEntity { protected void defineSynchedData(SynchedEntityData.Builder builder) { super.defineSynchedData(builder); 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 @@ -153,6 +257,7 @@ public class WormEntity extends Monster implements GeoEntity { if (this.level().isClientSide) { return; } + this.tickGrowth(); this.tickMucusTrail(); if (this.attackAnimationTicks > 0) { this.attackAnimationTicks--; @@ -160,7 +265,21 @@ public class WormEntity extends Monster implements GeoEntity { 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() { + if (this.getGrowthStage() == GrowthStage.SMALL) return; if (this.mucusTrailCooldown > 0) { this.mucusTrailCooldown--; } diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/portal/DimensionalTearEntity.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/portal/DimensionalTearEntity.java index 2e06f84e..d3b57614 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/portal/DimensionalTearEntity.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/portal/DimensionalTearEntity.java @@ -74,6 +74,7 @@ public class DimensionalTearEntity extends Entity implements GeoEntity { private static final double PORTAL_RADIUS = 1.35D; private static final double EXIT_OFFSET = 2.4D; private static final Map TELEPORT_COOLDOWNS = new HashMap<>(); + private static long lastTeleportCooldownCleanupTick = Long.MIN_VALUE; private static final Map> QUEEN_TEAR_IDS = new HashMap<>(); private final AnimatableInstanceCache geoCache = GeckoLibUtil.createInstanceCache(this); @@ -149,6 +150,7 @@ public class DimensionalTearEntity extends Entity implements GeoEntity { } this.ageTicks++; + cleanupTeleportCooldowns(this.level().getGameTime()); if (this.ageTicks == 1) { 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; if (entity instanceof LivingEntity living) { int invertedDuration = AntarchySettings.dimensionalTearInvertedDurationTicks(); - Antarchy.LOGGER.info( + Antarchy.LOGGER.debug( "[antarchy-dimensional-tear] applying inverted effect targetType={} uuid={} fromTear={} toTear={} startPos=({}, {}, {}) exitPos=({}, {}, {}) dim={} durationTicks={}", living.getType(), living.getUUID(), this.getUUID(), destination.getUUID(), 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); } + 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() { Vec3 facing = this.getViewVector(1.0F); if (facing.lengthSqr() < 1.0E-4D) { diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/KingEntity.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/KingEntity.java index 4668b667..de5059a5 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/KingEntity.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/KingEntity.java @@ -332,9 +332,12 @@ public class KingEntity extends RoyalBossEntity { return; } this.tickRoyalPunishment(); - if (this.iceBuildup.size() > 16) { - this.iceBuildup.clear(); - } + long gameTime = this.level().getGameTime(); + 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(); if (target != null && !this.isDeadOrDying() && this.level() instanceof ServerLevel level) { this.trackBehavior(target); @@ -346,6 +349,7 @@ public class KingEntity extends RoyalBossEntity { if (this.isDeadOrDying() || !(this.level() instanceof ServerLevel)) { return; } + this.setRoyalFlying(true); this.decreeRetreatPressure = false; if (this.patrolCenter == null || this.position().distanceToSqr(this.patrolCenter) > 64.0D * 64.0D) { this.patrolCenter = this.position(); @@ -397,7 +401,7 @@ public class KingEntity extends RoyalBossEntity { away = away.normalize(); this.exileTarget.setDeltaMovement(away.x * 2.6D, 1.35D, away.z * 2.6D); 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); } } @@ -555,6 +559,7 @@ public class KingEntity extends RoyalBossEntity { this.activeDecree = decree; this.activeDecreeTicks = 300; this.decreeCooldownTicks = 0; + this.projectRoyalSound(AntarchySoundEvents.KING_DECREE.get(), 4.0F, 1.0F, target); this.sendDecreeTitle(target); return true; } @@ -672,7 +677,7 @@ public class KingEntity extends RoyalBossEntity { && 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() { @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); } @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() { @Override public void onStart() { 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) { Vec3 origin = KingEntity.this.headAnchor(KingEntity.this.royalHead(RoyalHead.Slot.LEFT)); @@ -891,6 +898,7 @@ public class KingEntity extends RoyalBossEntity { this.decreeRetreatPressure = false; 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.projectRoyalSound(AntarchySoundEvents.KING_DECREE.get(), 4.0F, 1.0F, target); if (target instanceof ServerPlayer player) this.sendDecreeTitle(player); } int countdown = this.activeDecree.countdownTicks(target); @@ -904,6 +912,7 @@ public class KingEntity extends RoyalBossEntity { return; } if (this.activeDecree != null && --this.activeDecreeTicks <= 0) { + this.projectRoyalSound(AntarchySoundEvents.KING_SUCCESS.get(), 4.0F, 1.0F, target); this.endDecree(target); } } @@ -955,12 +964,12 @@ public class KingEntity extends RoyalBossEntity { 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) { if (this.activeDecree == null || target == null || !target.isAlive()) { return; } if (target instanceof ServerPlayer player) { + this.projectRoyalSound(AntarchySoundEvents.KING_JUDGEMENT.get(), 4.0F, 1.0F, target); this.debugTriggerPunishment(RoyalPunishmentType.ROYAL_EXILE, player); } else { this.invokeJudgment(target); @@ -982,6 +991,7 @@ public class KingEntity extends RoyalBossEntity { long now = this.level().getGameTime(); if (this.judgmentCooldowns.getOrDefault(target.getUUID(), 0L) <= now) { 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); } } diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/PrinceEntity.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/PrinceEntity.java index 6464b270..cb671845 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/PrinceEntity.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/PrinceEntity.java @@ -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 protected String geoName() { return "prince"; diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/QueenEntity.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/QueenEntity.java index c10e2da1..9260683a 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/QueenEntity.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/QueenEntity.java @@ -7,6 +7,7 @@ import com.craisinlord.antarchy.content.AntarchyTags; import com.craisinlord.antarchy.content.entity.ManticoreEntity; import com.craisinlord.antarchy.content.gravity.AntarchyGravityApi; import java.util.HashMap; +import java.util.Iterator; import java.util.Map; import java.util.UUID; import net.minecraft.core.BlockPos; @@ -21,23 +22,27 @@ import net.minecraft.world.entity.Entity; import net.minecraft.world.BossEvent; import net.minecraft.world.entity.EntityType; 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.AttributeModifier; import net.minecraft.world.entity.ai.attributes.AttributeSupplier; 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.level.Level; import net.minecraft.world.phys.Vec3; import com.craisinlord.antarchy.content.entity.royal.beam.RoyalBeamSettings; import com.craisinlord.antarchy.content.entity.royal.beam.RoyalBeamTerrainMode; +import com.craisinlord.antarchy.content.entity.royal.beam.RoyalBeamElement; import com.craisinlord.antarchy.content.entity.royal.attack.RoyalAttackLane; import com.craisinlord.antarchy.content.entity.royal.attack.RoyalEffectController; import com.craisinlord.antarchy.content.entity.portal.DimensionalTearEntity; -import com.craisinlord.antarchy.content.time.TimeDilationApi; import net.minecraft.world.damagesource.DamageSource; import net.minecraft.world.entity.player.Player; +import org.jetbrains.annotations.Nullable; 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 int FAILED_SUMMON_RETRY_TICKS = 20; 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 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_DURATION = 90; private static final double MOMENTUM_LOCK_RADIUS = 26.0D; @@ -61,13 +65,12 @@ public class QueenEntity extends RoyalBossEntity { private int manticoreSummonCooldownTicks; private int gravityStompCooldownTicks; - private int timeFieldCooldownTicks; private int momentumLockCooldownTicks; private int crushingGravityCooldownTicks; private int accelerationCooldownTicks; private int blackHoleCooldownTicks; + private final int[] dreamFireballCooldownTicks = new int[3]; private int idleWanderCooldownTicks; - private int finalTimeControlTicks; private final RoyalEffectController royalEffects = new RoyalEffectController(); private final Map frozenVelocities = new HashMap<>(); @@ -89,6 +92,52 @@ public class QueenEntity extends RoyalBossEntity { 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 protected SoundEvent royalFlyLoopSound() { return AntarchySoundEvents.QUEEN_FLY_LOOP.get(); @@ -145,9 +194,20 @@ public class QueenEntity extends RoyalBossEntity { 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) { - return entity instanceof RoyalBossEntity - || entity.getType().is(AntarchyTags.Entities.TIME_DILATION_IMMUNE); + return entity.getType().is(AntarchyTags.Entities.QUEEN_DOES_NOT_ATTACK); } @Override @@ -162,10 +222,49 @@ public class QueenEntity extends RoyalBossEntity { } this.tickManticoreSummon(); this.tickQueenAbilities(); - this.tickFinalTimeControl(); + this.tickDreamFireballs(); + this.tickFinalAcceleration(); 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() { if (this.idleWanderCooldownTicks-- > 0) { return; @@ -239,7 +338,6 @@ public class QueenEntity extends RoyalBossEntity { private void tickQueenAbilities() { if (this.gravityStompCooldownTicks > 0) this.gravityStompCooldownTicks--; - if (this.timeFieldCooldownTicks > 0) this.timeFieldCooldownTicks--; if (this.momentumLockCooldownTicks > 0) this.momentumLockCooldownTicks--; if (this.crushingGravityCooldownTicks > 0) this.crushingGravityCooldownTicks--; 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 allowOverlap = phase != Phase.ONE; String[] candidates = phase == Phase.ONE - ? new String[] {"gravity_stomp", "time_field", "black_hole"} - : new String[] {"gravity_stomp", "time_field", "black_hole", "momentum_lock", "crushing_gravity", "acceleration"}; + ? new String[] {"gravity_stomp", "black_hole"} + : new String[] {"gravity_stomp", "black_hole", "momentum_lock", "crushing_gravity", "acceleration"}; String selected = this.attackScheduler.chooseWeighted(candidates, id -> { if (!this.queenAttackReady(id, phase, majorBusy, allowOverlap)) return 0; return switch (id) { @@ -270,36 +368,39 @@ public class QueenEntity extends RoyalBossEntity { if (selected == null) return; int cooldown = switch (selected) { case "gravity_stomp" -> GRAVITY_STOMP_COOLDOWN; - case "time_field" -> TIME_FIELD_COOLDOWN; case "black_hole" -> BLACK_HOLE_COOLDOWN; case "momentum_lock" -> MOMENTUM_LOCK_COOLDOWN; case "crushing_gravity" -> CRUSHING_GRAVITY_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() { @Override public void onStart() { if ("gravity_stomp".equals(selected)) { QueenEntity.this.triggerAnim("body_action", "stomp"); - } else { - QueenEntity.this.triggerAnim("body_action", "wing_gust"); + } else if ("momentum_lock".equals(selected)) { + 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) { - 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; } - private void performSelectedQueenAttack(String selected, Phase phase, int cooldown, + private void performSelectedQueenAttack(String selected, int cooldown, ServerLevel level, LivingEntity target) { switch (selected) { 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 "momentum_lock" -> { this.momentumLockCooldownTicks = cooldown; this.startMomentumLock(level); } case "crushing_gravity" -> { @@ -316,8 +417,9 @@ public class QueenEntity extends RoyalBossEntity { if (!allowOverlap && majorBusy) return false; return switch (id) { 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 "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; @@ -325,21 +427,13 @@ public class QueenEntity extends RoyalBossEntity { }; } - private void tickFinalTimeControl() { - if (this.phase() != Phase.THREE || !(this.level() instanceof ServerLevel level)) { + private void tickFinalAcceleration() { + if (this.phase() != Phase.THREE) { return; } if (!this.royalEffects.active("acceleration") && this.accelerationCooldownTicks <= 0) { 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) { @@ -347,26 +441,32 @@ public class QueenEntity extends RoyalBossEntity { this.playRoyalSound(AntarchySoundEvents.QUEEN_ROAR.get(), 0.7F); DamageSource source = this.damageSources().mobAttack(this); 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)); living.hurt(source, (float) (12.0D * Math.max(0.25D, 1.0D - distance / 24.0D))); Vec3 push = living.position().subtract(this.position()).normalize().scale(1.3D); living.setDeltaMovement(living.getDeltaMovement().add(push.x, 0.65D, push.z)); living.hasImpulse = true; } - 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) { + double vertical = 2.0D + this.random.nextDouble() * 2.0D; + if (AntarchyGravityApi.isGravityInverted(target)) { + vertical = -vertical; + } Vec3 anchor = target.position().add( (this.random.nextDouble() - 0.5D) * 4.0D, - 2.0D + this.random.nextDouble() * 2.0D, + vertical, (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); - this.triggerAnim("body_action", "wing_gust"); 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) { @@ -390,7 +490,6 @@ public class QueenEntity extends RoyalBossEntity { } this.frozenVelocities.clear(); }); - this.triggerAnim("body_action", "wing_gust"); this.playRoyalSound(AntarchySoundEvents.QUEEN_ROAR.get(), 0.5F); } @@ -398,9 +497,11 @@ public class QueenEntity extends RoyalBossEntity { if (!(this.level() instanceof ServerLevel level)) { return; } - for (Map.Entry entry : this.frozenVelocities.entrySet()) { + for (Iterator> iterator = this.frozenVelocities.entrySet().iterator(); iterator.hasNext();) { + Map.Entry entry = iterator.next(); Entity entity = level.getEntity(entry.getKey()); if (entity == null || !entity.isAlive()) { + iterator.remove(); continue; } entity.setDeltaMovement(Vec3.ZERO); @@ -522,7 +623,6 @@ public class QueenEntity extends RoyalBossEntity { super.addAdditionalSaveData(tag); tag.putInt(SUMMON_COOLDOWN_KEY, this.manticoreSummonCooldownTicks); tag.putInt("GravityStompCooldownTicks", this.gravityStompCooldownTicks); - tag.putInt("TimeFieldCooldownTicks", this.timeFieldCooldownTicks); tag.putInt("AccelerationCooldownTicks", this.accelerationCooldownTicks); tag.putInt("BlackHoleCooldownTicks", this.blackHoleCooldownTicks); } @@ -532,7 +632,6 @@ public class QueenEntity extends RoyalBossEntity { super.readAdditionalSaveData(tag); this.manticoreSummonCooldownTicks = Math.max(0, tag.getInt(SUMMON_COOLDOWN_KEY)); this.gravityStompCooldownTicks = Math.max(0, tag.getInt("GravityStompCooldownTicks")); - this.timeFieldCooldownTicks = Math.max(0, tag.getInt("TimeFieldCooldownTicks")); this.momentumLockCooldownTicks = Math.max(0, tag.getInt("MomentumLockCooldownTicks")); this.crushingGravityCooldownTicks = Math.max(0, tag.getInt("CrushingGravityCooldownTicks")); this.accelerationCooldownTicks = Math.max(0, tag.getInt("AccelerationCooldownTicks")); diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/RoyalBlackHoleEntity.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/RoyalBlackHoleEntity.java index eb4c0f8e..e1539422 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/RoyalBlackHoleEntity.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/RoyalBlackHoleEntity.java @@ -2,20 +2,27 @@ package com.craisinlord.antarchy.content.entity.royal; import com.craisinlord.antarchy.content.AntarchyObjects; import com.craisinlord.antarchy.content.time.TimeDilationApi; +import com.craisinlord.antarchy.content.time.TimeDilationFieldEntity; import com.craisinlord.antarchy.config.AntarchySettings; import java.util.UUID; +import net.minecraft.core.BlockPos; import net.minecraft.core.particles.ParticleTypes; import net.minecraft.nbt.CompoundTag; import net.minecraft.network.syncher.EntityDataAccessor; import net.minecraft.network.syncher.EntityDataSerializers; import net.minecraft.network.syncher.SynchedEntityData; import net.minecraft.server.level.ServerLevel; +import net.minecraft.util.Mth; import net.minecraft.world.damagesource.DamageSource; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.ExperienceOrb; 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.Level; +import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.Vec3; import org.jetbrains.annotations.Nullable; @@ -33,21 +40,50 @@ public class RoyalBlackHoleEntity extends Entity implements GeoEntity { SynchedEntityData.defineId(RoyalBlackHoleEntity.class, EntityDataSerializers.FLOAT); private static final EntityDataAccessor COLLAPSING = SynchedEntityData.defineId(RoyalBlackHoleEntity.class, EntityDataSerializers.BOOLEAN); - + private static final EntityDataAccessor VISUAL_GROWTH = + SynchedEntityData.defineId(RoyalBlackHoleEntity.class, EntityDataSerializers.FLOAT); + private static final EntityDataAccessor EFFECT_RADIUS = + SynchedEntityData.defineId(RoyalBlackHoleEntity.class, EntityDataSerializers.FLOAT); private static final String OWNER_KEY = "OwnerUuid"; private static final String AGE_KEY = "Age"; 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 int DEFAULT_ACTIVE_TICKS = 120; - private static final int COLLAPSE_TICKS = 12; + private static final int QUEEN_ANIMATION_TICKS = 403; + 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 private UUID ownerId; private int age; - private int activeTicks = DEFAULT_ACTIVE_TICKS; - private boolean assailantVariant; + private int activeTicks = QUEEN_ACTIVE_TICKS; + private int animationAge; + private int suckedBlocks; + private int travelTicksRemaining; + @Nullable + private UUID dilationFieldId; + @Nullable + private Vec3 destination; private final AnimatableInstanceCache geoCache = GeckoLibUtil.createInstanceCache(this); public RoyalBlackHoleEntity(EntityType 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) { RoyalBlackHoleEntity hole = new RoyalBlackHoleEntity(AntarchyObjects.ROYAL_BLACK_HOLE.get(), level); - hole.setPos(center.x, center.y, center.z); - hole.setRadius(radius > 0.0D ? radius : AntarchySettings.queenBlackHoleRadius()); - hole.activeTicks = activeTicks > 0 ? Math.max(20, activeTicks) : AntarchySettings.queenBlackHoleActiveTicks(); - hole.ownerId = ownerId; + hole.configure(center, radius, activeTicks, ownerId); return hole; } 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) { - RoyalBlackHoleEntity hole = create(level, center, AntarchySettings.queenBlackHoleRadius(), AntarchySettings.queenBlackHoleActiveTicks(), ownerId); - hole.assailantVariant = true; + public static RoyalBlackHoleEntity createProjectile(ServerLevel level, Vec3 origin, Vec3 destination, + @Nullable UUID ownerId) { + 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; } + 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 protected void defineSynchedData(SynchedEntityData.Builder builder) { builder.define(RADIUS, (float) DEFAULT_RADIUS); 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) { @@ -89,28 +135,48 @@ public class RoyalBlackHoleEntity extends Entity implements GeoEntity { 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() { return this.entityData.get(COLLAPSING); } + public float visualGrowth() { + return this.entityData.get(VISUAL_GROWTH); + } + @Override public void tick() { super.tick(); + this.animationAge++; if (this.level().isClientSide) { this.spawnClientParticles(); return; } + if (this.travelTicksRemaining > 0 && this.destination != null) { + this.tickProjectileTravel(); + return; + } this.age++; if (!this.isCollapsing()) { - if (this.assailantVariant) { + if (this.risesWhileActive()) { this.setPos(this.getX(), this.getY() + 0.035D, this.getZ()); } + this.entityData.set(EFFECT_RADIUS, (float) this.computeEffectRadius()); + this.ensureAttachedDilationField(); this.applyPull(); - if (this.age % 10 == 0 && this.level() instanceof ServerLevel level) { - TimeDilationApi.createField(level, this.position(), Math.max(2.0D, this.radius() * 0.55D), 0.18D, 14, this.ownerId, !this.assailantVariant); - if (this.age % 20 == 0 && level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { - RoyalBlockDestruction.destroySphere(level, this, this.position(), Math.min(2.0D, this.radius() * 0.2D), 8, 30.0D, 0.0F); - } + if (this.pullsTerrainBlocks() && this.age % BLOCK_SUCTION_INTERVAL == 0 + && this.level() instanceof ServerLevel level) { + this.pullTerrainBlocks(level); } if (this.age >= this.activeTicks) { this.entityData.set(COLLAPSING, true); @@ -118,22 +184,105 @@ public class RoyalBlackHoleEntity extends Entity implements GeoEntity { } return; } - if (this.age >= COLLAPSE_TICKS) { + if (this.age >= this.collapseDurationTicks()) { this.collapse(); + this.discardAttachedDilationField(); 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() { - Vec3 center = this.position(); - double radius = this.radius(); + Vec3 center = this.effectCenter(); + double radius = this.effectRadius(); 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; } Vec3 toCenter = center.subtract(entity.position()); 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; } 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() { if (!(this.level() instanceof ServerLevel level)) { return; } - Vec3 center = this.position(); - double radius = this.radius(); + Vec3 center = this.effectCenter(); + double radius = this.effectRadius(); DamageSource source = this.ownerEntity() != null ? this.damageSources().mobAttack(this.ownerEntity()) : this.damageSources().magic(); @@ -172,10 +368,14 @@ public class RoyalBlackHoleEntity extends Entity implements GeoEntity { } private boolean isImmune(Entity entity) { - if (entity instanceof RoyalBossEntity) { + if (this.ownerId != null && this.ownerId.equals(entity.getUUID())) { 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 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() { - Vec3 center = this.position(); + Vec3 center = this.effectCenter(); for (int i = 0; i < 6; i++) { 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 pz = center.z + Math.sin(angle) * r; 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) { this.setRadius(tag.getDouble("Radius")); 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.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 @@ -226,10 +436,20 @@ public class RoyalBlackHoleEntity extends Entity implements GeoEntity { tag.putDouble("Radius", this.radius()); tag.putInt(AGE_KEY, this.age); tag.putInt(ACTIVE_KEY, this.activeTicks); + tag.putInt(ANIMATION_AGE_KEY, this.animationAge); + tag.putInt(SUCKED_BLOCKS_KEY, this.suckedBlocks); if (this.ownerId != null) { 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 @@ -238,7 +458,7 @@ public class RoyalBlackHoleEntity extends Entity implements GeoEntity { } private PlayState blackHoleController(AnimationState state) { - return state.setAndContinue(RawAnimation.begin().thenLoop("animation")); + return state.setAndContinue(RawAnimation.begin().thenPlay("animation")); } @Override diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/RoyalBoltEntity.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/RoyalBoltEntity.java index da08884f..4fdf081d 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/RoyalBoltEntity.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/RoyalBoltEntity.java @@ -78,7 +78,7 @@ public class RoyalBoltEntity extends AbstractHurtingProjectile { case FIRE -> ParticleTypes.FLAME; case LIGHTNING -> ParticleTypes.ELECTRIC_SPARK; case ICE -> ParticleTypes.SNOWFLAKE; - case GENERIC -> ParticleTypes.WITCH; + default -> ParticleTypes.WITCH; }; } diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/RoyalBossEntity.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/RoyalBossEntity.java index babcefcb..713d2ea2 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/RoyalBossEntity.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/RoyalBossEntity.java @@ -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.gravity.AntarchyGravityApi; import com.craisinlord.antarchy.content.worldgen.thoraxis.ThoraxisUndersideManager; +import java.util.Arrays; import java.util.List; import net.minecraft.network.chat.Component; +import net.minecraft.nbt.CompoundTag; import net.minecraft.network.syncher.EntityDataAccessor; import net.minecraft.network.syncher.EntityDataSerializers; 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.Attributes; 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.LookAtPlayerGoal; import net.minecraft.world.entity.ai.goal.RandomLookAroundGoal; import net.minecraft.world.entity.ai.goal.target.HurtByTargetGoal; import net.minecraft.world.entity.ai.goal.target.NearestAttackableTargetGoal; import net.minecraft.world.entity.ai.navigation.FlyingPathNavigation; +import net.minecraft.world.entity.ai.navigation.GroundPathNavigation; import net.minecraft.world.entity.ai.navigation.PathNavigation; import net.minecraft.world.entity.monster.Monster; 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_HIT_TICK = 6; + private static final int HEAD_ATTACK_ANIMATION_TICKS = 25; private static final int DEATH_TICKS = 60; private static final double CRUSH_RADIUS = 3.5D; 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 double OBSTRUCTION_CLEAR_MAX_RESISTANCE = 60.0D; 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_CENTER_Y = 11.5D; 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_MAX_HOVER_ABOVE_GROUND = 24.0D; - private static final EntityDataAccessor FIRING = - SynchedEntityData.defineId(RoyalBossEntity.class, EntityDataSerializers.BOOLEAN); - private static final EntityDataAccessor HAS_BEAM_END = - SynchedEntityData.defineId(RoyalBossEntity.class, EntityDataSerializers.BOOLEAN); + @SuppressWarnings("unchecked") + private static final EntityDataAccessor[] BEAM_ACTIVE = new EntityDataAccessor[] { + SynchedEntityData.defineId(RoyalBossEntity.class, EntityDataSerializers.BOOLEAN), + SynchedEntityData.defineId(RoyalBossEntity.class, EntityDataSerializers.BOOLEAN), + SynchedEntityData.defineId(RoyalBossEntity.class, EntityDataSerializers.BOOLEAN) + }; private static final EntityDataAccessor ACCELERATED = SynchedEntityData.defineId(RoyalBossEntity.class, EntityDataSerializers.BOOLEAN); - private static final EntityDataAccessor BEAM_ELEMENT = - SynchedEntityData.defineId(RoyalBossEntity.class, EntityDataSerializers.INT); - private static final EntityDataAccessor BEAM_HEAD_SLOT = - SynchedEntityData.defineId(RoyalBossEntity.class, EntityDataSerializers.INT); - private static final EntityDataAccessor BEAM_END_X = - SynchedEntityData.defineId(RoyalBossEntity.class, EntityDataSerializers.FLOAT); - private static final EntityDataAccessor BEAM_END_Y = - SynchedEntityData.defineId(RoyalBossEntity.class, EntityDataSerializers.FLOAT); - private static final EntityDataAccessor BEAM_END_Z = - SynchedEntityData.defineId(RoyalBossEntity.class, EntityDataSerializers.FLOAT); + private static final EntityDataAccessor ROYAL_FLYING = + SynchedEntityData.defineId(RoyalBossEntity.class, EntityDataSerializers.BOOLEAN); + @SuppressWarnings("unchecked") + private static final EntityDataAccessor[] BEAM_ELEMENT = new EntityDataAccessor[] { + SynchedEntityData.defineId(RoyalBossEntity.class, EntityDataSerializers.INT), + SynchedEntityData.defineId(RoyalBossEntity.class, EntityDataSerializers.INT), + SynchedEntityData.defineId(RoyalBossEntity.class, EntityDataSerializers.INT) + }; + @SuppressWarnings("unchecked") + private static final EntityDataAccessor[] 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[] 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[] 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 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 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(); private final ServerBossEvent bossEvent; 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.RIGHT) }; - private int beamCooldownTicks; + private final int[] beamLoopSoundTicks = new int[3]; + private final int[] assignedHeadCounts = new int[3]; + private int beamVolleyLimit; @Nullable - private RoyalHead activeBeamHead; - private int beamLoopSoundTicks; - private int beamHeadCursor; - private boolean beamStarted; + private Phase beamVolleyPhase; + private final MoveControl groundMoveControl; + private final FlyingMoveControl flyingMoveControl; + private final PathNavigation groundNavigation; + private final PathNavigation flyingNavigation; + private int locomotionDecisionTicks = 80; + private boolean landingForCombat; @Nullable private Entity[] multipartParts; protected RoyalBossEntity(EntityType entityType, Level level) { super(entityType, level); + for (int i = 0; i < this.beamControllers.length; i++) { + this.beamControllers[i] = new RoyalBeamController(this); + } 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()) { - 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.getUUID(), @@ -222,6 +259,14 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult protected abstract boolean isFlyingBoss(); + protected double groundCombatBias() { + return 0.35D; + } + + protected double biteApproachSpeed() { + return 1.3D; + } + public String geoNameForRender() { 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(7, new LookAtPlayerGoal(this, Player.class, 48.0F)); this.goalSelector.addGoal(8, new RandomLookAroundGoal(this)); + this.registerRoyalTargetGoals(); + } + + protected void registerRoyalTargetGoals() { this.targetSelector.addGoal(1, new HurtByTargetGoal(this)); 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 protected void defineSynchedData(SynchedEntityData.Builder builder) { super.defineSynchedData(builder); - builder.define(FIRING, false); - builder.define(HAS_BEAM_END, false); builder.define(ACCELERATED, false); - builder.define(BEAM_ELEMENT, RoyalBeamElement.GENERIC.ordinal()); - builder.define(BEAM_HEAD_SLOT, -1); - builder.define(BEAM_END_X, 0.0F); - builder.define(BEAM_END_Y, 0.0F); - builder.define(BEAM_END_Z, 0.0F); + builder.define(ROYAL_FLYING, true); + for (int i = 0; i < 3; i++) { + builder.define(BEAM_ACTIVE[i], false); + builder.define(BEAM_ELEMENT[i], RoyalBeamElement.GENERIC.ordinal()); + 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 @@ -308,9 +365,27 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult 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 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 public void travel(Vec3 travelVector) { - if (this.isFlyingBoss() && this.isEffectiveAi()) { + if (this.isRoyalFlying() && this.isEffectiveAi()) { this.moveRelative(this.getSpeed(), travelVector); this.move(MoverType.SELF, this.getDeltaMovement()); this.setDeltaMovement(this.getDeltaMovement().scale(0.91D)); @@ -336,19 +411,24 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult OBSTRUCTION_CLEAR_DROP_CHANCE); } if (this.isFlyingBoss()) { + this.moveControl = this.isRoyalFlying() ? this.flyingMoveControl : this.groundMoveControl; + this.navigation = this.isRoyalFlying() ? this.flyingNavigation : this.groundNavigation; this.setNoGravity(!AntarchyGravityApi.isGravityInverted(this)); + if (!this.isRoyalFlying()) { + this.setNoGravity(false); + } } if (this.level().isClientSide) { return; } - if (this.isFlyingBoss() && !this.isDeadOrDying()) { + if (this.isRoyalFlying() && !this.isDeadOrDying() && !this.landingForCombat) { this.tickFlyingAltitude(); } this.bossEvent.setProgress(this.getHealth() / this.getMaxHealth()); 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); } @@ -364,6 +444,7 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult return; } + this.tickCombatLocomotionMode(primaryTarget); this.getLookControl().setLookAt(primaryTarget, 30.0F, 30.0F); this.assignHeadTargets(primaryTarget); this.tickRoyalBeam(primaryTarget); @@ -400,45 +481,49 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult } private void tickRoyalBeam(LivingEntity primaryTarget) { - if (this.beamCooldownTicks > 0 || this.attackScheduler.laneBusy(this.beamLane())) { - this.beamCooldownTicks--; - } - - if (this.activeBeamHead != null) { - LivingEntity beamTarget = this.activeBeamHead.target(this.level()); - if (!this.activeBeamHead.shooting() || beamTarget == null || !beamTarget.isAlive() - || this.distanceTo(beamTarget) < this.royalBeamMinimumRange()) { - this.stopRoyalBeam(); - } else if (!this.beamStarted) { - return; - } else { - if (this.beamLoopSoundTicks-- <= 0) { - this.beamLoopSoundTicks = 20; - this.playRoyalSound(this.royalBeamLoopSound(), 1.0F); - } - this.beamController.tick( - this.beamAnchor(this.activeBeamHead), - beamTarget, - this.royalBeamSettings(), - this.royalBeamTerrainMode(this.activeBeamHead), - this::setRoyalBeamEndPosition); - Vec3 beamEnd = this.beamController.beamEndPosition(); - if (beamEnd != null) { - this.tickRoyalBeamEffects(this.activeBeamHead, this.beamAnchor(this.activeBeamHead), beamEnd); - } - if (!this.beamController.isFiring()) { - this.stopRoyalBeam(); - } - return; + Phase phase = this.phase(); + int activeBeams = 0; + for (RoyalHead head : this.heads) { + if (head.beamActive()) { + activeBeams++; } } - - if (this.beamCooldownTicks > 0) { - return; + if (activeBeams == 0 || this.beamVolleyPhase != phase) { + this.beamVolleyPhase = phase; + this.beamVolleyLimit = Math.max(1, this.selectBeamVolleyLimit(phase)); } - Phase phase = this.phase(); - for (int offset = 0; offset < this.heads.length; offset++) { - RoyalHead head = this.heads[(this.beamHeadCursor + offset) % this.heads.length]; + 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.stopRoyalBeam(head); + continue; + } + if (this.beamLoopSoundTicks[index]-- <= 0) { + this.beamLoopSoundTicks[index] = 20; + this.playRoyalSound(this.royalBeamLoopSound(), 1.0F); + } + controller.tick( + this.beamAnchor(head), + beamTarget, + this.royalBeamSettings(), + this.royalBeamTerrainMode(head), + end -> this.setRoyalBeamEndPosition(head.slot(), end)); + Vec3 beamEnd = controller.beamEndPosition(); + if (beamEnd != null) { + this.tickRoyalBeamEffects(head, this.beamAnchor(head), beamEnd); + } + if (!controller.isFiring()) { + this.stopRoyalBeam(head); + } + continue; + } + if (activeBeams >= this.beamVolleyLimit) { + continue; + } LivingEntity target = head.target(this.level()); if (!head.readyToAttack() || this.attackScheduler.laneBusy(this.headLane(head)) || target == null || !this.canAttack(target) @@ -454,62 +539,53 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult windup = Math.max(0, AntarchySettings.kingBeamWindupTicks()); } 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() { @Override public void onStart() { head.startShoot(); + RoyalBossEntity.this.triggerAnim(head.slot().controllerName(), "shoot"); } @Override public void onActive(int elapsedTicks) { - if (elapsedTicks != 0 || RoyalBossEntity.this.beamStarted) { + if (elapsedTicks != 0 || RoyalBossEntity.this.beamControllers[index].isFiring()) { return; } - RoyalBossEntity.this.beamStarted = true; - RoyalBossEntity.this.beamController.start( + RoyalBossEntity.this.beamControllers[index].start( RoyalBossEntity.this.beamAnchor(head), committedTarget.getEyePosition(), duration); - RoyalBossEntity.this.entityData.set(BEAM_ELEMENT, RoyalBossEntity.this.royalBeamElement(head).ordinal()); - RoyalBossEntity.this.entityData.set(BEAM_HEAD_SLOT, head.slot().ordinal()); - RoyalBossEntity.this.setFiringRoyalBeam(true); - RoyalBossEntity.this.triggerAnim(head.slot().controllerName(), "shoot"); - RoyalBossEntity.this.beamLoopSoundTicks = 20; + RoyalBossEntity.this.entityData.set(BEAM_ELEMENT[index], RoyalBossEntity.this.royalBeamElement(head).ordinal()); + RoyalBossEntity.this.entityData.set(BEAM_ACTIVE[index], true); + RoyalBossEntity.this.beamLoopSoundTicks[index] = 20; RoyalBossEntity.this.playRoyalSound(RoyalBossEntity.this.royalBeamStartSound(head), 1.0F); RoyalBossEntity.this.playRoyalSound(RoyalBossEntity.this.royalBeamShootSound(), 1.0F); } @Override public void onComplete() { - if (RoyalBossEntity.this.activeBeamHead == head) { - RoyalBossEntity.this.stopRoyalBeam(); - } + RoyalBossEntity.this.stopRoyalBeam(head); } })) { continue; } head.setBeamActive(true); - this.activeBeamHead = head; - this.beamHeadCursor = (head.slot().partIndex()) % this.heads.length; - this.beamCooldownTicks = cooldown; - this.beamStarted = false; - return; + activeBeams++; } } - private void stopRoyalBeam() { - if (this.activeBeamHead != null) { - this.activeBeamHead.setBeamActive(false); - this.activeBeamHead.stopShoot(); - } - this.activeBeamHead = null; - this.beamStarted = false; - this.attackScheduler.cancel(this.beamLane()); - this.beamController.stop(); - this.entityData.set(BEAM_ELEMENT, RoyalBeamElement.GENERIC.ordinal()); - this.entityData.set(BEAM_HEAD_SLOT, -1); + protected int selectBeamVolleyLimit(Phase phase) { + return phase.maxConcurrentHeadAttacks(); + } + + private void stopRoyalBeam(RoyalHead head) { + int index = head.slot().ordinal(); + head.setBeamActive(false); + head.stopShoot(); + 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.setFiringRoyalBeam(false); - this.setRoyalBeamEndPosition(null); + this.setRoyalBeamEndPosition(head.slot(), null); } public Phase phase() { @@ -524,6 +600,12 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult } private void assignHeadTargets(LivingEntity fallback) { + if (!this.shouldDistributeHeadTargets(fallback)) { + for (RoyalHead head : this.heads) { + head.setTarget(fallback); + } + return; + } List candidates = this.level().getEntitiesOfClass( Player.class, this.getBoundingBox().inflate(AntarchySettings.royalBossFollowRange()), @@ -535,7 +617,7 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult return; } - int[] assignedCounts = new int[candidates.size()]; + Arrays.fill(this.assignedHeadCounts, 0); for (RoyalHead head : this.heads) { Vec3 anchor = this.headAnchor(head); int bestIndex = 0; @@ -543,47 +625,113 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult double bestDistance = Double.MAX_VALUE; for (int i = 0; i < candidates.size(); i++) { 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; - bestCount = assignedCounts[i]; + bestCount = this.assignedHeadCounts[i]; bestDistance = distance; } } - assignedCounts[bestIndex]++; + this.assignedHeadCounts[bestIndex]++; head.setTarget(candidates.get(bestIndex)); } } + protected boolean shouldDistributeHeadTargets(LivingEntity primaryTarget) { + return true; + } + private void steerTowardTarget(LivingEntity target) { - double reach = this.getBbWidth() * 0.5D + this.biteReach() * 0.5D; double dx = target.getX() - this.getX(); 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 (closeHorizontally) { + if (!this.isRoyalFlying()) { + if (atStandoff) { this.getNavigation().stop(); } else { - this.getMoveControl().setWantedPosition(target.getX(), target.getY(), target.getZ(), 1.0D); + this.getMoveControl().setWantedPosition(wanted.x, target.getY(), wanted.z, approachSpeed); } return; } boolean inverted = AntarchyGravityApi.isGravityInverted(this); - double groundBelowTarget = this.groundYBelow(target.getX(), target.getZ()); - double wantedY = inverted + double groundBelowTarget = this.groundYBelow(wanted.x, wanted.z); + double wantedY = this.landingForCombat + ? (inverted ? target.getY() + target.getBbHeight() - this.getBbHeight() : groundBelowTarget) + : inverted ? target.getY() - 3.0D : Mth.clamp(target.getY() + 3.0D, groundBelowTarget + FLYING_MIN_HOVER, groundBelowTarget + FLYING_MAX_HOVER_ABOVE_GROUND); boolean closeVertically = Math.abs(wantedY - this.getY()) <= 2.5D; - if (closeHorizontally && closeVertically) { + if (atStandoff && closeVertically) { this.getNavigation().stop(); } 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) { return this.level().getHeight(net.minecraft.world.level.levelgen.Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, 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 center = new Vec3( 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)); RoyalBlockDestruction.destroySphere(serverLevel, this, center, CRUSH_RADIUS, CRUSH_MAX_BLOCKS, CRUSH_MAX_RESISTANCE, CRUSH_DROP_CHANCE); } private void startBite(RoyalHead head, Phase phase) { 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, new RoyalAttackScheduler.Action() { @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, 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) { @@ -676,7 +829,7 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult DamageSource damageSource = this.damageSources().mobAttack(this); AABB box = new AABB(anchor, anchor).inflate(reach); 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; } if (living.position().distanceToSqr(anchor) > reach * reach) { @@ -753,12 +906,22 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult @Override public void die(DamageSource damageSource) { 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) { head.reset(); } - this.beamController.stop(); + for (RoyalBeamController controller : this.beamControllers) { + controller.stop(); + } this.setFiringRoyalBeam(false); - this.setRoyalBeamEndPosition(null); + for (RoyalHead head : this.heads) { + this.stopRoyalBeam(head); + } + this.setRoyalFlying(false); this.getNavigation().stop(); this.setDeltaMovement(Vec3.ZERO); } @@ -791,6 +954,13 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult @Override protected void tickDeath() { 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()) { this.level().broadcastEntityEvent(this, (byte) 60); } @@ -835,71 +1005,85 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult } 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) { - this.entityData.set(FIRING, firing); } public RoyalBeamController beamController() { - return this.beamController; + return this.beamControllers[0]; } 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) { - 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); + this.entityData.set(BEAM_END_X[index], (float) position.x); + this.entityData.set(BEAM_END_Y[index], (float) position.y); + this.entityData.set(BEAM_END_Z[index], (float) position.z); } } @Nullable public Vec3 getRoyalBeamEndPosition() { - if (!this.entityData.get(HAS_BEAM_END)) { - return null; - } - return new Vec3(this.entityData.get(BEAM_END_X), this.entityData.get(BEAM_END_Y), this.entityData.get(BEAM_END_Z)); + return this.getRoyalBeamEndPosition(RoyalHead.Slot.CENTER); } public Vec3 getRoyalBeamShootFrom(float partialTicks) { - int slotIndex = this.activeBeamHead != null - ? this.activeBeamHead.slot().ordinal() - : this.entityData.get(BEAM_HEAD_SLOT); - if (slotIndex >= 0 && slotIndex < RoyalHead.Slot.values().length) { - double x = Mth.lerp(partialTicks, this.xo, this.getX()); - double y = Mth.lerp(partialTicks, this.yo, this.getY()); - double z = Mth.lerp(partialTicks, this.zo, this.getZ()); - float yaw = Mth.rotLerp(partialTicks, this.yRotO, this.getYRot()); - return this.beamAnchor(RoyalHead.Slot.values()[slotIndex], x, y, z, yaw); - } + return this.getRoyalBeamShootFrom(RoyalHead.Slot.CENTER, partialTicks); + } + + public Vec3 getRoyalBeamShootFrom(RoyalHead.Slot slot, float partialTicks) { double x = Mth.lerp(partialTicks, this.xo, this.getX()); - double y = Mth.lerp(partialTicks, this.yo, this.getY()) + this.getBbHeight() * 0.72D; + double y = Mth.lerp(partialTicks, this.yo, this.getY()); double z = Mth.lerp(partialTicks, this.zo, this.getZ()); - return new Vec3(x, y, z); + float yaw = Mth.rotLerp(partialTicks, this.yRotO, this.getYRot()); + return this.beamAnchor(slot, x, y, z, yaw); } 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 ? RoyalBeamElement.values()[ordinal] : 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 public RoyalHead.Slot getRoyalBeamHeadSlot() { - int slotIndex = this.activeBeamHead != null - ? this.activeBeamHead.slot().ordinal() - : this.entityData.get(BEAM_HEAD_SLOT); - return slotIndex >= 0 && slotIndex < RoyalHead.Slot.values().length - ? RoyalHead.Slot.values()[slotIndex] - : null; + for (RoyalHead.Slot slot : RoyalHead.Slot.values()) { + if (this.isFiringRoyalBeam(slot)) return slot; + } + return null; } @Override public void registerControllers(AnimatableManager.ControllerRegistrar controllers) { 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) .triggerableAnim("bite", RawAnimation.begin().thenPlay("bite_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"))); controllers.add(new AnimationController<>(this, "body_action", 0, this::bodyActionPredicate) .triggerableAnim("stomp", RawAnimation.begin().thenPlay("stomp")) - .triggerableAnim("wing_gust", RawAnimation.begin().thenPlay("wing_gust")) .triggerableAnim("minion_spawn", RawAnimation.begin().thenPlay("minion_spawn"))); } @@ -919,7 +1102,7 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult if (this.isDeadOrDying()) { return PlayState.STOP; } - if (this.isFlyingBoss()) { + if (this.isRoyalFlying()) { state.setAndContinue(FLY_ANIM); } else if (state.isMoving()) { state.setAndContinue(WALK_ANIM); @@ -940,6 +1123,18 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult 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 public AnimatableInstanceCache getAnimatableInstanceCache() { return this.geoCache; diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/RoyalElementalProjectileEntity.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/RoyalElementalProjectileEntity.java index 7db866f5..fec4280b 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/RoyalElementalProjectileEntity.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/RoyalElementalProjectileEntity.java @@ -5,6 +5,7 @@ import com.craisinlord.antarchy.content.entity.royal.beam.RoyalBeamElement; import com.craisinlord.antarchy.config.AntarchySettings; import net.minecraft.core.BlockPos; import net.minecraft.core.particles.ParticleTypes; +import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.nbt.CompoundTag; import net.minecraft.network.syncher.EntityDataAccessor; 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.util.GeckoLibUtil; -/** Shared travelling shell for the King's fireball and iceball. Impact rules stay element-specific. */ public class RoyalElementalProjectileEntity extends ThrowableProjectile implements GeoEntity { private static final EntityDataAccessor ATTACK_TYPE = SynchedEntityData.defineId(RoyalElementalProjectileEntity.class, EntityDataSerializers.INT); private static final int FIREBALL = 0; private static final int ICEBALL = 1; + private static final int DREAM_FIREBALL = 2; private static final int MAX_LIFETIME = 100; private final AnimatableInstanceCache geoCache = GeckoLibUtil.createInstanceCache(this); @@ -52,7 +53,8 @@ public class RoyalElementalProjectileEntity extends ThrowableProjectile implemen AntarchyObjects.ROYAL_ELEMENTAL_PROJECTILE.get(), level); projectile.setOwner(owner); 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); return projectile; } @@ -66,6 +68,10 @@ public class RoyalElementalProjectileEntity extends ThrowableProjectile implemen return this.entityData.get(ATTACK_TYPE) == ICEBALL; } + public boolean isDreamFireball() { + return this.entityData.get(ATTACK_TYPE) == DREAM_FIREBALL; + } + private void setAttackType(int type) { this.entityData.set(ATTACK_TYPE, type); } @@ -87,7 +93,7 @@ public class RoyalElementalProjectileEntity extends ThrowableProjectile implemen } super.tick(); 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); } } @@ -127,20 +133,43 @@ public class RoyalElementalProjectileEntity extends ThrowableProjectile implemen private void impactFire(ServerLevel level, Vec3 center) { 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), - entity -> entity.isAlive() && entity != this.getOwner())) { + this::canDamageWithImpact)) { double distance = Math.max(0.5D, living.position().distanceTo(center)); float falloff = (float) Math.max(0.25D, 1.0D - distance / 5.0D); living.hurt(source, (float) (AntarchySettings.kingFireballDamage() * falloff)); 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); } + 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) { 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), - entity -> entity.isAlive() && entity != this.getOwner())) { + this::canDamageWithImpact)) { living.hurt(source, (float) AntarchySettings.kingIceballDamage()); 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)); @@ -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); } + 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) { int changed = 0; 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() { - 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 diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/RoyalIceSpikeEntity.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/RoyalIceSpikeEntity.java index c1bace03..af78aad3 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/RoyalIceSpikeEntity.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/RoyalIceSpikeEntity.java @@ -22,7 +22,6 @@ import software.bernie.geckolib.animation.RawAnimation; import software.bernie.geckolib.util.GeckoLibUtil; 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 String ANIMATION_CONTROLLER = "ice_spike"; private final AnimatableInstanceCache geoCache = GeckoLibUtil.createInstanceCache(this); diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/RoyalMountEntity.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/RoyalMountEntity.java index ee2ecb1b..9c0be45e 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/RoyalMountEntity.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/RoyalMountEntity.java @@ -4,6 +4,12 @@ import com.craisinlord.antarchy.config.AntarchySettings; import com.craisinlord.antarchy.content.AntarchySoundEvents; import com.craisinlord.antarchy.content.AntarchyTags; 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 net.minecraft.core.BlockPos; import net.minecraft.nbt.CompoundTag; @@ -20,10 +26,12 @@ import net.minecraft.world.InteractionResult; import net.minecraft.world.damagesource.DamageSource; import net.minecraft.world.entity.AgeableMob; import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EntityDimensions; import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.Mob; import net.minecraft.world.entity.MoverType; +import net.minecraft.world.entity.Pose; import net.minecraft.world.entity.TamableAnimal; import net.minecraft.world.entity.ai.attributes.AttributeSupplier; 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.GroundPathNavigation; 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.player.Player; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; 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.pathfinder.PathType; import net.minecraft.world.phys.Vec3; +import net.minecraft.world.effect.MobEffectInstance; +import net.minecraft.core.particles.ParticleTypes; import org.jetbrains.annotations.Nullable; import software.bernie.geckolib.animatable.GeoEntity; 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); private static final EntityDataAccessor SADDLED = SynchedEntityData.defineId(RoyalMountEntity.class, EntityDataSerializers.BOOLEAN); + private static final EntityDataAccessor BEAM_ACTIVE = + SynchedEntityData.defineId(RoyalMountEntity.class, EntityDataSerializers.BOOLEAN); + private static final EntityDataAccessor BEAM_HEAD = + SynchedEntityData.defineId(RoyalMountEntity.class, EntityDataSerializers.INT); + private static final EntityDataAccessor HAS_BEAM_END = + SynchedEntityData.defineId(RoyalMountEntity.class, EntityDataSerializers.BOOLEAN); + private static final EntityDataAccessor BEAM_END_X = + SynchedEntityData.defineId(RoyalMountEntity.class, EntityDataSerializers.FLOAT); + private static final EntityDataAccessor BEAM_END_Y = + SynchedEntityData.defineId(RoyalMountEntity.class, EntityDataSerializers.FLOAT); + private static final EntityDataAccessor BEAM_END_Z = + SynchedEntityData.defineId(RoyalMountEntity.class, EntityDataSerializers.FLOAT); + private static final EntityDataAccessor GROWTH_PROGRESS = + SynchedEntityData.defineId(RoyalMountEntity.class, EntityDataSerializers.FLOAT); private static final RawAnimation IDLE_ANIM = RawAnimation.begin().thenLoop("idle"); 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 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 MoveControl groundMoveControl; @@ -100,6 +130,12 @@ public abstract class RoyalMountEntity extends TamableAnimal implements GeoEntit private int flapSoundTimer; private boolean riderAscendPressed; 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 entityType, Level level) { super(entityType, level); @@ -167,7 +203,7 @@ public abstract class RoyalMountEntity extends TamableAnimal implements GeoEntit this.goalSelector.addGoal(1, new SitWhenOrderedToGoal(this) { @Override public boolean canUse() { - return !RoyalMountEntity.this.isBaby() && super.canUse(); + return super.canUse(); } }); this.goalSelector.addGoal(2, new RoyalMountSpitGoal(this)); @@ -182,14 +218,14 @@ public abstract class RoyalMountEntity extends TamableAnimal implements GeoEntit @Override 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(6, new WaterAvoidingRandomStrollGoal(this, 0.8D) { @Override public boolean canUse() { - return !RoyalMountEntity.this.isBaby() && !RoyalMountEntity.this.isVehicle() + return !RoyalMountEntity.this.isVehicle() && !RoyalMountEntity.this.isOrderedToSit() && super.canUse(); } }); @@ -197,25 +233,25 @@ public abstract class RoyalMountEntity extends TamableAnimal implements GeoEntit this.goalSelector.addGoal(8, new RandomLookAroundGoal(this) { @Override public boolean canUse() { - return !RoyalMountEntity.this.isBaby() && super.canUse(); + return super.canUse(); } }); this.targetSelector.addGoal(1, new OwnerHurtByTargetGoal(this) { @Override public boolean canUse() { - return !RoyalMountEntity.this.isBaby() && super.canUse(); + return super.canUse(); } }); this.targetSelector.addGoal(2, new OwnerHurtTargetGoal(this) { @Override public boolean canUse() { - return !RoyalMountEntity.this.isBaby() && super.canUse(); + return super.canUse(); } }); this.targetSelector.addGoal(3, new HurtByTargetGoal(this) { @Override public boolean canUse() { - return !RoyalMountEntity.this.isBaby() && super.canUse(); + return super.canUse(); } }.setAlertOthers()); } @@ -226,6 +262,13 @@ public abstract class RoyalMountEntity extends TamableAnimal implements GeoEntit builder.define(ANIM_STATE, ANIM_IDLE); builder.define(FLYING, 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 @@ -233,6 +276,10 @@ public abstract class RoyalMountEntity extends TamableAnimal implements GeoEntit super.addAdditionalSaveData(tag); tag.putBoolean("Saddled", this.isSaddled()); 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 @@ -240,12 +287,30 @@ public abstract class RoyalMountEntity extends TamableAnimal implements GeoEntit super.readAdditionalSaveData(tag); this.setSaddled(tag.getBoolean("Saddled")); 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() { 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) { if (this.entityData.get(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); } - public float getAgeScale() { - if (!this.isBaby()) { - return 1.0F; + public float getGrowthScale() { + return 1.0F + 14.0F * this.getGrowthProgress(); + } + + 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(); } - float progress = 1.0F - Mth.clamp(-this.getAge() / (float) GROW_TICKS, 0.0F, 1.0F); - return 0.35F + 0.65F * progress; + } + + @Override + public EntityDimensions getDefaultDimensions(Pose pose) { + return super.getDefaultDimensions(pose).scale(this.getGrowthScale()); } @Override @@ -297,9 +375,7 @@ public abstract class RoyalMountEntity extends TamableAnimal implements GeoEntit stack.shrink(1); } this.heal(8.0F); - if (this.isBaby()) { - this.ageUp((int) (-this.getAge() * 0.1F), true); - } + this.setGrowthProgress(this.getGrowthProgress() + (1.0F - this.getGrowthProgress()) * 0.1F); if (!this.isTame() && this.random.nextInt(3) == 0) { this.tame(player); this.level().broadcastEntityEvent(this, (byte) 7); @@ -337,7 +413,7 @@ public abstract class RoyalMountEntity extends TamableAnimal implements GeoEntit return InteractionResult.sidedSuccess(this.level().isClientSide); } - if (stack.isEmpty() && !this.isBaby() && !player.isPassenger()) { + if (stack.isEmpty() && !player.isPassenger()) { if (!this.level().isClientSide) { this.setOrderedToSit(false); this.setInSittingPose(false); @@ -365,10 +441,6 @@ public abstract class RoyalMountEntity extends TamableAnimal implements GeoEntit public void tick() { super.tick(); - if (this.isBaby()) { - this.refreshDimensions(); - } - if (this.level().isClientSide) { return; } @@ -376,6 +448,7 @@ public abstract class RoyalMountEntity extends TamableAnimal implements GeoEntit if (this.spitCooldown > 0) { this.spitCooldown--; } + this.tickRoyalAbilities(); if ((this.isFlying() || !this.onGround()) && --this.flapSoundTimer <= 0) { 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() { if (this.isVehicle()) { return; @@ -540,6 +717,11 @@ public abstract class RoyalMountEntity extends TamableAnimal implements GeoEntit public void setRiderAscend(boolean 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) { @@ -566,8 +748,8 @@ public abstract class RoyalMountEntity extends TamableAnimal implements GeoEntit if (!this.hasPassenger(passenger)) { return; } - double yOffset = (this.isFlying() ? 1.55D : 1.35D) * this.getAgeScale(); - Vec3 back = this.getLookAngle().scale(-0.35D * this.getAgeScale()); + double yOffset = (this.isFlying() ? 1.55D : 1.35D) * this.getGrowthScale() - 1.0D; + Vec3 back = this.getLookAngle().scale(-0.35D * this.getGrowthScale()); moveFunction.accept(passenger, this.getX() + back.x, this.getY() + yOffset, this.getZ() + back.z); } diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/RoyalMountFollowOwnerGoal.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/RoyalMountFollowOwnerGoal.java index b85c10b3..0235984e 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/RoyalMountFollowOwnerGoal.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/RoyalMountFollowOwnerGoal.java @@ -1,5 +1,6 @@ package com.craisinlord.antarchy.content.entity.royal; +import net.minecraft.core.BlockPos; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.ai.goal.Goal; import net.minecraft.world.phys.Vec3; @@ -58,7 +59,7 @@ public class RoyalMountFollowOwnerGoal extends Goal { double distanceSqr = this.mount.distanceToSqr(this.owner); if (distanceSqr > TELEPORT_DISTANCE * TELEPORT_DISTANCE) { - this.mount.teleportTo(this.owner.getX(), this.owner.getY(), this.owner.getZ()); + this.trySafeTeleport(); return; } @@ -81,4 +82,29 @@ public class RoyalMountFollowOwnerGoal extends Goal { public boolean requiresUpdateEveryTick() { 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; + } + } + } + } + } + } } diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/RoyalMountSpitGoal.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/RoyalMountSpitGoal.java index 09358abb..8c745920 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/RoyalMountSpitGoal.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/RoyalMountSpitGoal.java @@ -13,7 +13,7 @@ public class RoyalMountSpitGoal extends Goal { @Override public boolean canUse() { - if (this.mount.isBaby() || this.mount.isVehicle() || this.mount.isOrderedToSit()) { + if (this.mount.isVehicle() || this.mount.isOrderedToSit()) { return false; } LivingEntity target = this.mount.getTarget(); diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/attack/RoyalAttackScheduler.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/attack/RoyalAttackScheduler.java index dfa2d0ea..d3c3338e 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/attack/RoyalAttackScheduler.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/attack/RoyalAttackScheduler.java @@ -8,7 +8,6 @@ import java.util.Map; import java.util.function.ToIntFunction; import net.minecraft.util.RandomSource; -/** Shared server-side arbitration for Royal attack lifecycles. */ public final class RoyalAttackScheduler { public interface Action { default void onStart() {} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/attack/RoyalEffectController.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/attack/RoyalEffectController.java index f0da3ab0..31c88218 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/attack/RoyalEffectController.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/attack/RoyalEffectController.java @@ -3,7 +3,6 @@ package com.craisinlord.antarchy.content.entity.royal.attack; import java.util.HashMap; import java.util.Map; -/** Owns persistent per-tick effects started by a Royal attack. */ public final class RoyalEffectController { private record Effect(Runnable tick, Runnable end, int duration, int age) { private Effect advance() { diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/beam/RoyalBeamController.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/beam/RoyalBeamController.java index 75fdfcfe..96987cd3 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/beam/RoyalBeamController.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/beam/RoyalBeamController.java @@ -1,5 +1,6 @@ package com.craisinlord.antarchy.content.entity.royal.beam; +import com.craisinlord.antarchy.content.entity.royal.RoyalBossEntity; import java.util.List; import java.util.HashSet; import java.util.Set; @@ -34,6 +35,7 @@ public final class RoyalBeamController { private int beamAgeTicks; private int trackingDelayTicks; private int terrainMutationsThisTick; + private final Set damagedThisTick = new HashSet<>(); private static final int ICE_MUTATIONS_PER_TICK = 3; private static final float ICE_IMPACT_RADIUS = 2.0F; @@ -137,17 +139,17 @@ public final class RoyalBeamController { Vec3 direction = beamEnd.subtract(shootFrom).normalize(); double distance = shootFrom.distanceTo(beamEnd); DamageSource damageSource = this.owner.damageSources().mobAttack(this.owner); - Set damagedThisTick = new HashSet<>(); + this.damagedThisTick.clear(); boolean pathMutates = terrainMode == RoyalBeamTerrainMode.DESTROY; for (double walked = settings.pathStep(); walked < Math.min(distance, settings.range()); walked += settings.pathStep()) { Vec3 sample = shootFrom.add(direction.scale(walked)); - hurtEntitiesAround(sample, settings.pathDamageRadius(), settings.damage(), settings.knockback(), damageSource, settings.requireLineOfSightForDamage(), damagedThisTick); + hurtEntitiesAround(sample, settings.pathDamageRadius(), settings.damage(), settings.knockback(), damageSource, settings.requireLineOfSightForDamage(), this.damagedThisTick); if (pathMutates && shouldMutateTerrain(settings, walked)) { 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)) { return; @@ -193,6 +195,7 @@ public final class RoyalBeamController { if (living.is(this.owner) || this.owner.isAlliedTo(living) || living.getType() == this.owner.getType() + || this.owner instanceof RoyalBossEntity royalBoss && !royalBoss.canDamageWithRoyalAttack(living) || living.distanceToSqr(center) > radius * radius || damagedThisTick.contains(living.getId()) || requireLineOfSight && !canEntityBeHurtFrom(center, living)) { diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/beam/RoyalBeamElement.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/beam/RoyalBeamElement.java index 3706166d..f98e9967 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/beam/RoyalBeamElement.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/beam/RoyalBeamElement.java @@ -4,5 +4,9 @@ public enum RoyalBeamElement { GENERIC, FIRE, LIGHTNING, - ICE + ICE, + QUEEN_PURPLE, + QUEEN_RED, + QUEEN_BLACK, + DREAM_FIRE } diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/decree/AdvanceDecree.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/decree/AdvanceDecree.java index f28bf41a..a1aeab5e 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/decree/AdvanceDecree.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/decree/AdvanceDecree.java @@ -7,7 +7,7 @@ public final class AdvanceDecree implements RoyalDecree { public String translationKey() { return "decree.antarchy.advance"; } public void apply(ServerLevel level, KingEntity king, LivingEntity target) { king.setDecreeRetreatPressure(true); } @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 public int contextWeight(KingEntity king, LivingEntity target) { diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/decree/ComeNoCloserDecree.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/decree/ComeNoCloserDecree.java index f98e4ef9..4df50114 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/decree/ComeNoCloserDecree.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/decree/ComeNoCloserDecree.java @@ -6,7 +6,7 @@ public final class ComeNoCloserDecree implements RoyalDecree { public String translationKey() { return "decree.antarchy.come_no_closer"; } public void apply(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 public int contextWeight(KingEntity king, LivingEntity target) { diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/decree/FightMeCowardDecree.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/decree/FightMeCowardDecree.java index d541f8b7..efeab5e4 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/decree/FightMeCowardDecree.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/decree/FightMeCowardDecree.java @@ -7,7 +7,7 @@ public final class FightMeCowardDecree implements RoyalDecree { public String name() { return "FIGHT ME, COWARD!"; } public String translationKey() { return "decree.antarchy.fight_me_coward"; } 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); target.setDeltaMovement(target.getDeltaMovement().add(pull.x, 0.05D, pull.z)); target.hasImpulse = true; @@ -15,6 +15,6 @@ public final class FightMeCowardDecree implements RoyalDecree { } @Override 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; } } diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/decree/KeepYourDistanceDecree.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/decree/KeepYourDistanceDecree.java index cc876daa..780db185 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/decree/KeepYourDistanceDecree.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/decree/KeepYourDistanceDecree.java @@ -7,7 +7,7 @@ public final class KeepYourDistanceDecree implements RoyalDecree { public String translationKey() { return "decree.antarchy.keep_your_distance"; } public void apply(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 public int contextWeight(KingEntity king, LivingEntity target) { diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/decree/NoRetreatDecree.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/decree/NoRetreatDecree.java index 53a8ed1f..ce190b32 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/decree/NoRetreatDecree.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/decree/NoRetreatDecree.java @@ -6,7 +6,7 @@ public final class NoRetreatDecree implements RoyalDecree { private static final double ALLOWED_RANGE = 26.0D; public String translationKey() { return "decree.antarchy.no_retreat"; } 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); } } diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/item/AntimetalScaffoldingItem.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/item/AntimetalScaffoldingItem.java index 712f276f..657a2e86 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/item/AntimetalScaffoldingItem.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/item/AntimetalScaffoldingItem.java @@ -2,15 +2,10 @@ package com.craisinlord.antarchy.content.item; import com.craisinlord.antarchy.content.block.AntimetalScaffoldingBlock; import com.craisinlord.antarchy.content.gravity.AntarchyGravityApi; -import java.util.List; -import net.minecraft.ChatFormatting; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; -import net.minecraft.network.chat.Component; import net.minecraft.world.item.Item; -import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.ScaffoldingBlockItem; -import net.minecraft.world.item.TooltipFlag; import net.minecraft.world.item.context.BlockPlaceContext; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.Block; @@ -22,12 +17,6 @@ public class AntimetalScaffoldingItem extends ScaffoldingBlockItem { super(block, properties); } -// @Override -// public void appendHoverText(ItemStack stack, Item.TooltipContext context, List tooltipComponents, TooltipFlag tooltipFlag) { -// tooltipComponents.add(Component.translatable("tooltip.antarchy.antimetal_scaffolding").withStyle(ChatFormatting.DARK_AQUA)); -// super.appendHoverText(stack, context, tooltipComponents, tooltipFlag); -// } - @Nullable @Override public BlockPlaceContext updatePlacementContext(BlockPlaceContext context) { diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/item/EyeOfTheStormItem.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/item/EyeOfTheStormItem.java index d8baf43c..b48d158f 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/item/EyeOfTheStormItem.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/item/EyeOfTheStormItem.java @@ -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.entity.vortex.WindVortexEntity; import java.util.List; -import java.util.Map; -import java.util.UUID; -import java.util.concurrent.ConcurrentHashMap; import java.util.function.Consumer; import net.minecraft.ChatFormatting; 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 String IDLE_CONTROLLER = "idle_controller"; private static final String IDLE_ANIMATION = "spinning_idle"; - private static final Map LAST_UPDRAFT_TICK = new ConcurrentHashMap<>(); - private static final Map LAST_SURGE_TICK = new ConcurrentHashMap<>(); private static final float SURGE_PROJECTILE_SPEED = 0.65F; private final AnimatableInstanceCache geoCache = GeckoLibUtil.createInstanceCache(this); @@ -67,12 +62,9 @@ public class EyeOfTheStormItem extends Item implements GeoItem { return InteractionResultHolder.consume(stack); } - long now = level.getGameTime(); - Long last = LAST_UPDRAFT_TICK.get(player.getUUID()); - if (last != null && now - last < AntarchySettings.eyeOfTheStormUpdraftCooldownTicks()) { + if (player.getCooldowns().isOnCooldown(this)) { return InteractionResultHolder.fail(stack); } - LAST_UPDRAFT_TICK.put(player.getUUID(), now); ServerLevel serverLevel = (ServerLevel) level; 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.hasImpulse = true; player.resetFallDistance(); + player.getCooldowns().addCooldown(this, AntarchySettings.eyeOfTheStormUpdraftCooldownTicks()); if (player instanceof ServerPlayer serverPlayer) { serverPlayer.connection.send(new ClientboundSetEntityMotionPacket(serverPlayer)); } @@ -109,15 +102,13 @@ public class EyeOfTheStormItem extends Item implements GeoItem { return; } - long now = level.getGameTime(); - Long last = LAST_SURGE_TICK.get(player.getUUID()); - if (last != null && now - last < AntarchySettings.eyeOfTheStormSurgeCooldownTicks()) { + if (player.getCooldowns().isOnCooldown(this)) { return; } - LAST_SURGE_TICK.put(player.getUUID(), now); Vec3 look = player.getViewVector(1.0F); launchStormVortex(level, player.getEyePosition().add(look), look, player); + player.getCooldowns().addCooldown(this, AntarchySettings.eyeOfTheStormSurgeCooldownTicks()); player.awardStat(Stats.ITEM_USED.get(this)); stack.hurtAndBreak(1, player, EquipmentSlot.MAINHAND); diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/item/MucusBlockItem.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/item/MucusBlockItem.java index 5fa62c75..c2033984 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/item/MucusBlockItem.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/item/MucusBlockItem.java @@ -1,13 +1,6 @@ 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.Item; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.item.TooltipFlag; import net.minecraft.world.level.block.Block; public class MucusBlockItem extends BlockItem { @@ -15,9 +8,4 @@ public class MucusBlockItem extends BlockItem { super(block, properties); } - @Override - public void appendHoverText(ItemStack stack, Item.TooltipContext context, List tooltipComponents, TooltipFlag tooltipFlag) { -// tooltipComponents.add(Component.translatable("tooltip.antarchy.mucus_slippery").withStyle(ChatFormatting.DARK_AQUA)); - super.appendHoverText(stack, context, tooltipComponents, tooltipFlag); - } } diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/item/RoyalAssailantArmorItem.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/item/RoyalAssailantArmorItem.java index 31734408..6801f2cd 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/item/RoyalAssailantArmorItem.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/item/RoyalAssailantArmorItem.java @@ -1,6 +1,5 @@ package com.craisinlord.antarchy.content.item; -import com.craisinlord.antarchy.Antarchy; import com.craisinlord.antarchy.config.AntarchySettings; import com.craisinlord.antarchy.content.AntarchyObjects; 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()); 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()); } 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)); } } diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/item/RoyalAssailantBattleAxeItem.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/item/RoyalAssailantBattleAxeItem.java index cdaf0426..6bccc046 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/item/RoyalAssailantBattleAxeItem.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/item/RoyalAssailantBattleAxeItem.java @@ -1,7 +1,7 @@ package com.craisinlord.antarchy.content.item; 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 java.util.List; import net.minecraft.ChatFormatting; @@ -81,7 +81,7 @@ public class RoyalAssailantBattleAxeItem extends AxeItem { return InteractionResultHolder.sidedSuccess(stack, true); } if (level instanceof ServerLevel serverLevel) { - RoyalBlackHoleEntity blackHole = RoyalBlackHoleEntity.createAssailant( + RoyalAssailantBlackHoleEntity blackHole = RoyalAssailantBlackHoleEntity.create( serverLevel, player.position().add(player.getLookAngle().normalize().scale(2.0D)).add(0.0D, 2.0D, 0.0D), player.getUUID()); diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/item/WormHookTetherManager.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/item/WormHookTetherManager.java index 5edf26af..117f0048 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/item/WormHookTetherManager.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/item/WormHookTetherManager.java @@ -13,15 +13,6 @@ import net.minecraft.util.Mth; import net.minecraft.world.entity.Entity; 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 { private static final double MIN_LEN = 2.0D; private static final double REEL_SPEED = 0.12D; @@ -51,14 +42,12 @@ public final class WormHookTetherManager { 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) { clear(player); TETHERS.put(player.getUUID(), new TetherState(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) { TetherState state = TETHERS.get(player.getUUID()); if (state == null || state.hookEntityId != hook.getId()) { @@ -108,7 +97,6 @@ public final class WormHookTetherManager { } if (state.rope == null) { - // Hook is still flying; nothing to simulate yet, just tracking it for the render sync. return; } @@ -135,8 +123,6 @@ public final class WormHookTetherManager { 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) { Vec3 pull = towardPivot.scale(-REEL_PULL_STRENGTH); Vec3 reelMotion = player.getDeltaMovement().add(pull); diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/portal/PermanentPortalTeleporter.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/portal/PermanentPortalTeleporter.java index 3c4e586a..22ecc618 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/portal/PermanentPortalTeleporter.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/portal/PermanentPortalTeleporter.java @@ -63,7 +63,7 @@ public final class PermanentPortalTeleporter { return active.center(); } - Vec3 safe = findSafeArrivalPosition(entity, destination, preferredPos); + Vec3 safe = findSafeArrivalPosition(entity, destination, preferredPos, type); if (safe != null) { PermanentPortalShape nearbyActive = findActiveNearby(destination, BlockPos.containing(safe), type); if (nearbyActive != null) { @@ -77,7 +77,11 @@ public final class PermanentPortalTeleporter { 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 @@ -108,14 +112,16 @@ public final class PermanentPortalTeleporter { } @Nullable - private static Vec3 findSafeArrivalPosition(Entity entity, ServerLevel destination, BlockPos preferredPos) { - int[] yRange = getDimensionYRange(destination); + private static Vec3 findSafeArrivalPosition(Entity entity, ServerLevel destination, BlockPos preferredPos, PermanentPortalType type) { + int[] yRange = getDimensionYRange(destination, type); if (yRange != null) { return findSafeArrivalPositionInYRange(entity, destination, preferredPos, yRange[0], yRange[1]); } Set candidates = new LinkedHashSet<>(); - addCandidate(candidates, preferredPos); + if (type != PermanentPortalType.ELYTHIA) { + addCandidate(candidates, preferredPos); + } for (int radius = 0; radius <= SEARCH_RADIUS; radius++) { 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) { Vec3 safe = tryFindSafePosition(entity, destination, candidate); if (safe != null) { @@ -277,12 +287,12 @@ public final class PermanentPortalTeleporter { } @Nullable - private static int[] getDimensionYRange(ServerLevel destination) { + private static int[] getDimensionYRange(ServerLevel destination, PermanentPortalType type) { ResourceKey dim = destination.dimension(); if (dim == AntarchySettings.termiteDestinationDimension()) { return new int[]{100, 200}; } - if (dim == AntarchySettings.brownAntDestinationDimension()) { + if (type != PermanentPortalType.ELYTHIA && dim == AntarchySettings.brownAntDestinationDimension()) { return new int[]{75, 120}; } return null; diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/time/TimeDilationApi.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/time/TimeDilationApi.java index c08d3ec6..2304de25 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/time/TimeDilationApi.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/time/TimeDilationApi.java @@ -97,7 +97,6 @@ public final class TimeDilationApi { SYNCED_CLIENT_RATES.put(entityUuid, clampedRate); } - /** Clears both the authoritative entity state and any client-side UUID cache. */ public static void clearRate(Entity entity) { if (entity instanceof TimeDilationEntityAccess access) { boolean wasDilated = Math.abs(access.antarchy$getTimeDilationRate() - TimeDilationMath.NORMAL_RATE) > 0.001D; diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/time/TimeDilationFieldEntity.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/time/TimeDilationFieldEntity.java index fecf2e27..6da445bc 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/time/TimeDilationFieldEntity.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/time/TimeDilationFieldEntity.java @@ -1,6 +1,10 @@ package com.craisinlord.antarchy.content.time; 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.network.syncher.EntityDataAccessor; import net.minecraft.network.syncher.EntityDataSerializers; @@ -25,6 +29,8 @@ public class TimeDilationFieldEntity extends Entity { private int durationTicks = -1; private int age; private UUID ownerId; + private UUID anchorId; + private int missingAnchorTicks; private boolean visual = true; public TimeDilationFieldEntity(EntityType entityType, Level level) { @@ -56,6 +62,23 @@ public class TimeDilationFieldEntity extends Entity { 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() { return this.visual; } @@ -72,6 +95,19 @@ public class TimeDilationFieldEntity extends Entity { if (this.level().isClientSide) { 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++; if (this.durationTicks >= 0 && this.age >= this.durationTicks) { this.discard(); @@ -127,6 +163,7 @@ public class TimeDilationFieldEntity extends Entity { this.durationTicks = tag.contains(DURATION_KEY) ? tag.getInt(DURATION_KEY) : -1; this.age = tag.getInt(AGE_KEY); 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"); } @@ -139,6 +176,9 @@ public class TimeDilationFieldEntity extends Entity { if (this.ownerId != null) { tag.putUUID("OwnerUuid", this.ownerId); } + if (this.anchorId != null) { + tag.putUUID("AnchorUuid", this.anchorId); + } tag.putBoolean("Visual", this.visual); } } diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/time/TimeDilationFieldSampler.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/time/TimeDilationFieldSampler.java index 5053c6db..f48cc092 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/time/TimeDilationFieldSampler.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/time/TimeDilationFieldSampler.java @@ -32,7 +32,7 @@ public final class TimeDilationFieldSampler { public static double sample(List fields, Entity entity) { double combinedRate = TimeDilationMath.NORMAL_RATE; for (TimeDilationFieldEntity field : fields) { - if (field.isOwnedBy(entity)) { + if (!field.affects(entity)) { continue; } Vec3 center = field.position(); diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/time/TimeDilationManager.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/time/TimeDilationManager.java index 12fd3f4b..2daa25e0 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/time/TimeDilationManager.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/time/TimeDilationManager.java @@ -1,7 +1,12 @@ package com.craisinlord.antarchy.content.time; import java.util.ArrayList; +import java.util.Collections; +import java.util.IdentityHashMap; 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.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; @@ -9,33 +14,57 @@ import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.projectile.Projectile; 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.RoyalEffectEligibility; import com.craisinlord.antarchy.content.effect.RoyalEffectHooks; public final class TimeDilationManager { + private static final Map TRACKING = new WeakHashMap<>(); + private TimeDilationManager() { } public static void tickServer(MinecraftServer server) { for (ServerLevel level : server.getAllLevels()) { List fields = collectFields(level); - syncFieldSnapshots(level, fields); - updateEntities(level, fields); + TrackingState tracking = TRACKING.computeIfAbsent(level, ignored -> new TrackingState()); + discoverFieldEntities(level, fields, tracking); + syncFieldSnapshots(level, fields, tracking); + updateEntities(level, fields, tracking); + if (fields.isEmpty() && tracking.entities.isEmpty()) { + TRACKING.remove(level); + } if (!fields.isEmpty()) { TimeDilationParticles.spawnFieldBorders(level, fields.stream().filter(TimeDilationFieldEntity::isVisual).toList()); } } } - private static void syncFieldSnapshots(ServerLevel level, List 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 fields, TrackingState tracking) { List snapshots = fields.stream() .map(field -> new TimeDilationFieldSnapshot( field.getX(), field.getY(), field.getZ(), field.fieldRadius(), field.fieldRate(), field.fieldAge(), field.fieldDurationTicks())) .limit(128) .toList(); + if (level.getGameTime() % 4L != 0L && snapshots.size() == tracking.lastSnapshots.size()) { + return; + } + tracking.lastSnapshots = snapshots; for (ServerPlayer player : level.players()) { TimeDilationApi.syncFields(player, snapshots); } @@ -51,8 +80,23 @@ public final class TimeDilationManager { return fields; } - private static void updateEntities(ServerLevel level, List fields) { - for (Entity entity : level.getAllEntities()) { + private static void discoverFieldEntities(ServerLevel level, List fields, TrackingState tracking) { + 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 fields, TrackingState tracking) { + var iterator = tracking.entities.iterator(); + while (iterator.hasNext()) { + Entity entity = iterator.next(); if (!(entity instanceof TimeDilationEntityAccess access) || entity instanceof TimeDilationFieldEntity) { continue; } @@ -62,6 +106,7 @@ public final class TimeDilationManager { living.removeEffect(RoyalEffectHooks.dilatedHolder()); } TimeDilationApi.clearRate(entity); + iterator.remove(); continue; } double previousRate = access.antarchy$getTimeDilationRate(); @@ -116,7 +161,23 @@ public final class TimeDilationManager { if (Math.abs(previousRate - rate) > 0.001D) { 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 entities = Collections.newSetFromMap(new IdentityHashMap<>()); + private List lastSnapshots = List.of(); } } diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/time/TimeDilationMath.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/time/TimeDilationMath.java index 96077e88..9f18e850 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/time/TimeDilationMath.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/time/TimeDilationMath.java @@ -18,7 +18,6 @@ public final class TimeDilationMath { 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) { double current = clampRate(currentRate); double target = clampRate(targetRate); diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/worldgen/elythia/OuranwoodTreeFeature.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/worldgen/elythia/OuranwoodTreeFeature.java index 9dd44aad..ed6b89b3 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/worldgen/elythia/OuranwoodTreeFeature.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/worldgen/elythia/OuranwoodTreeFeature.java @@ -9,8 +9,10 @@ import com.mojang.serialization.Codec; import java.util.ArrayList; import java.util.ArrayDeque; import java.util.HashMap; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; +import java.util.Set; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.server.level.ServerLevel; @@ -33,6 +35,13 @@ import net.minecraft.world.level.block.state.properties.IntegerProperty; import org.jetbrains.annotations.Nullable; public class OuranwoodTreeFeature extends Feature { + private static final ThreadLocal ACTIVE_PLACEMENT = new ThreadLocal<>(); + + private static final class TreePlacement { + private final Set logPositions = new LinkedHashSet<>(); + private final Set leafPositions = new LinkedHashSet<>(); + } + public OuranwoodTreeFeature(Codec codec) { super(codec); } @@ -44,9 +53,12 @@ public class OuranwoodTreeFeature extends Feature { OuranwoodTreeConfiguration config = context.config(); RandomSource random = context.random(); - if (!canGrowOn(level, origin.below())) { - return false; - } + TreePlacement placement = new TreePlacement(); + ACTIVE_PLACEMENT.set(placement); + try { + if (!canGrowOn(level, origin.below())) { + return false; + } int height = config.height().sample(random); int trunkRadius = config.trunkRadius().sample(random); @@ -86,7 +98,10 @@ public class OuranwoodTreeFeature extends Feature { this.placeVinesAndGlowBerries(level, canopyCenter, config, random, canopyRadius, canopyDepth, height); this.placeFlyingSquirrelNest(level, origin, config, random, trunkRadius, height); 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) { @@ -1044,6 +1059,28 @@ public class OuranwoodTreeFeature extends Feature { } 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 minY = canopyCenter.getY() - Math.max(4, canopyDepth / 2 + 1); int maxY = origin.getY() + height + canopyDepth + 6; @@ -1077,6 +1114,23 @@ public class OuranwoodTreeFeature extends Feature { } 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 minY = Math.max(origin.getY() + Math.max(4, height / 4), canopyCenter.getY() - canopyDepth - 3); int maxY = origin.getY() + height + canopyDepth + 4; @@ -1112,6 +1166,11 @@ public class OuranwoodTreeFeature extends Feature { } 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 minY = Math.max(level.getMinBuildHeight(), origin.getY() - 2); int maxY = Math.min(level.getMaxBuildHeight() - 1, origin.getY() + height + canopyDepth + 8); @@ -1200,6 +1259,78 @@ public class OuranwoodTreeFeature extends Feature { } } + private void updateLeafDistances(WorldGenLevel level, Set leafPositions) { + Map distances = new HashMap<>(); + ArrayDeque 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) { int maxAcorns = config.juvenile() ? 12 + random.nextInt(6) : 40 + random.nextInt(20); int attempts = maxAcorns * 12; @@ -1530,6 +1661,15 @@ public class OuranwoodTreeFeature extends Feature { protected void setBlock(WorldGenLevel level, BlockPos pos, BlockState state) { if (canWriteAt(level, pos)) { 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); + } + } } } diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/worldgen/thoraxis/LucidAntiwaterPoolFeature.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/worldgen/thoraxis/LucidAntiwaterPoolFeature.java index 3e14ca50..ecbade9b 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/worldgen/thoraxis/LucidAntiwaterPoolFeature.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/worldgen/thoraxis/LucidAntiwaterPoolFeature.java @@ -2,6 +2,7 @@ package com.craisinlord.antarchy.content.worldgen.thoraxis; import com.craisinlord.antarchy.content.AntarchyObjects; import com.mojang.serialization.Codec; +import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap; import net.minecraft.core.BlockPos; import net.minecraft.util.Mth; import net.minecraft.util.RandomSource; @@ -34,6 +35,7 @@ public final class LucidAntiwaterPoolFeature extends Feature maxCeilingY + 3) { continue; } @@ -113,7 +116,7 @@ public final class LucidAntiwaterPoolFeature extends Feature { private final ResourceLocation blockId; private final Direction direction; diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/worldgen/thoraxis/ThoraxisIchorLakeFeature.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/worldgen/thoraxis/ThoraxisIchorLakeFeature.java index fec5c963..2b0d067c 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/worldgen/thoraxis/ThoraxisIchorLakeFeature.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/worldgen/thoraxis/ThoraxisIchorLakeFeature.java @@ -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.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 { private static final int LAKE_FLOOR_Y = 1; private static final int LAKE_SURFACE_Y = 8; diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/worldgen/thoraxis/ThoraxisUndersideManager.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/worldgen/thoraxis/ThoraxisUndersideManager.java index 04e3ba75..cefd699c 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/worldgen/thoraxis/ThoraxisUndersideManager.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/worldgen/thoraxis/ThoraxisUndersideManager.java @@ -71,6 +71,7 @@ public final class ThoraxisUndersideManager { int refreshedLiving = 0; int flippedItems = 0; int restoredItems = 0; + Set evicted = Collections.newSetFromMap(new IdentityHashMap<>()); for (Entity entity : tracking.entities) { if (!entity.isAlive() || entity.isSpectator()) { continue; @@ -82,6 +83,8 @@ public final class ThoraxisUndersideManager { if (living.getY() < threshold) { refreshInvertedEffect(living); refreshedLiving++; + } else if (!hasEffect) { + evicted.add(entity); } continue; } @@ -122,9 +125,12 @@ public final class ThoraxisUndersideManager { AntarchyGravityApi.setGravityDirection(entity, AntarchyGravityDirection.DOWN, false, TRANSITION); restoredItems++; } + evicted.add(entity); } } + tracking.entities.removeAll(evicted); + tracking.forcedItems.removeAll(evicted); tracking.entities.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); @@ -222,4 +228,11 @@ public final class ThoraxisUndersideManager { public static boolean isAboveUndersideExit(Entity entity) { 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; + } } diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/gravity/AbstractArrowGravityMixin.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/gravity/AbstractArrowGravityMixin.java index 86dba05e..16089fe7 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/gravity/AbstractArrowGravityMixin.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/gravity/AbstractArrowGravityMixin.java @@ -1,16 +1,15 @@ package com.craisinlord.antarchy.mixins.gravity; 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.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.LivingEntity; import net.minecraft.world.entity.projectile.AbstractArrow; -import net.minecraft.world.phys.Vec3; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.ModifyVariable; @Mixin(AbstractArrow.class) /* @@ -18,20 +17,24 @@ import org.spongepowered.asm.mixin.injection.ModifyVariable; */ public abstract class AbstractArrowGravityMixin { - @ModifyVariable(method = "tick", at = @At(value = "STORE"), ordinal = 0) - private Vec3 antarchy$fixArrowGravityDroop(Vec3 velocity) { - AbstractArrow self = (AbstractArrow) (Object) this; + @WrapOperation(method = "tick", at = @At( + value = "INVOKE", + target = "Lnet/minecraft/world/entity/projectile/AbstractArrow;applyGravity()V")) + private void antarchy$applyArrowGravityUpward(AbstractArrow self, Operation original) { Entity owner = self.getOwner(); - if (!(owner instanceof LivingEntity living)) return velocity; - if (owner instanceof net.minecraft.world.entity.player.Player && !AntarchySettings.invertProjectilesFromInvertedPlayers()) { - return velocity; + if (!(owner instanceof LivingEntity living)) { + original.call(self); + return; } - AntarchyGravityDirection direction = AntarchyGravityApi.getGravityDirection(living); - if (!direction.isInverted()) return velocity; - velocity = new Vec3(velocity.x, velocity.y + 0.05D, velocity.z); - velocity = AntarchyGravityRotationUtil.vecWorldToPlayer(velocity, direction); - velocity = new Vec3(velocity.x, velocity.y - 0.05D, velocity.z); - velocity = AntarchyGravityRotationUtil.vecPlayerToWorld(velocity, direction); - return velocity; + if (owner instanceof net.minecraft.world.entity.player.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)); } } diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/gravity/BehaviorUtilsGravityMixin.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/gravity/BehaviorUtilsGravityMixin.java index 62b95f14..b9bd9065 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/gravity/BehaviorUtilsGravityMixin.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/gravity/BehaviorUtilsGravityMixin.java @@ -68,8 +68,6 @@ public abstract class BehaviorUtilsGravityMixin { } private static boolean antarchy$shouldInvertThrownItem(LivingEntity entity) { - return AntarchyGravityApi.isGravityInverted(entity) - && ThoraxisUndersideManager.isThoraxis(entity.level()) - && entity.getY() < ThoraxisUndersideManager.GRAVITY_FLIP_Y; + return ThoraxisUndersideManager.shouldInvertInUnderside(entity); } } diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/gravity/BucketItemGravityMixin.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/gravity/BucketItemGravityMixin.java index 9c552da2..4cbde7b7 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/gravity/BucketItemGravityMixin.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/gravity/BucketItemGravityMixin.java @@ -54,10 +54,6 @@ public abstract class BucketItemGravityMixin { } Direction face = blockHitResult.getDirection(); - if (face.getAxis() != Direction.Axis.Y) { - return; - } - BlockPos clickedPos = blockHitResult.getBlockPos(); BlockState clickedState = level.getBlockState(clickedPos); Block clickedBlock = clickedState.getBlock(); diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/gravity/CrossbowItemGravityMixin.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/gravity/CrossbowItemGravityMixin.java index ee7ebbe3..a66a1cd6 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/gravity/CrossbowItemGravityMixin.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/gravity/CrossbowItemGravityMixin.java @@ -4,6 +4,7 @@ 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.content.worldgen.thoraxis.ThoraxisUndersideManager; import com.llamalad7.mixinextras.injector.wrapoperation.Operation; import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; import net.minecraft.world.entity.LivingEntity; @@ -54,7 +55,8 @@ public abstract class CrossbowItemGravityMixin { } private static boolean antarchy$shouldFixProjectileSpawn(LivingEntity shooter) { - return !(shooter instanceof net.minecraft.world.entity.player.Player) - || AntarchySettings.invertProjectilesFromInvertedPlayers(); + return ThoraxisUndersideManager.shouldInvertInUnderside(shooter) + && (!(shooter instanceof net.minecraft.world.entity.player.Player) + || AntarchySettings.invertProjectilesFromInvertedPlayers()); } } diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/gravity/ThrowableProjectileGravityMixin.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/gravity/ThrowableProjectileGravityMixin.java index 2f637248..c659e752 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/gravity/ThrowableProjectileGravityMixin.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/gravity/ThrowableProjectileGravityMixin.java @@ -1,19 +1,41 @@ package com.craisinlord.antarchy.mixins.gravity; import com.craisinlord.antarchy.content.gravity.AntarchyGravityApi; -import com.craisinlord.antarchy.content.gravity.AntarchyGravityDirection; 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.LivingEntity; import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.projectile.ThrowableProjectile; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @Mixin(ThrowableProjectile.class) /* * Flips thrown projectile gravity for inverted shooters. */ 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 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)); + } } diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/time/EntityDeltaMovementTimeDilationMixin.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/time/EntityDeltaMovementTimeDilationMixin.java index 9f849d67..b3844f9d 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/time/EntityDeltaMovementTimeDilationMixin.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/time/EntityDeltaMovementTimeDilationMixin.java @@ -21,7 +21,7 @@ public abstract class EntityDeltaMovementTimeDilationMixin { } double rate = TimeDilationApi.getRate(entity); - if (Math.abs(rate - TimeDilationMath.NORMAL_RATE) < 0.001D) { + if (rate >= TimeDilationMath.NORMAL_RATE - 0.001D) { return nextMovement; } diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/time/LivingEntityRendererAnimationTimeDilationMixin.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/time/LivingEntityRendererAnimationTimeDilationMixin.java index e2d0d06c..4f303429 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/time/LivingEntityRendererAnimationTimeDilationMixin.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/time/LivingEntityRendererAnimationTimeDilationMixin.java @@ -58,6 +58,13 @@ public abstract class LivingEntityRendererAnimationTimeDilationMixin { 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) private static void antarchy$slowHurtOverlay(LivingEntity entity, float partialTick, CallbackInfoReturnable cir) { if (com.craisinlord.antarchy.content.time.TimeDilationApi.getRate(entity) < 1.0D diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/time/LivingEntityTimeDilationMixin.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/time/LivingEntityTimeDilationMixin.java index fb419183..84e54b67 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/time/LivingEntityTimeDilationMixin.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/time/LivingEntityTimeDilationMixin.java @@ -1,6 +1,7 @@ package com.craisinlord.antarchy.mixins.time; import com.craisinlord.antarchy.content.time.TimeDilationApi; +import com.craisinlord.antarchy.content.time.TimeDilationManager; import net.minecraft.world.InteractionHand; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.LivingEntity; @@ -11,6 +12,11 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @Mixin(LivingEntity.class) 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) private void antarchy$slowSwingTime(CallbackInfo ci) { Entity entity = (Entity) (Object) this; diff --git a/common-1.21.1/src/main/resources/assets/antarchy/lang/en_us.json b/common-1.21.1/src/main/resources/assets/antarchy/lang/en_us.json index 683acb57..b9d4c043 100644 --- a/common-1.21.1/src/main/resources/assets/antarchy/lang/en_us.json +++ b/common-1.21.1/src/main/resources/assets/antarchy/lang/en_us.json @@ -543,7 +543,7 @@ "item.antarchy.royal_assailant_battleaxe": "Royal Assailant Battle Axe", "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_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_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.", @@ -1047,6 +1047,9 @@ "subtitles.antarchy.king_beam_shoot": "The King fires a beam", "subtitles.antarchy.king_fireball_shoot": "The King breathes fire", "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_walk": "The Queen's footfalls", "subtitles.antarchy.queen_fly_loop": "The Queen's wings beat", diff --git a/common-1.21.1/src/main/resources/assets/antarchy/sounds.json b/common-1.21.1/src/main/resources/assets/antarchy/sounds.json index db87120b..0c1d9f8d 100644 --- a/common-1.21.1/src/main/resources/assets/antarchy/sounds.json +++ b/common-1.21.1/src/main/resources/assets/antarchy/sounds.json @@ -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": { "subtitle": "subtitles.antarchy.queen_idle", "sounds": [ diff --git a/common-1.21.1/src/main/resources/assets/antarchy/textures/entity/queen/queen_beam_end_1.png b/common-1.21.1/src/main/resources/assets/antarchy/textures/entity/queen/queen_beam_end_1.png deleted file mode 100644 index c3f6118ee8cd61536fcc1bd3372307426f16d807..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1907 zcmah~`#;nBAAj%D#%AFor-dluOir9oTtZnRmnaqC6mpK@ zxKGYpaxhFIMK>l>6u!UyWNz~_V`sQM;_@c5Eu&gW_<+;H4H{aQPn(idb-B0y zm=5sYjpzQct;IDa{_YT?J0hAD70wEeiYf_D^Sl`5H7^~oVJ%3ed)gyZI@eZ~MQ_Lp zDbH%%IIL1}KK0akht$6=Ajdt?Q#8Z$5bLwf=!JW~|SAOhX35u|i+_ z*?VD$CQB^T-oxssek_Xhb~6GUTr0G&S!9KMYR}p!b+?zK!VFmG1gq%E^WR9anb;UJ!OsUlb(_n2YivHKXwdb3q|m|4K?7O?g!O zsh0P6yf@Yc@iNk_l@{=dZs}&k339wSSc!sv&sAyk0XhlV%Su>Xb6cILYGAY3dUmT3 zN>F>YJYir6)@&gcr1F z;C{shK~#w+<)qrntX;~xs~$fHo%%IUF7R5n0Cm0<#@F@yM~c+t>QazD)tnqN)^O{# z3kW`hAZjBW(0ig|%9k9E-n_eS)aTvE!O}-RfRlHH$*$fPEc8X1-ed2&V*4y|LDSMi zWX9z=scg4CK9;T1$IJ2o`wVC1vtn}-9`XBmPggD*nue`(*#d)rqt~v-&a9O>`XqX| zFBjjn3HoVziJ%^-2E>jA2F%Gsm(|7yRmF*pDv@D=N6=}Z*+hc%^x^|S3b?W)Z5tU{ z|A&x{^|_UxOdoapAwG8fz0^sEP{q_?nXyfI^(Uy~-<#xWoB@vKWO|xkZaa7dlXvX$ zk+U)G)@>wFFL=U(YINQI16V(!rUC5qwe4xd)j=Ov5(`0a!oa8*VaDAFY zDonL^QE3xpuugfXwAHrf3a){s<%P)A#pVO+{YOl)3^JQ$X( zFJm`h-Fft3&4Mq;pOIj{lgxIuc&K>qjW`s(za~;$1Eb4Gnff;m^W6Rf;(6}dxIPmc z@r$I6Hc1+v57`@71+t0cNNIZY*ge5-&`f1`=)c~wjq-805gjJ z>3w2D&#k~uqE`{al_h_z={dKAOz}w7wzxhLNAfkRl;KUcfQQMRF$Afq1g|R7STpU> z>~+)~}i&NCk zmI5JdMC;slInHmEjCYP<7+6&W>M(ly5RL_HgYr$1zI{&P@b5C-~XVGTIq=VW|aHMpbJ_vANh{=8HAM;|~&OG+qM|I$u; z3m@Ep`Cj*N0`GJRHxOZ<$1b%r;gsX=BgK%b!&~oQ8lY1wF%>8{=dW@}HK@r835mh# zT9N3rEJTp~$KfR$F@#%lwg>8uYHg93aE6h=@AFW+`S`dIIv`;tdNWZk17DM-s{nV) zn^F^OzW8C>hZ6|okA=tSIASGPu~7t}54l&?7;_luDp6@DI5qpj7b<8p+%ZYO>T2jU zN~%DVhh6_=9MRMvs`dn8E-#X|V$AW6pF1mp{h|8f6VoD?j_9~@Y7XBskZzPsBiPi)&R|v(-RofGzog&sYTuP_)ATt%Dn1n$UV{2)xwIoa} zgQ{_vro=KxNjpf1L5p;1D^*cj)Yg*PDwUglyC3JnIp=xb^PKm*-`-4jw=*h=8j1h_ z74lh!^YWSWsgMYH@3!`P3IJk>>|lH0XV~f;E5*x>=+w873jVr%IK|`s!83;iM4Sd{ z7A`oHL8UESQdZ7UDM-#t^X}5LPV@h;y|yoU)wSENZ$e~1;x#Tf=Rd}~k z^fpj-5ZPJ!x_NJ^xVOAoI11+m z*7;a%aXx+Vj9W>&uF`g1nd^MO_3_w|babOC9B|OA(D;O}s(r|EP>#)()$r*ufLG|k z4`mY%jGQ1GGXi zx~s2*mopIAY~^(2MG$RJ>TOg7@L%f&~MnEpJY-*7{AK~Mh&gTF;QJ+Oj5wFb# z7;iQI?zB8Sr3G(>CtX0MoSh7$4VHa-7t?$)UN93tb5$8L>qH(s1Y%p1lYJ8o{CBsC z_P^9(5XutG6ILC}QV^p72SH!N(W zl(eM}oVI8v7#o$wLAT%)b>m^vAKT*>i!qcB(y0VB`_tBRlcGIGswb!})W$vCdFRLT zWyp{NiZctfGKF>O`h0tl?P1=?i_+TN4&;9zKE*O#Pd3>4_TWuHyU|84>BL(E+i|d+ zcvPg2oC&V&&kbwFoQN@r+5Fq0uNuK+$F}5dPU@IoK(8&XVS2AVVg1n-(S|otp+>xH zjWJN<2h}dqkH9Gb=TIc!8pUs0GsX+(h>Y*YpQ0QiWdSb0(`e*cn@dhq`M8AlCUJ0Cg zvS+R~%)9Imce41$Vc|vgW-*pUU1Di=@!$j@%}d&ZLxx~MQ9kz3Ww+b`8#nwn+TmBM(R%69-P1;0PpbJs_jqO}Xt<0xW6yrB5*_jLEHlEv{QrQ|%YyMYjj4E@kq zp`oQ-1GDl=dg-}CA#cQt}x}I(lloJm5lD<01Abm zM)*9yZLn_d)=HLqv$w&@SUBJj|9COgA?-jHBa_7R@@8J7@PV@m-9r*$w9slx)dkZ{ z8&BHDBEMlmBbCRwFTcN0Q~rrwzfj)5WANht_-{{t4_od;tFHRbXk<3TH%0gKwn^)? zDEl8}Q`S5jmrV}+)do5ae>Yxv%ExYIwHyjpdpxs;C56L<4~$`DjwU~MKF{lum)OQ)$mmVM*?@bgmI>#Lg`E zOBCK9f;(9V=-TQnxez+LZF0>Sa4{;L=io>uueE5sq{$3Ktv(DQaH1b!NudrmM}Gk~ zFrJToKvh^&K3*mRTO?!jcj%eo1U^bH3GcwAe+Lgv4PiaRlF*etTtirkoyFN7PGG=}`P!5C(#&yI@6k{Bs>#*09i>c~SY zR0XkXXvGD%p_WGrp=U}z-V>h%ZkccX2^h+F^C}m5rWp|=CW9g5KLTz@UX2QB=BxQT zWnaTAwP$NAbdzdXF<0cSQ!;8NS)kr-x}S$lQq`%a+r!%wWA(+(;B(c8t&K1k05Vz3 hhfFnj^dUZF-e-Vm2GeS+;*;eJAUnD_)Z0;S{0|?xTDkxL diff --git a/common-1.21.1/src/main/resources/assets/antarchy/textures/entity/queen/queen_beam_inner.png b/common-1.21.1/src/main/resources/assets/antarchy/textures/entity/queen/queen_beam_inner.png deleted file mode 100644 index 7658c7b846b8f71dd3f420cdbe7b28357186b316..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 314 zcmV-A0mc4_P)eMU}$Wp2u5NFC*SDEzU59~y#1N|_U;Xv^|hki zw${V`qszJ0EcZC^GJ}Q^e$Q7(I$`z*FrO`CpH50yu1Yx_m5%r4Es$qy@j}?cw+%trd-RL4S53q0RR8xkaRf!000I_L_t&o08;XHo)7eE$^ZZW M07*qoM6N<$g2p(3;s5{u diff --git a/common-1.21.1/src/main/resources/assets/antarchy/textures/entity/queen/queen_beam_outer.png b/common-1.21.1/src/main/resources/assets/antarchy/textures/entity/queen/queen_beam_outer.png deleted file mode 100644 index 74143aa67337a1dc50dc9007a6111d14f9373098..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 393 zcmV;40e1e0P)s=YPm_%k|4ie28U-i(tw;SPZ!6Kid(Ly z9QlqIa5!IVOYA7z`M>ft+tQ6k9@!UuT@ahzF!Q%!3#+8uk6C{v-(mXd@BQnp>={)ZEQFL+LXo$F>!_qFqkC^R<>Q X%VIgOlI61x&{hUdS3j3^P6 AntarchyFabricEntities.WIND_VORTEX.get()); AntarchyObjects.setTimeDilationField(() -> AntarchyFabricEntities.TIME_DILATION_FIELD.get()); AntarchyObjects.setRoyalBlackHole(() -> AntarchyFabricEntities.ROYAL_BLACK_HOLE.get()); + AntarchyObjects.setRoyalAssailantBlackHole(() -> AntarchyFabricEntities.ROYAL_ASSAILANT_BLACK_HOLE.get()); AntarchyObjects.setVortexChargeProjectile(() -> AntarchyFabricEntities.VORTEX_CHARGE_PROJECTILE.get()); AntarchyObjects.setPrince(() -> AntarchyFabricEntities.PRINCE.get()); AntarchyObjects.setPrincess(() -> AntarchyFabricEntities.PRINCESS.get()); diff --git a/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/client/AntarchyFabricClientBootstrap.java b/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/client/AntarchyFabricClientBootstrap.java index 9730e8d9..f75efe4b 100644 --- a/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/client/AntarchyFabricClientBootstrap.java +++ b/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/client/AntarchyFabricClientBootstrap.java @@ -148,6 +148,7 @@ public final class AntarchyFabricClientBootstrap { EntityRendererRegistry.register(AntarchyFabricEntities.WIND_VORTEX.get(), WindVortexRenderer::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_ASSAILANT_BLACK_HOLE.get(), com.craisinlord.antarchy.content.client.renderer.RoyalAssailantBlackHoleRenderer::new); EntityRendererRegistry.register(AntarchyFabricEntities.CRITTER_CAGE_PROJECTILE.get(), ThrownItemRenderer::new); EntityRendererRegistry.register(AntarchyFabricEntities.WORM_HOOK_PROJECTILE.get(), ThrownItemRenderer::new); EntityRendererRegistry.register(AntarchyFabricEntities.HUSH_PROJECTILE.get(), HushProjectileRenderer::new); diff --git a/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/content/fluid/AntiwaterFluid.java b/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/content/fluid/AntiwaterFluid.java index 9b32a774..06ff2046 100644 --- a/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/content/fluid/AntiwaterFluid.java +++ b/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/content/fluid/AntiwaterFluid.java @@ -1,4 +1,5 @@ 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.AntarchyFabricItems; import com.craisinlord.antarchy.fabric.registry.AntarchyFabricMisc; @@ -169,6 +170,8 @@ public abstract class AntiwaterFluid extends FlowingFluid { return; } + AntiwaterBubbleColumnUtil.update(level, pos, state); + if (!state.isSource() && !this.isFlowingStateValid(level, pos, state)) { level.setBlockAndUpdate(pos, net.minecraft.world.level.block.Blocks.AIR.defaultBlockState()); this.scheduleCleanup(level, pos); diff --git a/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/registry/AntarchyFabricEntities.java b/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/registry/AntarchyFabricEntities.java index 43ef66f1..472914ce 100644 --- a/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/registry/AntarchyFabricEntities.java +++ b/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/registry/AntarchyFabricEntities.java @@ -484,6 +484,13 @@ public final class AntarchyFabricEntities { .updateInterval(2) .fireImmune() .build("royal_black_hole")); + public static final DeferredHolder, EntityType> ROYAL_ASSAILANT_BLACK_HOLE = ENTITY_TYPES.register("royal_assailant_black_hole", + () -> EntityType.Builder.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> LUCID_BOLT = ENTITY_TYPES.register("lucid_bolt", diff --git a/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/registry/AntarchyFabricSounds.java b/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/registry/AntarchyFabricSounds.java index 267aa2c1..eb1634cc 100644 --- a/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/registry/AntarchyFabricSounds.java +++ b/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/registry/AntarchyFabricSounds.java @@ -543,6 +543,9 @@ public final class AntarchyFabricSounds { public static final DeferredHolder KING_BEAM_SHOOT = registerSoundEvent("king_beam_shoot"); public static final DeferredHolder KING_FIREBALL_SHOOT = registerSoundEvent("king_fireball_shoot"); public static final DeferredHolder KING_DECREE_CAST = registerSoundEvent("king_decree_cast"); + public static final DeferredHolder KING_DECREE = registerSoundEvent("king_decree"); + public static final DeferredHolder KING_SUCCESS = registerSoundEvent("king_success"); + public static final DeferredHolder KING_JUDGEMENT = registerSoundEvent("king_judgement"); public static final DeferredHolder QUEEN_IDLE = registerSoundEvent("queen_idle"); public static final DeferredHolder QUEEN_WALK = registerSoundEvent("queen_walk"); diff --git a/forge-1.20.1/src/main/java/com/craisinlord/antarchy/forge/registry/AntarchyForgeMisc.java b/forge-1.20.1/src/main/java/com/craisinlord/antarchy/forge/registry/AntarchyForgeMisc.java index 7eeeaf32..2252ade7 100644 --- a/forge-1.20.1/src/main/java/com/craisinlord/antarchy/forge/registry/AntarchyForgeMisc.java +++ b/forge-1.20.1/src/main/java/com/craisinlord/antarchy/forge/registry/AntarchyForgeMisc.java @@ -337,9 +337,9 @@ public final class AntarchyForgeMisc { // Biome sources public static final RegistryObject> ELYTHIA_BIOME_SOURCE = BIOME_SOURCES.register("elythia_biome_source", - () -> ElythiaBiomeSource.CODEC.codec()); + () -> ElythiaBiomeSource.CODEC); public static final RegistryObject> THORAXIS_BIOME_SOURCE = BIOME_SOURCES.register("thoraxis_biome_source", - () -> ThoraxisBiomeSource.CODEC.codec()); + () -> ThoraxisBiomeSource.CODEC); // Density function public static final RegistryObject> ELYTHIA_RIVER_CARVE = DENSITY_FUNCTION_TYPES.register("elythia_river_carve", diff --git a/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/AntarchyConfigModuleNeoforge.java b/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/AntarchyConfigModuleNeoforge.java index 7db0d17f..573239ef 100644 --- a/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/AntarchyConfigModuleNeoforge.java +++ b/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/AntarchyConfigModuleNeoforge.java @@ -159,6 +159,10 @@ public final class AntarchyConfigModuleNeoforge { AntarchySettings.setQueenBeamTracking(AntarchyMobsConfig.queenBeamTracking()); AntarchySettings.setQueenBeamTerrainRadius(AntarchyMobsConfig.queenBeamTerrainRadius()); 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.setKingBeamRange(AntarchyMobsConfig.kingBeamRange()); AntarchySettings.setKingBeamMinimumRange(AntarchyMobsConfig.kingBeamMinimumRange()); @@ -178,6 +182,7 @@ public final class AntarchyConfigModuleNeoforge { AntarchySettings.setQueenBlackHoleRadius(AntarchyMobsConfig.queenBlackHoleRadius()); AntarchySettings.setQueenBlackHoleActiveTicks(AntarchyMobsConfig.queenBlackHoleActiveTicks()); AntarchySettings.setQueenBlackHolePullStrength(AntarchyMobsConfig.queenBlackHolePullStrength()); + AntarchySettings.setQueenBlackHoleBlockSuctionCap(AntarchyMobsConfig.queenBlackHoleBlockSuctionCap()); AntarchySettings.setQueenFinalTimeFieldRadius(AntarchyMobsConfig.queenFinalTimeFieldRadius()); AntarchySettings.setQueenFinalTimeFieldRate(AntarchyMobsConfig.queenFinalTimeFieldRate()); AntarchySettings.setQueenFinalTimeFieldDurationTicks(AntarchyMobsConfig.queenFinalTimeFieldDurationTicks()); diff --git a/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/AntarchyMobsConfig.java b/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/AntarchyMobsConfig.java index 8216313f..cf21481d 100644 --- a/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/AntarchyMobsConfig.java +++ b/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/AntarchyMobsConfig.java @@ -125,7 +125,9 @@ public final class AntarchyMobsConfig { private static final ModConfigSpec.DoubleValue ROYAL_BOSS_BITE_DAMAGE_MULTIPLIER; 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.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.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; @@ -134,7 +136,7 @@ public final class AntarchyMobsConfig { KING_ROYAL_MUSTER_DURATION_TICKS, KING_ROYAL_MUSTER_CAP; private static final ModConfigSpec.DoubleValue QUEEN_BLACK_HOLE_RADIUS, QUEEN_BLACK_HOLE_PULL_STRENGTH, 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; 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; @@ -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_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_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_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); @@ -596,6 +598,10 @@ public final class AntarchyMobsConfig { 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_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_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); @@ -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_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_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_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_RATE = b.defineInRange("queenFinalTimeFieldRate", 0.05D, 0.05D, 1.0D); 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 queenBeamTerrainRadius() { return QUEEN_BEAM_TERRAIN_RADIUS.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 kingBeamRange() { return KING_BEAM_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 int queenBlackHoleActiveTicks() { return QUEEN_BLACK_HOLE_ACTIVE_TICKS.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 queenFinalTimeFieldRate() { return QUEEN_FINAL_TIME_FIELD_RATE.get(); } static int queenFinalTimeFieldDurationTicks() { return QUEEN_FINAL_TIME_FIELD_DURATION_TICKS.get(); } diff --git a/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/AntarchyNeoforge.java b/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/AntarchyNeoforge.java index 14a010d4..f7abd906 100644 --- a/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/AntarchyNeoforge.java +++ b/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/AntarchyNeoforge.java @@ -231,7 +231,10 @@ public class AntarchyNeoforge { AntarchyNeoforgeSounds.PRINCESS_BITE, AntarchyNeoforgeSounds.PRINCESS_SHOOT, AntarchyNeoforgeSounds.PRINCESS_FLY, - AntarchyNeoforgeSounds.PRINCESS_STEP + AntarchyNeoforgeSounds.PRINCESS_STEP, + AntarchyNeoforgeSounds.KING_DECREE, + AntarchyNeoforgeSounds.KING_SUCCESS, + AntarchyNeoforgeSounds.KING_JUDGEMENT ); AntarchySoundEvents.bindDimensionalTearSounds( AntarchyNeoforgeSounds.DIMENSIONAL_TEAR_OPEN, @@ -430,6 +433,7 @@ public class AntarchyNeoforge { AntarchyObjects.setWindVortex(() -> AntarchyNeoforgeEntites.WIND_VORTEX.get()); AntarchyObjects.setTimeDilationField(() -> AntarchyNeoforgeEntites.TIME_DILATION_FIELD.get()); AntarchyObjects.setRoyalBlackHole(() -> AntarchyNeoforgeEntites.ROYAL_BLACK_HOLE.get()); + AntarchyObjects.setRoyalAssailantBlackHole(() -> AntarchyNeoforgeEntites.ROYAL_ASSAILANT_BLACK_HOLE.get()); AntarchyObjects.setVortexChargeProjectile(() -> AntarchyNeoforgeEntites.VORTEX_CHARGE_PROJECTILE.get()); AntarchyObjects.setPrince(() -> AntarchyNeoforgeEntites.PRINCE.get()); AntarchyObjects.setPrincess(() -> AntarchyNeoforgeEntites.PRINCESS.get()); diff --git a/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/client/AntarchyNeoForgeClient.java b/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/client/AntarchyNeoForgeClient.java index 5a8f2ba2..97a333c4 100644 --- a/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/client/AntarchyNeoForgeClient.java +++ b/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/client/AntarchyNeoForgeClient.java @@ -151,6 +151,7 @@ public final class AntarchyNeoForgeClient { event.registerEntityRenderer(AntarchyNeoforgeEntites.WIND_VORTEX.get(), WindVortexRenderer::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_ASSAILANT_BLACK_HOLE.get(), com.craisinlord.antarchy.content.client.renderer.RoyalAssailantBlackHoleRenderer::new); event.registerEntityRenderer(AntarchyNeoforgeEntites.CRITTER_CAGE_PROJECTILE.get(), ThrownItemRenderer::new); event.registerEntityRenderer(AntarchyNeoforgeEntites.WORM_HOOK_PROJECTILE.get(), ThrownItemRenderer::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.WATER_BOMB.get(), WaterBombRenderer::new); 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.OURANWOOD_DEER.get(), context -> new com.craisinlord.antarchy.content.client.renderer.OuranwoodDeerRenderer(context)); + event.registerEntityRenderer(AntarchyNeoforgeEntites.DORRIE.get(), context -> withParalyzedGeoLayer(new com.craisinlord.antarchy.content.client.renderer.DorrieRenderer(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.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.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))); @@ -196,6 +197,8 @@ public final class AntarchyNeoForgeClient { renderer.addLayer(new TigerEyeCamouflagePlayerLayer(renderer)); renderer.addLayer(new GoopedLivingLayer(renderer)); renderer.addLayer(new GlimmeringLivingLayer(renderer)); + renderer.addLayer(new TemporalContractionAfterimageLayer<>(renderer)); + renderer.addLayer(new RoyalAssailantAxeAfterimageLayer(renderer)); renderer.addLayer(new BrutalflyElytraLayer(renderer)); renderer.addLayer(new com.craisinlord.antarchy.content.client.renderer.ManticoreWingsLayer(renderer)); renderer.addLayer(new FallenKingCrownLayer(renderer)); @@ -235,11 +238,13 @@ public final class AntarchyNeoForgeClient { ((LivingEntityRenderer) livingRenderer).addLayer(new ParalyzedStoneLivingLayer<>(livingRenderer)); ((LivingEntityRenderer) livingRenderer).addLayer(new GoopedLivingLayer<>(livingRenderer)); ((LivingEntityRenderer) livingRenderer).addLayer(new GlimmeringLivingLayer<>(livingRenderer)); + ((LivingEntityRenderer) livingRenderer).addLayer(new TemporalContractionAfterimageLayer<>(livingRenderer)); } private static GeoEntityRenderer withParalyzedGeoLayer(GeoEntityRenderer renderer) { renderer.addRenderLayer(new ParalyzedStoneGeoLayer<>(renderer)); renderer.addRenderLayer(new GoopedGeoLayer<>(renderer)); + renderer.addRenderLayer(new TemporalContractionGeoLayer<>(renderer)); return renderer; } diff --git a/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/content/fluid/AntiwaterFluid.java b/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/content/fluid/AntiwaterFluid.java index 61d1829a..c685a1e8 100644 --- a/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/content/fluid/AntiwaterFluid.java +++ b/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/content/fluid/AntiwaterFluid.java @@ -1,5 +1,6 @@ package com.craisinlord.antarchy.neoforge.content.fluid; +import com.craisinlord.antarchy.content.fluid.AntiwaterBubbleColumnUtil; import com.craisinlord.antarchy.neoforge.registry.AntarchyNeoforgeBlocks; import java.util.HashSet; import java.util.Set; @@ -173,6 +174,8 @@ public abstract class AntiwaterFluid extends BaseFlowingFluid { return; } + AntiwaterBubbleColumnUtil.update(level, pos, state); + if (!state.isSource() && !this.isFlowingStateValid(level, pos, state)) { level.setBlockAndUpdate(pos, net.minecraft.world.level.block.Blocks.AIR.defaultBlockState()); this.scheduleCleanup(level, pos); diff --git a/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/registry/AntarchyNeoforgeEntites.java b/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/registry/AntarchyNeoforgeEntites.java index 39ecf4b2..4f240e90 100644 --- a/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/registry/AntarchyNeoforgeEntites.java +++ b/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/registry/AntarchyNeoforgeEntites.java @@ -393,6 +393,13 @@ public final class AntarchyNeoforgeEntites { .updateInterval(2) .fireImmune() .build("royal_black_hole")); + public static final DeferredHolder, EntityType> ROYAL_ASSAILANT_BLACK_HOLE = ENTITY_TYPES.register("royal_assailant_black_hole", + () -> EntityType.Builder.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> TIME_DILATION_FIELD = ENTITY_TYPES.register("time_dilation_field", () -> EntityType.Builder.of(TimeDilationFieldEntity::new, MobCategory.MISC) .sized(0.5F, 0.5F) @@ -497,7 +504,7 @@ public final class AntarchyNeoforgeEntites { .build("lurking_terror")); public static final DeferredHolder, EntityType> MANTICORE = ENTITY_TYPES.register("manticore", () -> EntityType.Builder.of(ManticoreEntity::new, MobCategory.MONSTER) - .sized(1.95F, 2.0F) + .sized(1.95F, 2.25F) .clientTrackingRange(10) .fireImmune() .build("manticore")); diff --git a/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/registry/AntarchyNeoforgeSounds.java b/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/registry/AntarchyNeoforgeSounds.java index 1213a4a6..2b19f989 100644 --- a/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/registry/AntarchyNeoforgeSounds.java +++ b/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/registry/AntarchyNeoforgeSounds.java @@ -222,6 +222,9 @@ public final class AntarchyNeoforgeSounds { public static final DeferredHolder KING_BEAM_SHOOT = registerSoundEvent("king_beam_shoot"); public static final DeferredHolder KING_FIREBALL_SHOOT = registerSoundEvent("king_fireball_shoot"); public static final DeferredHolder KING_DECREE_CAST = registerSoundEvent("king_decree_cast"); + public static final DeferredHolder KING_DECREE = registerSoundEvent("king_decree"); + public static final DeferredHolder KING_SUCCESS = registerSoundEvent("king_success"); + public static final DeferredHolder KING_JUDGEMENT = registerSoundEvent("king_judgement"); public static final DeferredHolder QUEEN_IDLE = registerSoundEvent("queen_idle"); public static final DeferredHolder QUEEN_WALK = registerSoundEvent("queen_walk");