From 68eba61022e146e77f1d37392095757d4cc6b6b4 Mon Sep 17 00:00:00 2001 From: craisinlord Date: Mon, 7 Sep 2026 21:56:21 -0500 Subject: [PATCH] big king and queen dev --- .../antarchy/config/AntarchySettings.java | 2 + .../content/entity/ReverieEntity.java | 3 +- .../content/entity/StinkBugEntity.java | 14 +- .../content/entity/kraken/KrakenEntity.java | 3 +- .../content/item/KrakensGraspItem.java | 2 + .../blockstates/dimensional_tear_marker.json | 7 + .../blockstates/portal_gun_portal_base.json | 7 + .../blockstates/portal_gun_portal_master.json | 7 + .../antarchy/geo/fallen_king_crown.geo.json | 4 +- .../antarchy/geo/krakens_grasp.geo.json | 4 +- .../assets/antarchy/geo/rpo_launcher.geo.json | 4 +- .../assets/antarchy/geo/water_cannon.geo.json | 4 +- .../models/block/nadir_hanging_sign.json | 2 +- .../models/block/nadir_wall_hanging_sign.json | 2 +- .../antarchy/config/AntarchySettings.java | 75 ++- .../antarchy/content/AntarchyObjects.java | 12 + .../antarchy/content/AntarchySoundEvents.java | 3 + .../antarchy/content/AntarchyTags.java | 2 + .../DimensionalTearMarkerBlockEntity.java | 32 +- .../client/ClientTimeDilationTicker.java | 6 + .../client/model/RoyalBlackHoleModel.java | 23 + .../model/RoyalElementalProjectileModel.java | 26 + .../client/model/RoyalIceSpikeModel.java | 23 + .../renderer/RoyalBlackHoleRenderer.java | 30 + .../client/renderer/RoyalBossRenderer.java | 4 +- .../RoyalElementalProjectileRenderer.java | 38 ++ .../renderer/RoyalIceSpikeRenderer.java | 14 + .../client/renderer/WindVortexRenderer.java | 102 ++-- .../content/creative/CreativeTabContents.java | 3 +- .../content/effect/CommandedBehavior.java | 99 ++++ .../content/effect/CommandedEntityAccess.java | 10 + .../content/effect/CommandedMobEffect.java | 25 + .../content/effect/DilatedMobEffect.java | 23 + .../content/effect/InvertedMobEffect.java | 16 + .../effect/RoyalEffectEligibility.java | 57 ++ .../content/effect/RoyalEffectHooks.java | 27 + .../antarchy/content/entity/BedBugEntity.java | 37 +- .../content/entity/DiamondMinecartEntity.java | 34 +- .../content/entity/ManticoreEntity.java | 36 ++ .../content/entity/ReverieEntity.java | 3 +- .../content/entity/ScorpionEntity.java | 31 +- .../content/entity/StinkBugEntity.java | 14 +- .../content/entity/ant/AntTeleportHelper.java | 7 +- .../content/entity/kraken/KrakenEntity.java | 3 +- .../entity/portal/DimensionalTearEntity.java | 31 +- .../content/entity/royal/KingEntity.java | 200 +++++-- .../content/entity/royal/QueenEntity.java | 225 ++++--- .../entity/royal/RoyalBlackHoleEntity.java | 41 +- .../content/entity/royal/RoyalBossEntity.java | 180 +++++- .../royal/RoyalElementalProjectileEntity.java | 199 +++++++ .../content/entity/royal/RoyalHead.java | 48 +- .../entity/royal/RoyalIceSpikeEntity.java | 98 ++++ .../entity/royal/attack/RoyalAttackLane.java | 12 + .../royal/attack/RoyalAttackScheduler.java | 123 ++++ .../royal/attack/RoyalEffectController.java | 54 ++ .../royal/beam/RoyalBeamController.java | 16 +- .../entity/royal/beam/RoyalBeamSettings.java | 3 + .../entity/royal/decree/AdvanceDecree.java | 8 +- .../royal/decree/ComeNoCloserDecree.java | 3 + .../entity/royal/decree/DoNotRunDecree.java | 5 +- .../royal/decree/FightMeCowardDecree.java | 4 + .../royal/decree/KeepYourDistanceDecree.java | 7 +- .../entity/royal/decree/KneelDecree.java | 2 +- .../entity/royal/decree/RoyalDecree.java | 10 + .../royal/decree/ShowNoMercyDecree.java | 7 +- .../entity/royal/decree/SkyIsMineDecree.java | 5 +- .../entity/royal/decree/StandTallDecree.java | 6 +- .../royal/decree/StandYourGroundDecree.java | 8 +- .../entity/vortex/WindVortexEntity.java | 150 ++++- .../gravity/GravityWalkNodeEvaluator.java | 64 +- .../content/item/EyeOfTheStormItem.java | 1 + .../content/item/KrakensGraspItem.java | 2 + .../content/item/ManticoreWingsItem.java | 12 +- .../content/item/RoyalAssailantArmorItem.java | 11 + .../content/time/TimeDilationApi.java | 12 + .../content/time/TimeDilationManager.java | 56 +- .../worldgen/elythia/ElythiaFloraFeature.java | 65 ++- .../elythia/OuranwoodTreeFeature.java | 91 ++- .../thoraxis/ThoraxisIchorLakeFeature.java | 63 ++ .../thoraxis/ThoraxisUndersideManager.java | 50 +- .../truffalo/TruffaloTreeConfiguration.java | 8 +- .../truffalo/TruffaloTreeFeature.java | 12 +- .../antarchy/mixins/CommandedMobMixin.java | 52 ++ .../mixins/LivingEntityRoyalEffectsMixin.java | 90 +++ .../mixins/MobCommandedTargetMixin.java | 29 + .../mixins/gravity/EntityGravityMixin.java | 79 +++ .../gravity/GroundPathNavigationMixin.java | 73 ++- .../gravity/MeleeAttackGoalGravityMixin.java | 101 ++++ .../gravity/MoveControlGravityMixin.java | 48 ++ .../gravity/RandomStrollGoalGravityMixin.java | 161 ++++++ .../LivingEntityTimeDilationCleanupMixin.java | 17 + .../resources/antarchy-common.mixins.json | 9 +- .../animations/black_hole.animation.json | 381 ++++++++++++ .../animations/fire_ball.animation.json | 33 ++ .../animations/ice_ball.animation.json | 56 ++ .../animations/ice_spike.animation.json | 363 ++++++++++++ .../blockstates/dimensional_tear_marker.json | 7 + .../blockstates/portal_gun_portal_base.json | 7 + .../blockstates/portal_gun_portal_master.json | 7 + .../antarchy/blockstates/typhonite_spike.json | 20 +- .../assets/antarchy/geo/black_hole.geo.json | 118 ++++ .../antarchy/geo/eye_of_the_storm.geo.json | 4 +- .../antarchy/geo/fallen_king_crown.geo.json | 4 +- .../assets/antarchy/geo/fire_ball.geo.json | 28 + .../assets/antarchy/geo/ice_ball.geo.json | 24 + .../assets/antarchy/geo/ice_spike.geo.json | 214 +++++++ .../antarchy/geo/krakens_grasp.geo.json | 4 +- .../assets/antarchy/geo/portal_gun.geo.json | 4 +- .../geo/royal_assailant_staff.geo.json | 4 +- .../geo/royal_guardian_sword.geo.json | 4 +- .../assets/antarchy/geo/rpo_launcher.geo.json | 4 +- .../assets/antarchy/geo/water_cannon.geo.json | 4 +- .../resources/assets/antarchy/lang/en_us.json | 20 + .../models/block/nadir_hanging_sign.json | 2 +- .../models/block/nadir_wall_hanging_sign.json | 2 +- .../block/typhonite_spike_down_base.json | 1 - .../block/typhonite_spike_down_frustum.json | 1 - .../block/typhonite_spike_down_middle.json | 1 - .../block/typhonite_spike_down_tip.json | 1 - .../block/typhonite_spike_down_tip_merge.json | 1 - .../textures/block/verdant_nyxium.png | Bin 634 -> 567 bytes .../textures/block/verdant_nyxium_old.png | Bin 0 -> 588 bytes .../textures/entity/king/fire_ball.png | Bin 0 -> 18283 bytes .../textures/entity/king/ice_ball.png | Bin 0 -> 1313 bytes .../textures/entity/king/ice_spike.png | Bin 0 -> 6720 bytes .../textures/entity/queen/black_hole.png | Bin 0 -> 83862 bytes .../entity/queen/black_hole.png.mcmeta | 5 + .../item/critter_cage_item_overlay.png | Bin 0 -> 267 bytes .../textures/mob_effect/commanded.png | Bin 0 -> 312 bytes .../antarchy/textures/mob_effect/dilated.png | Bin 0 -> 227 bytes .../antarchy/advancement/visit_cavaryn.json | 2 +- .../antarchy/advancement/visit_elythia.json | 2 +- .../antarchy/advancement/visit_thoraxis.json | 2 +- .../data/antarchy/dimension/elythia.json | 547 ++---------------- .../tags/entity_type/commanded_blacklist.json | 14 + .../tags/entity_type/dilated_blacklist.json | 6 + .../worldgen/biome/butterfly_fields.json | 2 +- .../antarchy/worldgen/biome/dream_dunes.json | 3 +- .../worldgen/biome/elythia_meadow.json | 2 +- .../antarchy/worldgen/biome/lucid_pools.json | 3 +- .../antarchy/worldgen/biome/nadir_forest.json | 1 + .../worldgen/biome/nightmare_wastes.json | 3 +- .../worldgen/biome/truffalo_forest.json | 3 +- .../antarchy/worldgen/biome/umbral_hills.json | 3 +- .../worldgen/biome/vortex_expanse.json | 3 +- .../thoraxis_ichor_lake.json | 4 + .../truffalo_large_tree.json | 21 + .../density_function/elythia_surface.json | 179 ++++-- .../noise/elythia_terrain_roughness.json | 9 + .../worldgen/noise_settings/elythia.json | 8 +- .../placed_feature/elythia_underground.json | 2 +- .../placed_feature/thoraxis_ichor_lake.json | 23 + .../truffalo_large_forest_trees.json | 19 + .../placed_feature/typhonite_spikes.json | 2 +- .../placed_feature/typhonite_veined_ore.json | 4 +- .../vortex_expanse_typhonite_spikes.json | 27 + .../antarchy/fabric/AntarchyFabric.java | 2 +- .../antarchy/fabric/AntarchyFabric.java | 2 +- .../fabric/AntarchyFabricContent.java | 7 + .../client/AntarchyFabricClientBootstrap.java | 4 +- .../registry/AntarchyFabricEntities.java | 15 + .../fabric/registry/AntarchyFabricMisc.java | 16 + .../AntarchyConfigModuleNeoforge.java | 21 + .../antarchy/neoforge/AntarchyMobsConfig.java | 59 +- .../antarchy/neoforge/AntarchyNeoforge.java | 3 + .../client/AntarchyNeoForgeClient.java | 4 +- .../registry/AntarchyNeoForgeEvents.java | 151 ++++- .../registry/AntarchyNeoforgeEntites.java | 9 + .../registry/AntarchyNeoforgeMisc.java | 15 + 169 files changed, 4897 insertions(+), 1060 deletions(-) create mode 100644 common-1.20.1/src/main/resources/assets/antarchy/blockstates/dimensional_tear_marker.json create mode 100644 common-1.20.1/src/main/resources/assets/antarchy/blockstates/portal_gun_portal_base.json create mode 100644 common-1.20.1/src/main/resources/assets/antarchy/blockstates/portal_gun_portal_master.json create mode 100644 common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/model/RoyalBlackHoleModel.java create mode 100644 common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/model/RoyalElementalProjectileModel.java create mode 100644 common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/model/RoyalIceSpikeModel.java create mode 100644 common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/RoyalBlackHoleRenderer.java create mode 100644 common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/RoyalElementalProjectileRenderer.java create mode 100644 common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/RoyalIceSpikeRenderer.java create mode 100644 common-1.21.1/src/main/java/com/craisinlord/antarchy/content/effect/CommandedBehavior.java create mode 100644 common-1.21.1/src/main/java/com/craisinlord/antarchy/content/effect/CommandedEntityAccess.java create mode 100644 common-1.21.1/src/main/java/com/craisinlord/antarchy/content/effect/CommandedMobEffect.java create mode 100644 common-1.21.1/src/main/java/com/craisinlord/antarchy/content/effect/DilatedMobEffect.java create mode 100644 common-1.21.1/src/main/java/com/craisinlord/antarchy/content/effect/RoyalEffectEligibility.java create mode 100644 common-1.21.1/src/main/java/com/craisinlord/antarchy/content/effect/RoyalEffectHooks.java create mode 100644 common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/RoyalElementalProjectileEntity.java create mode 100644 common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/RoyalIceSpikeEntity.java create mode 100644 common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/attack/RoyalAttackLane.java create mode 100644 common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/attack/RoyalAttackScheduler.java create mode 100644 common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/attack/RoyalEffectController.java create mode 100644 common-1.21.1/src/main/java/com/craisinlord/antarchy/content/worldgen/thoraxis/ThoraxisIchorLakeFeature.java create mode 100644 common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/CommandedMobMixin.java create mode 100644 common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/LivingEntityRoyalEffectsMixin.java create mode 100644 common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/MobCommandedTargetMixin.java create mode 100644 common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/gravity/MeleeAttackGoalGravityMixin.java create mode 100644 common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/gravity/MoveControlGravityMixin.java create mode 100644 common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/gravity/RandomStrollGoalGravityMixin.java create mode 100644 common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/time/LivingEntityTimeDilationCleanupMixin.java create mode 100644 common-1.21.1/src/main/resources/assets/antarchy/animations/black_hole.animation.json create mode 100644 common-1.21.1/src/main/resources/assets/antarchy/animations/fire_ball.animation.json create mode 100644 common-1.21.1/src/main/resources/assets/antarchy/animations/ice_ball.animation.json create mode 100644 common-1.21.1/src/main/resources/assets/antarchy/animations/ice_spike.animation.json create mode 100644 common-1.21.1/src/main/resources/assets/antarchy/blockstates/dimensional_tear_marker.json create mode 100644 common-1.21.1/src/main/resources/assets/antarchy/blockstates/portal_gun_portal_base.json create mode 100644 common-1.21.1/src/main/resources/assets/antarchy/blockstates/portal_gun_portal_master.json create mode 100644 common-1.21.1/src/main/resources/assets/antarchy/geo/black_hole.geo.json create mode 100644 common-1.21.1/src/main/resources/assets/antarchy/geo/fire_ball.geo.json create mode 100644 common-1.21.1/src/main/resources/assets/antarchy/geo/ice_ball.geo.json create mode 100644 common-1.21.1/src/main/resources/assets/antarchy/geo/ice_spike.geo.json create mode 100644 common-1.21.1/src/main/resources/assets/antarchy/textures/block/verdant_nyxium_old.png create mode 100644 common-1.21.1/src/main/resources/assets/antarchy/textures/entity/king/fire_ball.png create mode 100644 common-1.21.1/src/main/resources/assets/antarchy/textures/entity/king/ice_ball.png create mode 100644 common-1.21.1/src/main/resources/assets/antarchy/textures/entity/king/ice_spike.png create mode 100644 common-1.21.1/src/main/resources/assets/antarchy/textures/entity/queen/black_hole.png create mode 100644 common-1.21.1/src/main/resources/assets/antarchy/textures/entity/queen/black_hole.png.mcmeta create mode 100644 common-1.21.1/src/main/resources/assets/antarchy/textures/item/critter_cage_item_overlay.png create mode 100644 common-1.21.1/src/main/resources/assets/antarchy/textures/mob_effect/commanded.png create mode 100644 common-1.21.1/src/main/resources/assets/antarchy/textures/mob_effect/dilated.png create mode 100644 common-1.21.1/src/main/resources/data/antarchy/tags/entity_type/commanded_blacklist.json create mode 100644 common-1.21.1/src/main/resources/data/antarchy/tags/entity_type/dilated_blacklist.json create mode 100644 common-1.21.1/src/main/resources/data/antarchy/worldgen/configured_feature/thoraxis_ichor_lake.json create mode 100644 common-1.21.1/src/main/resources/data/antarchy/worldgen/configured_feature/truffalo_large_tree.json create mode 100644 common-1.21.1/src/main/resources/data/antarchy/worldgen/noise/elythia_terrain_roughness.json create mode 100644 common-1.21.1/src/main/resources/data/antarchy/worldgen/placed_feature/thoraxis_ichor_lake.json create mode 100644 common-1.21.1/src/main/resources/data/antarchy/worldgen/placed_feature/truffalo_large_forest_trees.json create mode 100644 common-1.21.1/src/main/resources/data/antarchy/worldgen/placed_feature/vortex_expanse_typhonite_spikes.json 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 df9e172e..664bafbc 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 @@ -1368,6 +1368,8 @@ public final class AntarchySettings { public static void setKrakensGraspAttackSpeed(double value) { krakensGraspAttackSpeed = value; } public static void setKrakensGraspThrownDamage(double value) { krakensGraspThrownDamage = value; } public static void setKrakensGraspLightningDamage(double value) { krakensGraspLightningDamage = value; } + + public static double krakensGraspLightningDamage() { return krakensGraspLightningDamage; } public static void setKrakensGraspInnateLoyalty(boolean value) { krakensGraspInnateLoyalty = value; } public static void setKrakensGraspInnateLoyaltyLevel(int value) { krakensGraspInnateLoyaltyLevel = value; } public static void setKrakensGraspTentacleDurationTicks(int value) { krakensGraspTentacleDurationTicks = value; } diff --git a/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/entity/ReverieEntity.java b/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/entity/ReverieEntity.java index ae691805..0c7c4abb 100644 --- a/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/entity/ReverieEntity.java +++ b/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/entity/ReverieEntity.java @@ -73,6 +73,7 @@ import software.bernie.geckolib.core.animation.RawAnimation; import software.bernie.geckolib.util.GeckoLibUtil; public class ReverieEntity extends PathfinderMob implements FlyingAnimal, GeoEntity { + private static final int DAMAGE_REACTION_DURATION_TICKS = 40; private static final EntityDataAccessor MOOD = SynchedEntityData.defineId(ReverieEntity.class, EntityDataSerializers.INT); private static final EntityDataAccessor ANIMATION_STATE = @@ -309,7 +310,7 @@ public class ReverieEntity extends PathfinderMob implements FlyingAnimal, GeoEnt this.releaseFocus(); this.clearNoticePlayer(); this.ambientTarget = null; - this.damageReactionTicks = 0; + this.damageReactionTicks = DAMAGE_REACTION_DURATION_TICKS; this.inversionReactionTicks = 0; this.transitionToMood(Mood.WHITE); } diff --git a/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/entity/StinkBugEntity.java b/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/entity/StinkBugEntity.java index 33db0795..468a74a3 100644 --- a/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/entity/StinkBugEntity.java +++ b/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/entity/StinkBugEntity.java @@ -15,6 +15,8 @@ import net.minecraft.world.entity.MobSpawnType; import net.minecraft.world.entity.ai.attributes.AttributeSupplier; import net.minecraft.world.entity.ai.attributes.Attributes; import net.minecraft.world.entity.ai.goal.FloatGoal; +import net.minecraft.world.entity.ai.goal.BreedGoal; +import net.minecraft.world.entity.ai.goal.TemptGoal; import net.minecraft.world.entity.ai.goal.LookAtPlayerGoal; import net.minecraft.world.entity.ai.goal.PanicGoal; import net.minecraft.world.entity.ai.goal.RandomLookAroundGoal; @@ -23,6 +25,8 @@ import net.minecraft.world.entity.animal.Animal; import net.minecraft.world.entity.player.Player; import net.minecraft.sounds.SoundEvent; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import net.minecraft.world.item.crafting.Ingredient; import net.minecraft.world.level.Level; import net.minecraft.world.level.ServerLevelAccessor; import org.jetbrains.annotations.Nullable; @@ -76,9 +80,11 @@ public class StinkBugEntity extends Animal implements GeoEntity { protected void registerGoals() { this.goalSelector.addGoal(0, new FloatGoal(this)); this.goalSelector.addGoal(1, new PanicGoal(this, 1.4D)); - this.goalSelector.addGoal(2, new WaterAvoidingRandomStrollGoal(this, 0.9D)); - this.goalSelector.addGoal(3, new LookAtPlayerGoal(this, Player.class, 6.0F)); - this.goalSelector.addGoal(4, new RandomLookAroundGoal(this)); + this.goalSelector.addGoal(2, new TemptGoal(this, 1.0D, Ingredient.of(Items.ROTTEN_FLESH), false)); + this.goalSelector.addGoal(3, new BreedGoal(this, 0.9D)); + this.goalSelector.addGoal(4, new WaterAvoidingRandomStrollGoal(this, 0.9D)); + this.goalSelector.addGoal(5, new LookAtPlayerGoal(this, Player.class, 6.0F)); + this.goalSelector.addGoal(6, new RandomLookAroundGoal(this)); } @Override @@ -108,7 +114,7 @@ public class StinkBugEntity extends Animal implements GeoEntity { @Override public boolean isFood(ItemStack stack) { - return false; + return stack.is(Items.ROTTEN_FLESH); } @Override diff --git a/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/entity/kraken/KrakenEntity.java b/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/entity/kraken/KrakenEntity.java index 79e63795..b91ce43c 100644 --- a/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/entity/kraken/KrakenEntity.java +++ b/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/entity/kraken/KrakenEntity.java @@ -1026,7 +1026,8 @@ public class KrakenEntity extends Monster implements GeoEntity, MultipartEntityO for (int i = 0; i < strikeBolts; i++) { this.spawnVisualLightning(serverLevel, this.getTargetStormStrikePos(target)); } - target.hurt(AntarchyDamageSources.krakenLightning(serverLevel, this), this.isPhaseTwo() ? 12.0F : 8.0F); + target.hurt(AntarchyDamageSources.krakenLightning(serverLevel, this), + this.isPhaseTwo() ? 12.0F : AntarchySettings.krakenLightningDamagePhaseOne()); } } else { this.lightningStrikeCooldown = this.isPhaseTwo() ? 30 : 60; diff --git a/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/item/KrakensGraspItem.java b/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/item/KrakensGraspItem.java index afaaaff4..ee0f6b2e 100644 --- a/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/item/KrakensGraspItem.java +++ b/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/item/KrakensGraspItem.java @@ -91,12 +91,14 @@ public class KrakensGraspItem extends TridentItem { public static void strikeLightning(LivingEntity target, ServerLevel level) { strikeLightningAt(level, target.getX(), target.getY(), target.getZ()); + target.hurt(level.damageSources().lightningBolt(), (float) AntarchySettings.krakensGraspLightningDamage()); level.sendParticles(ParticleTypes.ELECTRIC_SPARK, target.getX(), target.getY(0.7D), target.getZ(), 10, 0.25D, 0.25D, 0.25D, 0.02D); } public static void strikeLightningAt(ServerLevel level, double x, double y, double z) { LightningBolt lightningBolt = new LightningBolt(net.minecraft.world.entity.EntityType.LIGHTNING_BOLT, level); lightningBolt.moveTo(x, y, z); + lightningBolt.setVisualOnly(true); level.addFreshEntity(lightningBolt); level.sendParticles(ParticleTypes.ELECTRIC_SPARK, x, y + 0.2D, z, 10, 0.25D, 0.25D, 0.25D, 0.02D); } diff --git a/common-1.20.1/src/main/resources/assets/antarchy/blockstates/dimensional_tear_marker.json b/common-1.20.1/src/main/resources/assets/antarchy/blockstates/dimensional_tear_marker.json new file mode 100644 index 00000000..f17f93e2 --- /dev/null +++ b/common-1.20.1/src/main/resources/assets/antarchy/blockstates/dimensional_tear_marker.json @@ -0,0 +1,7 @@ +{ + "variants": { + "": { + "model": "minecraft:block/air" + } + } +} diff --git a/common-1.20.1/src/main/resources/assets/antarchy/blockstates/portal_gun_portal_base.json b/common-1.20.1/src/main/resources/assets/antarchy/blockstates/portal_gun_portal_base.json new file mode 100644 index 00000000..f17f93e2 --- /dev/null +++ b/common-1.20.1/src/main/resources/assets/antarchy/blockstates/portal_gun_portal_base.json @@ -0,0 +1,7 @@ +{ + "variants": { + "": { + "model": "minecraft:block/air" + } + } +} diff --git a/common-1.20.1/src/main/resources/assets/antarchy/blockstates/portal_gun_portal_master.json b/common-1.20.1/src/main/resources/assets/antarchy/blockstates/portal_gun_portal_master.json new file mode 100644 index 00000000..f17f93e2 --- /dev/null +++ b/common-1.20.1/src/main/resources/assets/antarchy/blockstates/portal_gun_portal_master.json @@ -0,0 +1,7 @@ +{ + "variants": { + "": { + "model": "minecraft:block/air" + } + } +} diff --git a/common-1.20.1/src/main/resources/assets/antarchy/geo/fallen_king_crown.geo.json b/common-1.20.1/src/main/resources/assets/antarchy/geo/fallen_king_crown.geo.json index c8f22646..afa9b466 100644 --- a/common-1.20.1/src/main/resources/assets/antarchy/geo/fallen_king_crown.geo.json +++ b/common-1.20.1/src/main/resources/assets/antarchy/geo/fallen_king_crown.geo.json @@ -1,5 +1,5 @@ { - "format_version": "1.21.110", + "format_version": "1.12.0", "minecraft:geometry": [ { "description": { @@ -80,4 +80,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/common-1.20.1/src/main/resources/assets/antarchy/geo/krakens_grasp.geo.json b/common-1.20.1/src/main/resources/assets/antarchy/geo/krakens_grasp.geo.json index a8e4f668..b3df7394 100644 --- a/common-1.20.1/src/main/resources/assets/antarchy/geo/krakens_grasp.geo.json +++ b/common-1.20.1/src/main/resources/assets/antarchy/geo/krakens_grasp.geo.json @@ -1,5 +1,5 @@ { - "format_version": "1.21.110", + "format_version": "1.12.0", "minecraft:geometry": [ { "description": { @@ -199,4 +199,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/common-1.20.1/src/main/resources/assets/antarchy/geo/rpo_launcher.geo.json b/common-1.20.1/src/main/resources/assets/antarchy/geo/rpo_launcher.geo.json index 07451faf..794ca38d 100644 --- a/common-1.20.1/src/main/resources/assets/antarchy/geo/rpo_launcher.geo.json +++ b/common-1.20.1/src/main/resources/assets/antarchy/geo/rpo_launcher.geo.json @@ -1,5 +1,5 @@ { - "format_version": "1.21.110", + "format_version": "1.12.0", "minecraft:geometry": [ { "description": { @@ -284,4 +284,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/common-1.20.1/src/main/resources/assets/antarchy/geo/water_cannon.geo.json b/common-1.20.1/src/main/resources/assets/antarchy/geo/water_cannon.geo.json index 4240f801..6b844883 100644 --- a/common-1.20.1/src/main/resources/assets/antarchy/geo/water_cannon.geo.json +++ b/common-1.20.1/src/main/resources/assets/antarchy/geo/water_cannon.geo.json @@ -1,5 +1,5 @@ { - "format_version": "1.21.110", + "format_version": "1.12.0", "minecraft:geometry": [ { "description": { @@ -92,4 +92,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/common-1.20.1/src/main/resources/assets/antarchy/models/block/nadir_hanging_sign.json b/common-1.20.1/src/main/resources/assets/antarchy/models/block/nadir_hanging_sign.json index c101c4af..cc1d471b 100644 --- a/common-1.20.1/src/main/resources/assets/antarchy/models/block/nadir_hanging_sign.json +++ b/common-1.20.1/src/main/resources/assets/antarchy/models/block/nadir_hanging_sign.json @@ -1,5 +1,5 @@ { "textures": { - "particle": "antarchy:block/nadir/nadir_hanging" + "particle": "antarchy:block/nadir/nadir_planks" } } diff --git a/common-1.20.1/src/main/resources/assets/antarchy/models/block/nadir_wall_hanging_sign.json b/common-1.20.1/src/main/resources/assets/antarchy/models/block/nadir_wall_hanging_sign.json index c101c4af..cc1d471b 100644 --- a/common-1.20.1/src/main/resources/assets/antarchy/models/block/nadir_wall_hanging_sign.json +++ b/common-1.20.1/src/main/resources/assets/antarchy/models/block/nadir_wall_hanging_sign.json @@ -1,5 +1,5 @@ { "textures": { - "particle": "antarchy:block/nadir/nadir_hanging" + "particle": "antarchy:block/nadir/nadir_planks" } } 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 7bfe5623..d475667c 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 @@ -91,11 +91,11 @@ public final class AntarchySettings { private static double bigBerthaNoneModeDamageBonusPercent = 15.0D; private static double bigBerthaNightmareDamageBonusPercent = 30.0D; private static double potentNyxiteInvertedDurationSeconds = 60.0D; - private static boolean dimensionalTearsSpawnNaturally = false; + private static boolean dimensionalTearsSpawnNaturally = true; private static int dimensionalTearLifetimeTicks = 24000; private static int dimensionalTearInvertedDurationTicks = 2400; - private static int dimensionalTearEmergenceMinIntervalTicks = 2400; - private static int dimensionalTearEmergenceMaxIntervalTicks = 7200; + private static int dimensionalTearEmergenceMinIntervalTicks = 400; + private static int dimensionalTearEmergenceMaxIntervalTicks = 1200; private static float dimensionalTearLucidEventChance = 0.65F; private static int ultimateToolEnchantability = 25; private static int ultimateArmorEnchantability = 10; @@ -250,6 +250,23 @@ public final class AntarchySettings { private static double queenHealth = 6000.0D; private static double kingAttackDamage = 50.0D; private static double queenAttackDamage = 45.0D; + private static double royalBossSoundVolume = 4.5D; + private static double queenBlackHoleRadius = 14.0D; + private static int queenBlackHoleActiveTicks = 120; + private static double queenBlackHolePullStrength = 0.16D; + private static double queenFinalTimeFieldRadius = 20.0D; + private static double queenFinalTimeFieldRate = 0.05D; + private static int queenFinalTimeFieldDurationTicks = 45; + private static int queenFinalTimeFieldCooldownTicks = 80; + private static double kingFireballDamage = 32.0D; + private static double kingFireballRadius = 4.5D; + private static int kingFireballCooldownTicks = 150; + private static double kingIceballDamage = 18.0D; + private static double kingIceballRadius = 5.0D; + private static int kingIceballCooldownTicks = 190; + private static double kingIceSpikeDamage = 24.0D; + private static int kingIceSpikeCooldownTicks = 240; + private static int kingElementalTerrainCap = 256; private static double royalBossArmor = 20.0D; private static double royalBossFollowRange = 128.0D; private static double royalBossMovementSpeed = 0.22D; @@ -391,7 +408,7 @@ public final class AntarchySettings { private static double toreterrorDamageRange = 25.0D; private static double brutalflyDamageRange = 25.0D; private static double alphaMantisDamageRange = 25.0D; - private static float krakenLightningDamagePhaseOne = 12.0F; + private static float krakenLightningDamagePhaseOne = 6.0F; private static double lucidHealth = 70.0D; private static double lucidAttackDamage = 4.0D; private static double lucidMovementSpeed = 0.22D; @@ -402,7 +419,7 @@ public final class AntarchySettings { private static double vortexMovementSpeed = 0.22D; private static double vortexFlyingSpeed = 0.35D; private static int vortexMaxActiveVortexes = 3; - private static int windVortexDurationTicks = 140; + private static int windVortexDurationTicks = 240; private static double windVortexPullStrength = 0.32D; private static double windVortexLaunchStrength = 1.0D; private static boolean eyeOfTheStormEnabled = true; @@ -534,6 +551,8 @@ public final class AntarchySettings { private static double queenBeamDamage = 45.0D; private static double queenBeamRange = 100.0D; private static int queenBeamDurationTicks = 30; + private static int queenBeamWindupTicks = 20; + private static int queenBeamTravelTicks = 12; private static int queenBeamCooldownTicks = 90; private static double queenBeamTracking = 0.1D; private static double queenBeamTerrainRadius = 5.0D; @@ -541,6 +560,8 @@ public final class AntarchySettings { private static double kingBeamDamage = 50.0D; private static double kingBeamRange = 100.0D; private static int kingBeamDurationTicks = 30; + private static int kingBeamWindupTicks = 20; + private static int kingBeamTravelTicks = 12; private static int kingBeamCooldownTicks = 90; private static double kingBeamTracking = 0.1D; private static double kingBeamTerrainRadius = 5.0D; @@ -803,6 +824,8 @@ public final class AntarchySettings { return bigBerthaNightmareDamageBonusPercent; } + public static double krakensGraspLightningDamage() { return krakensGraspLightningDamage; } + public static double potentNyxiteInvertedDurationSeconds() { return potentNyxiteInvertedDurationSeconds; } @@ -2013,6 +2036,40 @@ public final class AntarchySettings { public static double queenAttackDamage() { return queenAttackDamage; } public static void setQueenAttackDamage(double v) { queenAttackDamage = v; } + public static double royalBossSoundVolume() { return royalBossSoundVolume; } + public static void setRoyalBossSoundVolume(double v) { royalBossSoundVolume = v; } + public static double queenBlackHoleRadius() { return queenBlackHoleRadius; } + public static void setQueenBlackHoleRadius(double v) { queenBlackHoleRadius = v; } + public static int queenBlackHoleActiveTicks() { return queenBlackHoleActiveTicks; } + public static void setQueenBlackHoleActiveTicks(int v) { queenBlackHoleActiveTicks = v; } + public static double queenBlackHolePullStrength() { return queenBlackHolePullStrength; } + public static void setQueenBlackHolePullStrength(double v) { queenBlackHolePullStrength = v; } + public static double queenFinalTimeFieldRadius() { return queenFinalTimeFieldRadius; } + public static void setQueenFinalTimeFieldRadius(double v) { queenFinalTimeFieldRadius = v; } + public static double queenFinalTimeFieldRate() { return queenFinalTimeFieldRate; } + public static void setQueenFinalTimeFieldRate(double v) { queenFinalTimeFieldRate = v; } + public static int queenFinalTimeFieldDurationTicks() { return queenFinalTimeFieldDurationTicks; } + public static void setQueenFinalTimeFieldDurationTicks(int v) { queenFinalTimeFieldDurationTicks = v; } + public static int queenFinalTimeFieldCooldownTicks() { return queenFinalTimeFieldCooldownTicks; } + public static void setQueenFinalTimeFieldCooldownTicks(int v) { queenFinalTimeFieldCooldownTicks = v; } + public static double kingFireballDamage() { return kingFireballDamage; } + public static void setKingFireballDamage(double v) { kingFireballDamage = v; } + public static double kingFireballRadius() { return kingFireballRadius; } + public static void setKingFireballRadius(double v) { kingFireballRadius = v; } + public static int kingFireballCooldownTicks() { return kingFireballCooldownTicks; } + public static void setKingFireballCooldownTicks(int v) { kingFireballCooldownTicks = v; } + public static double kingIceballDamage() { return kingIceballDamage; } + public static void setKingIceballDamage(double v) { kingIceballDamage = v; } + public static double kingIceballRadius() { return kingIceballRadius; } + public static void setKingIceballRadius(double v) { kingIceballRadius = v; } + public static int kingIceballCooldownTicks() { return kingIceballCooldownTicks; } + public static void setKingIceballCooldownTicks(int v) { kingIceballCooldownTicks = v; } + public static double kingIceSpikeDamage() { return kingIceSpikeDamage; } + public static void setKingIceSpikeDamage(double v) { kingIceSpikeDamage = v; } + public static int kingIceSpikeCooldownTicks() { return kingIceSpikeCooldownTicks; } + public static void setKingIceSpikeCooldownTicks(int v) { kingIceSpikeCooldownTicks = v; } + public static int kingElementalTerrainCap() { return kingElementalTerrainCap; } + public static void setKingElementalTerrainCap(int v) { kingElementalTerrainCap = v; } public static double royalBossArmor() { return royalBossArmor; } public static void setRoyalBossArmor(double v) { royalBossArmor = v; } @@ -2832,6 +2889,10 @@ public final class AntarchySettings { public static void setQueenBeamRange(double v) { queenBeamRange = v; } public static int queenBeamDurationTicks() { return queenBeamDurationTicks; } public static void setQueenBeamDurationTicks(int v) { queenBeamDurationTicks = v; } + public static int queenBeamWindupTicks() { return queenBeamWindupTicks; } + public static void setQueenBeamWindupTicks(int v) { queenBeamWindupTicks = v; } + public static int queenBeamTravelTicks() { return queenBeamTravelTicks; } + public static void setQueenBeamTravelTicks(int v) { queenBeamTravelTicks = v; } public static int queenBeamCooldownTicks() { return queenBeamCooldownTicks; } public static void setQueenBeamCooldownTicks(int v) { queenBeamCooldownTicks = v; } public static double queenBeamTracking() { return queenBeamTracking; } @@ -2846,6 +2907,10 @@ public final class AntarchySettings { public static void setKingBeamRange(double v) { kingBeamRange = v; } public static int kingBeamDurationTicks() { return kingBeamDurationTicks; } public static void setKingBeamDurationTicks(int v) { kingBeamDurationTicks = v; } + public static int kingBeamWindupTicks() { return kingBeamWindupTicks; } + public static void setKingBeamWindupTicks(int v) { kingBeamWindupTicks = v; } + public static int kingBeamTravelTicks() { return kingBeamTravelTicks; } + public static void setKingBeamTravelTicks(int v) { kingBeamTravelTicks = v; } public static int kingBeamCooldownTicks() { return kingBeamCooldownTicks; } public static void setKingBeamCooldownTicks(int v) { kingBeamCooldownTicks = v; } public static double kingBeamTracking() { return kingBeamTracking; } 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 e0cf683b..d1d87ac1 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 @@ -157,6 +157,10 @@ public final class AntarchyObjects { @SuppressWarnings("unchecked") public static Supplier> ROYAL_BOLT = (Supplier>) UNBOUND; @SuppressWarnings("unchecked") + public static Supplier> ROYAL_ELEMENTAL_PROJECTILE = (Supplier>) UNBOUND; + @SuppressWarnings("unchecked") + public static Supplier> ROYAL_ICE_SPIKE = (Supplier>) UNBOUND; + @SuppressWarnings("unchecked") public static Supplier> KING = (Supplier>) UNBOUND; @SuppressWarnings("unchecked") public static Supplier> QUEEN = (Supplier>) UNBOUND; @@ -725,6 +729,14 @@ public final class AntarchyObjects { ROYAL_BOLT = supplier; } + public static void setRoyalElementalProjectile(Supplier> supplier) { + ROYAL_ELEMENTAL_PROJECTILE = supplier; + } + + public static void setRoyalIceSpike(Supplier> supplier) { + ROYAL_ICE_SPIKE = supplier; + } + public static void setKing(Supplier> supplier) { KING = 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 095567d2..4849b8e9 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 @@ -202,6 +202,7 @@ public final class AntarchySoundEvents { public static Supplier KING_ROAR = (Supplier) UNBOUND; public static Supplier KING_WING_FLAP = (Supplier) UNBOUND; public static Supplier KING_FLY_LOOP = (Supplier) UNBOUND; + public static Supplier QUEEN_FLY_LOOP = (Supplier) UNBOUND; public static Supplier PRINCE_IDLE = (Supplier) UNBOUND; public static Supplier PRINCE_BITE = (Supplier) UNBOUND; public static Supplier PRINCE_SHOOT = (Supplier) UNBOUND; @@ -406,6 +407,7 @@ public final class AntarchySoundEvents { Supplier kingRoar, Supplier kingWingFlap, Supplier kingFlyLoop, + Supplier queenFlyLoop, Supplier princeIdle, Supplier princeBite, Supplier princeShoot, @@ -606,6 +608,7 @@ public final class AntarchySoundEvents { KING_ROAR = kingRoar; KING_WING_FLAP = kingWingFlap; KING_FLY_LOOP = kingFlyLoop; + QUEEN_FLY_LOOP = queenFlyLoop; PRINCE_IDLE = princeIdle; PRINCE_BITE = princeBite; PRINCE_SHOOT = princeShoot; 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 865c33a4..d8f19501 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,8 @@ 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> COMMANDED_BLACKLIST = TagKey.create(Registries.ENTITY_TYPE, id("commanded_blacklist")); + public static final TagKey> DILATED_BLACKLIST = TagKey.create(Registries.ENTITY_TYPE, id("dilated_blacklist")); private Entities() { } diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/block/entity/DimensionalTearMarkerBlockEntity.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/block/entity/DimensionalTearMarkerBlockEntity.java index 7cd8f318..546569ed 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/block/entity/DimensionalTearMarkerBlockEntity.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/block/entity/DimensionalTearMarkerBlockEntity.java @@ -12,17 +12,23 @@ import net.minecraft.util.RandomSource; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntityType; +import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.Vec3; import org.jetbrains.annotations.Nullable; public class DimensionalTearMarkerBlockEntity extends BlockEntity { - private static final int MAX_ATTEMPT_TICKS = 100; + private static final int MAX_ATTEMPT_TICKS = 80; private static final int MIN_LINK_RANGE = 50; private static final int MAX_LINK_RANGE = 500; - private static final int PARTNER_ORIGIN_ATTEMPTS = 12; - private static final int PARTNER_SEARCH_ATTEMPTS = 48; - private static final int LOCAL_SEARCH_ATTEMPTS = 18; - private static final int SEARCH_ATTEMPTS_PER_TICK = 2; + private static final int PARTNER_ORIGIN_ATTEMPTS = 8; + private static final int PARTNER_SEARCH_ATTEMPTS = 32; + private static final int LOCAL_SEARCH_ATTEMPTS = 12; + private static final int SEARCH_ATTEMPTS_PER_TICK = 1; + private static final int MIN_VERTICAL_SEPARATION = 24; + private static final int MIN_PARTNER_VERTICAL_OFFSET = 48; + private static final int MAX_PARTNER_VERTICAL_OFFSET = 96; + private static final int NATURAL_TEAR_LOCAL_RADIUS = 512; + private static final int NATURAL_TEAR_LOCAL_CAP = 6; private int attemptTicks; private int localAttempts; @@ -64,6 +70,11 @@ public class DimensionalTearMarkerBlockEntity extends BlockEntity { continue; } + if (partnerOrigin == null && partnerOriginAttempts == 0 + && countNearbyTears(level, origin) >= NATURAL_TEAR_LOCAL_CAP) { + return false; + } + if (partnerOrigin == null || partnerSearchAttempts >= PARTNER_SEARCH_ATTEMPTS) { if (partnerOriginAttempts >= PARTNER_ORIGIN_ATTEMPTS) { return false; @@ -75,7 +86,8 @@ public class DimensionalTearMarkerBlockEntity extends BlockEntity { partnerSearchAttempts++; BlockPos candidate = randomCandidate(partnerOrigin, random); - if (!isUsablePocket(level, candidate)) { + if (Math.abs(candidate.getY() - firstPocket.getY()) < MIN_VERTICAL_SEPARATION + || !isUsablePocket(level, candidate)) { continue; } return spawnPair(level, random, firstPocket, candidate); @@ -111,10 +123,16 @@ public class DimensionalTearMarkerBlockEntity extends BlockEntity { int distance = Mth.nextInt(random, MIN_LINK_RANGE, MAX_LINK_RANGE); int dx = Mth.floor(Math.cos(angle) * distance); int dz = Mth.floor(Math.sin(angle) * distance); - int dy = Mth.nextInt(random, -36, 36); + int verticalOffset = Mth.nextInt(random, MIN_PARTNER_VERTICAL_OFFSET, MAX_PARTNER_VERTICAL_OFFSET); + int dy = random.nextBoolean() ? verticalOffset : -verticalOffset; return first.offset(dx, dy, dz); } + private static int countNearbyTears(ServerLevel level, BlockPos origin) { + AABB area = new AABB(origin).inflate(NATURAL_TEAR_LOCAL_RADIUS); + return level.getEntitiesOfClass(DimensionalTearEntity.class, area).size(); + } + private static boolean isUsablePocket(ServerLevel level, BlockPos pos) { int minY = level.getMinBuildHeight() + 8; int maxY = level.getMaxBuildHeight() - 8; 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 97886a73..0a042ba3 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 @@ -10,6 +10,7 @@ import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.item.ItemEntity; import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.player.Player; import net.minecraft.util.Mth; public final class ClientTimeDilationTicker { @@ -32,6 +33,11 @@ public final class ClientTimeDilationTicker { com.craisinlord.antarchy.content.time.TimeDilationApi.clearSyncedClientRates(); } // Entity rates come from the server. Field snapshots are used only for local effects. + for (Player player : level.players()) { + if (player.isDeadOrDying() || rateAt(player.getX(), player.getY(), player.getZ()) >= TimeDilationMath.NORMAL_RATE) { + com.craisinlord.antarchy.content.time.TimeDilationApi.clearRate(player); + } + } } public static float dilateAnimationTime(Entity entity, float vanillaTime) { 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 new file mode 100644 index 00000000..a0d8d665 --- /dev/null +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/model/RoyalBlackHoleModel.java @@ -0,0 +1,23 @@ +package com.craisinlord.antarchy.content.client.model; + +import com.craisinlord.antarchy.Antarchy; +import com.craisinlord.antarchy.content.entity.royal.RoyalBlackHoleEntity; +import net.minecraft.resources.ResourceLocation; +import software.bernie.geckolib.model.GeoModel; + +public class RoyalBlackHoleModel extends GeoModel { + @Override + public ResourceLocation getModelResource(RoyalBlackHoleEntity animatable) { + return ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "geo/black_hole.geo.json"); + } + + @Override + public ResourceLocation getTextureResource(RoyalBlackHoleEntity animatable) { + return ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "textures/entity/queen/black_hole.png"); + } + + @Override + public ResourceLocation getAnimationResource(RoyalBlackHoleEntity 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/RoyalElementalProjectileModel.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/model/RoyalElementalProjectileModel.java new file mode 100644 index 00000000..04785e5d --- /dev/null +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/model/RoyalElementalProjectileModel.java @@ -0,0 +1,26 @@ +package com.craisinlord.antarchy.content.client.model; + +import com.craisinlord.antarchy.Antarchy; +import com.craisinlord.antarchy.content.entity.royal.RoyalElementalProjectileEntity; +import net.minecraft.resources.ResourceLocation; +import software.bernie.geckolib.model.GeoModel; + +public class RoyalElementalProjectileModel extends GeoModel { + @Override + public ResourceLocation getModelResource(RoyalElementalProjectileEntity animatable) { + return ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, + "geo/" + (animatable.isIceball() ? "ice_ball" : "fire_ball") + ".geo.json"); + } + + @Override + public ResourceLocation getTextureResource(RoyalElementalProjectileEntity animatable) { + return ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, + "textures/entity/king/" + (animatable.isIceball() ? "ice_ball" : "fire_ball") + ".png"); + } + + @Override + public ResourceLocation getAnimationResource(RoyalElementalProjectileEntity animatable) { + return ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, + "animations/" + (animatable.isIceball() ? "ice_ball" : "fire_ball") + ".animation.json"); + } +} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/model/RoyalIceSpikeModel.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/model/RoyalIceSpikeModel.java new file mode 100644 index 00000000..27ffa87e --- /dev/null +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/model/RoyalIceSpikeModel.java @@ -0,0 +1,23 @@ +package com.craisinlord.antarchy.content.client.model; + +import com.craisinlord.antarchy.Antarchy; +import com.craisinlord.antarchy.content.entity.royal.RoyalIceSpikeEntity; +import net.minecraft.resources.ResourceLocation; +import software.bernie.geckolib.model.GeoModel; + +public class RoyalIceSpikeModel extends GeoModel { + @Override + public ResourceLocation getModelResource(RoyalIceSpikeEntity animatable) { + return ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "geo/ice_spike.geo.json"); + } + + @Override + public ResourceLocation getTextureResource(RoyalIceSpikeEntity animatable) { + return ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "textures/entity/king/ice_spike.png"); + } + + @Override + public ResourceLocation getAnimationResource(RoyalIceSpikeEntity animatable) { + return ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "animations/ice_spike.animation.json"); + } +} 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 new file mode 100644 index 00000000..bd25a3ed --- /dev/null +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/RoyalBlackHoleRenderer.java @@ -0,0 +1,30 @@ +package com.craisinlord.antarchy.content.client.renderer; + +import com.craisinlord.antarchy.content.client.model.RoyalBlackHoleModel; +import com.craisinlord.antarchy.content.entity.royal.RoyalBlackHoleEntity; +import com.mojang.blaze3d.vertex.PoseStack; +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 org.jetbrains.annotations.Nullable; +import software.bernie.geckolib.renderer.GeoEntityRenderer; + +public class RoyalBlackHoleRenderer extends GeoEntityRenderer { + public RoyalBlackHoleRenderer(EntityRendererProvider.Context context) { + super(context, new RoyalBlackHoleModel()); + this.shadowRadius = 0.0F; + } + + @Override + public void render(RoyalBlackHoleEntity entity, float entityYaw, float partialTick, PoseStack poseStack, + MultiBufferSource bufferSource, int packedLight) { + super.render(entity, entityYaw, partialTick, poseStack, bufferSource, 0xF000F0); + } + + @Override + public @Nullable RenderType getRenderType(RoyalBlackHoleEntity animatable, ResourceLocation texture, + @Nullable MultiBufferSource bufferSource, float partialTick) { + return RenderType.entityTranslucent(this.getTextureLocation(animatable)); + } +} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/RoyalBossRenderer.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/RoyalBossRenderer.java index 7717bb46..f9b76e33 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/RoyalBossRenderer.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/RoyalBossRenderer.java @@ -58,8 +58,8 @@ public class RoyalBossRenderer extends GeoEntityRenderer { this.shadowRadius = 8.0F * RoyalBossEntity.MODEL_RENDER_SCALE; } - private static final float BEAM_OUTER_RADIUS = 0.55F; - private static final float BEAM_INNER_RADIUS = 0.26F; + private static final float BEAM_OUTER_RADIUS = 1.10F; + private static final float BEAM_INNER_RADIUS = 0.52F; private static final float BEAM_TILE_BLOCKS = 3.0F; private static final float BEAM_SCROLL_SPEED = 0.32F; 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 new file mode 100644 index 00000000..52056457 --- /dev/null +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/RoyalElementalProjectileRenderer.java @@ -0,0 +1,38 @@ +package com.craisinlord.antarchy.content.client.renderer; + +import com.craisinlord.antarchy.content.client.model.RoyalElementalProjectileModel; +import com.craisinlord.antarchy.content.entity.royal.RoyalElementalProjectileEntity; +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.entity.EntityRendererProvider; +import net.minecraft.util.Mth; +import software.bernie.geckolib.cache.object.BakedGeoModel; +import software.bernie.geckolib.renderer.GeoEntityRenderer; + +public class RoyalElementalProjectileRenderer extends GeoEntityRenderer { + public RoyalElementalProjectileRenderer(EntityRendererProvider.Context context) { + super(context, new RoyalElementalProjectileModel()); + this.shadowRadius = 0.0F; + } + + @Override + public void preRender(PoseStack poseStack, RoyalElementalProjectileEntity animatable, BakedGeoModel model, + MultiBufferSource bufferSource, 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.isIceball() ? 0.75F : 0.36F; + poseStack.scale(scale, scale, scale); + } + + @Override + protected void applyRotations(RoyalElementalProjectileEntity entity, PoseStack poseStack, float ageInTicks, + float rotationYaw, float partialTick, float nativeScale) { + float yaw = Mth.rotLerp(partialTick, entity.yRotO, entity.getYRot()); + float pitch = Mth.lerp(partialTick, entity.xRotO, entity.getXRot()); + poseStack.mulPose(Axis.YP.rotationDegrees(180.0F - yaw)); + poseStack.mulPose(Axis.XP.rotationDegrees(-pitch)); + } +} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/RoyalIceSpikeRenderer.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/RoyalIceSpikeRenderer.java new file mode 100644 index 00000000..49033216 --- /dev/null +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/RoyalIceSpikeRenderer.java @@ -0,0 +1,14 @@ +package com.craisinlord.antarchy.content.client.renderer; + +import com.craisinlord.antarchy.content.client.model.RoyalIceSpikeModel; +import com.craisinlord.antarchy.content.entity.royal.RoyalIceSpikeEntity; +import com.mojang.blaze3d.vertex.PoseStack; +import net.minecraft.client.renderer.entity.EntityRendererProvider; +import software.bernie.geckolib.renderer.GeoEntityRenderer; + +public class RoyalIceSpikeRenderer extends GeoEntityRenderer { + public RoyalIceSpikeRenderer(EntityRendererProvider.Context context) { + super(context, new RoyalIceSpikeModel()); + this.shadowRadius = 0.0F; + } +} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/WindVortexRenderer.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/WindVortexRenderer.java index 6cf50ec3..a88cf9ce 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/WindVortexRenderer.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/WindVortexRenderer.java @@ -12,9 +12,8 @@ import net.minecraft.util.Mth; import org.joml.Matrix4f; public class WindVortexRenderer extends EntityRenderer { - private static final int RINGS = 10; - private static final int SEGMENTS_PER_RING = 12; - private static final int ARC_STEPS = 6; + private static final int RINGS = 11; + private static final int SQUARES_PER_SIDE = 3; private static final float BASE_RADIUS = 0.35F; public WindVortexRenderer(EntityRendererProvider.Context context) { @@ -44,48 +43,72 @@ public class WindVortexRenderer extends EntityRenderer { float radius = Mth.lerp(shapeProgress, BASE_RADIUS, topRadius); float ringSpin = spinDirection * age * (0.13F + progress * 0.045F) + ring * 0.47F; float alpha = alphaMultiplier; - float thickness = 0.025F + radius * 0.012F; - float arcLength = 0.34F + progress * 0.08F; + float halfSide = radius * 0.92F; + float tileSize = Math.max(0.035F, radius * 0.12F); int red = push ? 70 : 64; int green = push ? 220 : 255; int blue = push ? 255 : 56; - - for (int segment = 0; segment < SEGMENTS_PER_RING; segment++) { - float baseAngle = ringSpin + segment * Mth.TWO_PI / SEGMENTS_PER_RING; - float start = baseAngle - arcLength * 0.5F; - float end = baseAngle + arcLength * 0.5F; - drawArc(consumer, pose, basis, radius, axisDistance, thickness, start, end, alpha, red, green, blue); - } + drawSquareRing(consumer, pose, basis, halfSide, axisDistance, tileSize, ringSpin, + alpha, red, green, blue); } super.render(entity, entityYaw, partialTick, poseStack, bufferSource, packedLight); } - private static void drawArc(VertexConsumer consumer, Matrix4f pose, WindVortexEntity.Basis basis, float radius, - float axisDistance, float thickness, float start, float end, float alpha, int red, int green, int blue) { - float previousAngle = start; - for (int step = 1; step <= ARC_STEPS; step++) { - float angle = Mth.lerp(step / (float) ARC_STEPS, start, end); - addLineQuad(consumer, pose, basis, radius, axisDistance, thickness, previousAngle, angle, alpha, red, green, blue); - previousAngle = angle; + private static void drawSquareRing(VertexConsumer consumer, Matrix4f pose, WindVortexEntity.Basis basis, + float halfSide, float axisDistance, float tileSize, float rotation, float alpha, + int red, int green, int blue) { + for (int side = 0; side < 4; side++) { + for (int tile = 0; tile < SQUARES_PER_SIDE; tile++) { + float start = -halfSide + (tile + 0.18F) * (2.0F * halfSide / SQUARES_PER_SIDE); + float end = -halfSide + (tile + 0.82F) * (2.0F * halfSide / SQUARES_PER_SIDE); + float sideAngle = rotation + side * (Mth.TWO_PI / 4.0F); + addSquareTile(consumer, pose, basis, halfSide, axisDistance, tileSize, + sideAngle, start, end, alpha, red, green, blue); + } } } - private static void addLineQuad(VertexConsumer consumer, Matrix4f pose, WindVortexEntity.Basis basis, float radius, - float axisDistance, float thickness, float startAngle, float endAngle, float alpha, int red, int green, int blue) { - org.joml.Vector3f start = point(basis, radius, axisDistance, startAngle); - org.joml.Vector3f end = point(basis, radius, axisDistance, endAngle); - org.joml.Vector3f axisOffset = new org.joml.Vector3f((float) basis.axis.x, (float) basis.axis.y, (float) basis.axis.z).mul(thickness); - float middleAngle = (startAngle + endAngle) * 0.5F; - org.joml.Vector3f radialOffset = new org.joml.Vector3f( - (float) (basis.sideA.x * Mth.cos(middleAngle) + basis.sideB.x * Mth.sin(middleAngle)), - (float) (basis.sideA.y * Mth.cos(middleAngle) + basis.sideB.y * Mth.sin(middleAngle)), - (float) (basis.sideA.z * Mth.cos(middleAngle) + basis.sideB.z * Mth.sin(middleAngle)) - ).mul(thickness); + private static void addSquareTile(VertexConsumer consumer, Matrix4f pose, WindVortexEntity.Basis basis, + float halfSide, float axisDistance, float tileSize, float sideAngle, float start, float end, + float alpha, int red, int green, int blue) { + org.joml.Vector3f center = squarePoint(basis, halfSide, axisDistance, sideAngle, (start + end) * 0.5F); + org.joml.Vector3f tangent = new org.joml.Vector3f( + (float) (-basis.sideA.x * Mth.sin(sideAngle) + basis.sideB.x * Mth.cos(sideAngle)), + (float) (-basis.sideA.y * Mth.sin(sideAngle) + basis.sideB.y * Mth.cos(sideAngle)), + (float) (-basis.sideA.z * Mth.sin(sideAngle) + basis.sideB.z * Mth.cos(sideAngle))); + org.joml.Vector3f axisOffset = new org.joml.Vector3f( + (float) basis.axis.x, (float) basis.axis.y, (float) basis.axis.z).mul(tileSize * 0.5F); + tangent.mul(tileSize * 0.5F); int a = Mth.clamp((int) (alpha * 255.0F), 0, 255); + addVertex(consumer, pose, center.x - tangent.x - axisOffset.x, center.y - tangent.y - axisOffset.y, + center.z - tangent.z - axisOffset.z, red, green, blue, a); + addVertex(consumer, pose, center.x + tangent.x - axisOffset.x, center.y + tangent.y - axisOffset.y, + center.z + tangent.z - axisOffset.z, red, green, blue, a); + addVertex(consumer, pose, center.x + tangent.x + axisOffset.x, center.y + tangent.y + axisOffset.y, + center.z + tangent.z + axisOffset.z, red, green, blue, a); + addVertex(consumer, pose, center.x - tangent.x + axisOffset.x, center.y - tangent.y + axisOffset.y, + center.z - tangent.z + axisOffset.z, red, green, blue, a); + addVertex(consumer, pose, center.x - tangent.x + axisOffset.x, center.y - tangent.y + axisOffset.y, + center.z - tangent.z + axisOffset.z, red, green, blue, a); + addVertex(consumer, pose, center.x + tangent.x + axisOffset.x, center.y + tangent.y + axisOffset.y, + center.z + tangent.z + axisOffset.z, red, green, blue, a); + addVertex(consumer, pose, center.x + tangent.x - axisOffset.x, center.y + tangent.y - axisOffset.y, + center.z + tangent.z - axisOffset.z, red, green, blue, a); + addVertex(consumer, pose, center.x - tangent.x - axisOffset.x, center.y - tangent.y - axisOffset.y, + center.z - tangent.z - axisOffset.z, red, green, blue, a); + } - addQuad(consumer, pose, start, end, axisOffset, red, green, blue, a); - addQuad(consumer, pose, start, end, radialOffset, red, green, blue, a); + private static org.joml.Vector3f squarePoint(WindVortexEntity.Basis basis, float halfSide, + float axisDistance, float sideAngle, float along) { + float x = Mth.cos(sideAngle) * halfSide; + float z = Mth.sin(sideAngle) * halfSide; + float tangentX = -Mth.sin(sideAngle) * along; + float tangentZ = Mth.cos(sideAngle) * along; + return new org.joml.Vector3f( + (float) (basis.axis.x * axisDistance + basis.sideA.x * (x + tangentX) + basis.sideB.x * (z + tangentZ)), + (float) (basis.axis.y * axisDistance + basis.sideA.y * (x + tangentX) + basis.sideB.y * (z + tangentZ)), + (float) (basis.axis.z * axisDistance + basis.sideA.z * (x + tangentX) + basis.sideB.z * (z + tangentZ))); } private static void addQuad(VertexConsumer consumer, Matrix4f pose, org.joml.Vector3f start, @@ -94,15 +117,14 @@ public class WindVortexRenderer extends EntityRenderer { addVertex(consumer, pose, end.x - offset.x, end.y - offset.y, end.z - offset.z, red, green, blue, alpha); addVertex(consumer, pose, end.x + offset.x, end.y + offset.y, end.z + offset.z, red, green, blue, alpha); addVertex(consumer, pose, start.x + offset.x, start.y + offset.y, start.z + offset.z, red, green, blue, alpha); - } - private static org.joml.Vector3f point(WindVortexEntity.Basis basis, float radius, float axisDistance, float angle) { - double radialA = Mth.cos(angle) * radius; - double radialB = Mth.sin(angle) * radius; - double x = basis.axis.x * axisDistance + basis.sideA.x * radialA + basis.sideB.x * radialB; - double y = basis.axis.y * axisDistance + basis.sideA.y * radialA + basis.sideB.y * radialB; - double z = basis.axis.z * axisDistance + basis.sideA.z * radialA + basis.sideB.z * radialB; - return new org.joml.Vector3f((float) x, (float) y, (float) z); + // The lightning render type can cull one winding from some camera + // angles. Emit the opposite winding as well so every ribbon is + // visible from both sides. + addVertex(consumer, pose, start.x + offset.x, start.y + offset.y, start.z + offset.z, red, green, blue, alpha); + addVertex(consumer, pose, end.x + offset.x, end.y + offset.y, end.z + offset.z, red, green, blue, alpha); + addVertex(consumer, pose, end.x - offset.x, end.y - offset.y, end.z - offset.z, red, green, blue, alpha); + addVertex(consumer, pose, start.x - offset.x, start.y - offset.y, start.z - offset.z, red, green, blue, alpha); } private static void addVertex(VertexConsumer consumer, Matrix4f pose, float x, float y, float z, diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/creative/CreativeTabContents.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/creative/CreativeTabContents.java index 820f422e..e126f967 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/creative/CreativeTabContents.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/creative/CreativeTabContents.java @@ -594,7 +594,8 @@ public final class CreativeTabContents { private static final List ANTARCHY_POTION_FAMILIES = List.of( "dread", "long_dread", "inversion", "long_inversion", "stinky", "long_stinky", "paralysis", "long_paralysis", "haste", "strong_haste", "shrinking", - "strong_shrinking", "extreme_shrinking", "growing", "strong_growing", "extreme_growing" + "strong_shrinking", "extreme_shrinking", "growing", "strong_growing", "extreme_growing", + "command", "long_command", "time_dilation", "long_time_dilation" ); private static final Map ORDER_INDEX = createOrderIndex(); diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/effect/CommandedBehavior.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/effect/CommandedBehavior.java new file mode 100644 index 00000000..cc6365ef --- /dev/null +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/effect/CommandedBehavior.java @@ -0,0 +1,99 @@ +package com.craisinlord.antarchy.content.effect; + +import java.util.UUID; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.Mob; +import net.minecraft.world.entity.player.Player; + +public final class CommandedBehavior { + private CommandedBehavior() { + } + + public static void tick(Mob mob) { + if (!(mob instanceof CommandedEntityAccess access)) { + return; + } + UUID commanderUuid = access.antarchy$getCommanderUuid(); + if (commanderUuid == null || mob.level().getServer() == null) { + clear(mob); + return; + } + ServerPlayer commander = mob.level().getServer().getPlayerList().getPlayer(commanderUuid); + if (commander == null || commander.level() != mob.level() || !RoyalEffectEligibility.canApplyCommanded(mob)) { + mob.removeEffect(RoyalEffectHooks.commandedHolder()); + clear(mob); + return; + } + LivingEntity target = mob.getTarget(); + if (target != null && isProtectedAlly(mob, target)) { + mob.setTarget(null); + target = null; + } + if (mob.tickCount % 10 != 0 || target != null) { + return; + } + LivingEntity candidate = commander.getLastHurtByMob(); + if (!isValidTarget(mob, commander, candidate)) { + candidate = commander.getLastHurtMob(); + } + if (!isValidTarget(mob, commander, candidate)) { + candidate = mob.getLastHurtByMob(); + } + if (!isValidTarget(mob, commander, candidate)) { + candidate = mob.level().getEntitiesOfClass(LivingEntity.class, mob.getBoundingBox().inflate(16.0D), + entity -> isValidTarget(mob, commander, entity)).stream() + .min((a, b) -> { + int distance = Double.compare(mob.distanceToSqr(a), mob.distanceToSqr(b)); + return distance != 0 ? distance : Integer.compare(a.getId(), b.getId()); + }).orElse(null); + } + if (candidate != null) { + mob.setTarget(candidate); + access.antarchy$setCommandedTargetOwned(true); + } + } + + public static boolean isProtectedAlly(Mob mob, Entity entity) { + if (!(mob instanceof CommandedEntityAccess access) || entity == null) { + return false; + } + UUID commanderUuid = access.antarchy$getCommanderUuid(); + if (commanderUuid == null) { + return false; + } + if (entity.getUUID().equals(commanderUuid)) { + return true; + } + if (entity instanceof Mob other && other instanceof CommandedEntityAccess otherAccess) { + return commanderUuid.equals(otherAccess.antarchy$getCommanderUuid()); + } + if (entity instanceof LivingEntity living && living.isAlliedTo(mob)) { + return true; + } + if (mob.level().getServer() != null + && mob.level().getServer().getPlayerList().getPlayer(commanderUuid) instanceof Player commander + && entity instanceof LivingEntity living) { + return living.isAlliedTo(commander); + } + return false; + } + + private static boolean isValidTarget(Mob mob, ServerPlayer commander, LivingEntity target) { + return target != null && target.isAlive() && target != mob && target != commander + && !isProtectedAlly(mob, target) && !target.isAlliedTo(commander) + && mob.canAttack(target); + } + + public static void clear(Mob mob) { + if (!(mob instanceof CommandedEntityAccess access)) { + return; + } + if (access.antarchy$isCommandedTargetOwned()) { + mob.setTarget(null); + } + access.antarchy$setCommandedTargetOwned(false); + access.antarchy$setCommanderUuid(null); + } +} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/effect/CommandedEntityAccess.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/effect/CommandedEntityAccess.java new file mode 100644 index 00000000..6907148d --- /dev/null +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/effect/CommandedEntityAccess.java @@ -0,0 +1,10 @@ +package com.craisinlord.antarchy.content.effect; + +import java.util.UUID; + +public interface CommandedEntityAccess { + UUID antarchy$getCommanderUuid(); + void antarchy$setCommanderUuid(UUID uuid); + boolean antarchy$isCommandedTargetOwned(); + void antarchy$setCommandedTargetOwned(boolean owned); +} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/effect/CommandedMobEffect.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/effect/CommandedMobEffect.java new file mode 100644 index 00000000..3138c6f9 --- /dev/null +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/effect/CommandedMobEffect.java @@ -0,0 +1,25 @@ +package com.craisinlord.antarchy.content.effect; + +import net.minecraft.world.effect.MobEffect; +import net.minecraft.world.effect.MobEffectCategory; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.Mob; + +public final class CommandedMobEffect extends MobEffect { + public CommandedMobEffect() { + super(MobEffectCategory.HARMFUL, 0xD4AF37); + } + + @Override + public boolean applyEffectTick(LivingEntity entity, int amplifier) { + if (!entity.level().isClientSide() && entity instanceof Mob mob) { + CommandedBehavior.tick(mob); + } + return true; + } + + @Override + public boolean shouldApplyEffectTickThisTick(int duration, int amplifier) { + return true; + } +} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/effect/DilatedMobEffect.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/effect/DilatedMobEffect.java new file mode 100644 index 00000000..908d92ee --- /dev/null +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/effect/DilatedMobEffect.java @@ -0,0 +1,23 @@ +package com.craisinlord.antarchy.content.effect; + +import net.minecraft.world.effect.MobEffect; +import net.minecraft.world.effect.MobEffectCategory; +import net.minecraft.world.entity.LivingEntity; + +public final class DilatedMobEffect extends MobEffect { + public static final double RATE = 0.35D; + + public DilatedMobEffect() { + super(MobEffectCategory.HARMFUL, 0x6C4AB6); + } + + @Override + public boolean applyEffectTick(LivingEntity entity, int amplifier) { + return true; + } + + @Override + public boolean shouldApplyEffectTickThisTick(int duration, int amplifier) { + return true; + } +} 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 b3bb5f15..b0e5da9f 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,11 +1,14 @@ 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); @@ -19,6 +22,19 @@ 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/effect/RoyalEffectEligibility.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/effect/RoyalEffectEligibility.java new file mode 100644 index 00000000..2f3dcf54 --- /dev/null +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/effect/RoyalEffectEligibility.java @@ -0,0 +1,57 @@ +package com.craisinlord.antarchy.content.effect; + +import com.craisinlord.antarchy.content.AntarchyTags; +import com.craisinlord.antarchy.content.item.RoyalAssailantArmorItem; +import com.craisinlord.antarchy.content.item.RoyalGuardianArmorItem; +import net.minecraft.world.effect.MobEffectInstance; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.Mob; +import net.minecraft.world.entity.player.Player; + +public final class RoyalEffectEligibility { + private RoyalEffectEligibility() { + } + + public static boolean isCommanded(MobEffectInstance instance) { + return RoyalEffectHooks.commandedHolder() != null && instance.is(RoyalEffectHooks.commandedHolder()); + } + + public static boolean isDilated(MobEffectInstance instance) { + return RoyalEffectHooks.dilatedHolder() != null && instance.is(RoyalEffectHooks.dilatedHolder()); + } + + public static boolean canApplyCommanded(LivingEntity entity) { + return entity instanceof Mob mob + && !(entity instanceof Player) + && !entity.getType().is(AntarchyTags.Entities.COMMANDED_BLACKLIST) + && !(mob.isNoAi()) + && !(entity.getItemBySlot(net.minecraft.world.entity.EquipmentSlot.HEAD).getItem() instanceof RoyalGuardianArmorItem) + && !(entity.getItemBySlot(net.minecraft.world.entity.EquipmentSlot.CHEST).getItem() instanceof RoyalGuardianArmorItem) + && !(entity.getItemBySlot(net.minecraft.world.entity.EquipmentSlot.LEGS).getItem() instanceof RoyalGuardianArmorItem) + && !(entity.getItemBySlot(net.minecraft.world.entity.EquipmentSlot.FEET).getItem() instanceof RoyalGuardianArmorItem); + } + + public static boolean canApplyDilated(LivingEntity entity) { + return !entity.getType().is(AntarchyTags.Entities.DILATED_BLACKLIST) + && !entity.getType().is(AntarchyTags.Entities.TIME_DILATION_IMMUNE) + && !(entity.getItemBySlot(net.minecraft.world.entity.EquipmentSlot.HEAD).getItem() instanceof RoyalAssailantArmorItem) + && !(entity.getItemBySlot(net.minecraft.world.entity.EquipmentSlot.CHEST).getItem() instanceof RoyalAssailantArmorItem) + && !(entity.getItemBySlot(net.minecraft.world.entity.EquipmentSlot.LEGS).getItem() instanceof RoyalAssailantArmorItem) + && !(entity.getItemBySlot(net.minecraft.world.entity.EquipmentSlot.FEET).getItem() instanceof RoyalAssailantArmorItem); + } + + public static boolean canApply(LivingEntity entity, MobEffectInstance instance) { + if (isCommanded(instance)) { + return canApplyCommanded(entity); + } + if (isDilated(instance)) { + return canApplyDilated(entity); + } + return true; + } + + public static boolean canUseCommandSource(Entity source) { + return source instanceof Player; + } +} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/effect/RoyalEffectHooks.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/effect/RoyalEffectHooks.java new file mode 100644 index 00000000..70068b36 --- /dev/null +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/effect/RoyalEffectHooks.java @@ -0,0 +1,27 @@ +package com.craisinlord.antarchy.content.effect; + +import com.craisinlord.antarchy.Antarchy; +import net.minecraft.core.Holder; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.core.registries.Registries; +import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.effect.MobEffect; + +public final class RoyalEffectHooks { + private static final ResourceKey COMMANDED_KEY = ResourceKey.create(Registries.MOB_EFFECT, + ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "commanded")); + private static final ResourceKey DILATED_KEY = ResourceKey.create(Registries.MOB_EFFECT, + ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "dilated")); + + private RoyalEffectHooks() { + } + + public static Holder commandedHolder() { + return BuiltInRegistries.MOB_EFFECT.getHolder(COMMANDED_KEY).orElse(null); + } + + public static Holder dilatedHolder() { + return BuiltInRegistries.MOB_EFFECT.getHolder(DILATED_KEY).orElse(null); + } +} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/BedBugEntity.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/BedBugEntity.java index 192216ad..bdb2c3ff 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/BedBugEntity.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/BedBugEntity.java @@ -98,6 +98,8 @@ public class BedBugEntity extends Animal implements GeoEntity { @Nullable private ItemEntity cachedRottenFlesh; private int nextFoodSearchTick; + private int ignoredRottenFleshId = -1; + private int ignoredRottenFleshUntilTick; private int guardNestCooldown; public BedBugEntity(EntityType entityType, Level level) { @@ -450,6 +452,24 @@ public class BedBugEntity extends Animal implements GeoEntity { this.nextFoodSearchTick = 0; } + private void temporarilyIgnoreRottenFlesh(ItemEntity itemEntity) { + this.ignoredRottenFleshId = itemEntity.getId(); + this.ignoredRottenFleshUntilTick = this.tickCount + 100; + this.invalidateFoodCache(); + } + + private boolean isTemporarilyIgnoredFood(ItemEntity itemEntity) { + if (itemEntity.getId() != this.ignoredRottenFleshId) { + return false; + } + if (this.tickCount >= this.ignoredRottenFleshUntilTick) { + this.ignoredRottenFleshId = -1; + this.ignoredRottenFleshUntilTick = 0; + return false; + } + return true; + } + @Nullable private ItemEntity findNearestRottenFlesh() { return this.level().getEntitiesOfClass( @@ -457,7 +477,8 @@ public class BedBugEntity extends Animal implements GeoEntity { this.getBoundingBox().inflate(FOOD_SEARCH_RADIUS, 2.0D, FOOD_SEARCH_RADIUS), itemEntity -> itemEntity.isAlive() && !itemEntity.getItem().isEmpty() - && itemEntity.getItem().is(Items.ROTTEN_FLESH)) + && itemEntity.getItem().is(Items.ROTTEN_FLESH) + && !this.isTemporarilyIgnoredFood(itemEntity)) .stream() .min(Comparator.comparingDouble(this::distanceToSqr)) .orElse(null); @@ -581,6 +602,7 @@ public class BedBugEntity extends Animal implements GeoEntity { private ItemEntity targetFood; private double lastDistanceSqr; private int stuckTicks; + private int pathRefreshCooldown; private SeekRottenFleshGoal() { this.setFlags(EnumSet.of(Flag.MOVE, Flag.LOOK)); @@ -614,6 +636,7 @@ public class BedBugEntity extends Animal implements GeoEntity { BedBugEntity.this.setTarget(null); this.lastDistanceSqr = Double.MAX_VALUE; this.stuckTicks = 0; + this.pathRefreshCooldown = 0; } @Override @@ -622,6 +645,7 @@ public class BedBugEntity extends Animal implements GeoEntity { this.targetFood = null; this.lastDistanceSqr = Double.MAX_VALUE; this.stuckTicks = 0; + this.pathRefreshCooldown = 0; } @Override @@ -631,7 +655,14 @@ public class BedBugEntity extends Animal implements GeoEntity { } BedBugEntity.this.getLookControl().setLookAt(this.targetFood, 30.0F, 30.0F); - BedBugEntity.this.getNavigation().moveTo(this.targetFood, 1.0D); + if (this.pathRefreshCooldown-- <= 0) { + if (!BedBugEntity.this.getNavigation().moveTo(this.targetFood, 1.0D)) { + BedBugEntity.this.temporarilyIgnoreRottenFlesh(this.targetFood); + this.targetFood = null; + return; + } + this.pathRefreshCooldown = 10; + } double distanceSqr = BedBugEntity.this.distanceToSqr(this.targetFood); if (distanceSqr < this.lastDistanceSqr - 0.2D) { @@ -639,7 +670,7 @@ public class BedBugEntity extends Animal implements GeoEntity { this.stuckTicks = 0; } else if (BedBugEntity.this.getNavigation().isDone() && distanceSqr > 4.0D) { if (++this.stuckTicks >= 20) { - BedBugEntity.this.invalidateFoodCache(); + BedBugEntity.this.temporarilyIgnoreRottenFlesh(this.targetFood); this.targetFood = null; return; } diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/DiamondMinecartEntity.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/DiamondMinecartEntity.java index c9655da6..07c22ea4 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/DiamondMinecartEntity.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/DiamondMinecartEntity.java @@ -29,12 +29,14 @@ import org.jetbrains.annotations.Nullable; import java.util.List; public class DiamondMinecartEntity extends Minecart { + private static final long INPUT_TIMEOUT_TICKS = 5L; private static final EntityDataAccessor SYNCED_SPEED = SynchedEntityData.defineId(DiamondMinecartEntity.class, EntityDataSerializers.FLOAT); private static final EntityDataAccessor SYNCED_FACING = SynchedEntityData.defineId(DiamondMinecartEntity.class, EntityDataSerializers.INT); private byte inputFlags = 0; + private long lastInputGameTime = Long.MIN_VALUE; private float currentSpeed = 0.0F; private Direction facingDir = Direction.SOUTH; private BlockPos lastBlockPos = null; @@ -109,19 +111,38 @@ public class DiamondMinecartEntity extends Minecart { @Override protected double getMaxSpeed() { - return 0.4D; + return AntarchySettings.diamondMinecartEnabled() + ? AntarchySettings.diamondMinecartMaxSpeed() + : 0.4D; } public void onInputReceived(byte flags) { this.inputFlags = flags; + this.lastInputGameTime = this.level().getGameTime(); } @Override public void tick() { super.tick(); + if (!this.level().isClientSide) { + this.serverTick(); + } } private void serverTick() { + if (!AntarchySettings.diamondMinecartEnabled()) { + this.inputFlags = 0; + this.lastInputGameTime = Long.MIN_VALUE; + this.currentSpeed = 0.0F; + this.entityData.set(SYNCED_SPEED, 0.0F); + this.setDeltaMovement(0.0D, this.getDeltaMovement().y, 0.0D); + return; + } + + if (this.level().getGameTime() - this.lastInputGameTime > INPUT_TIMEOUT_TICKS) { + this.inputFlags = 0; + } + Player rider = this.getRidingPlayer(); boolean hasRider = rider != null; @@ -176,6 +197,17 @@ public class DiamondMinecartEntity extends Minecart { if (AntarchySettings.diamondMinecartMobDamageEnabled() && this.currentSpeed > 0.15F && hasRider) { this.applyMobCollisionDamage(); } + + this.applyControlledVelocity(); + } + + private void applyControlledVelocity() { + double speed = this.currentSpeed; + this.setDeltaMovement( + this.facingDir.getStepX() * speed, + this.getDeltaMovement().y, + this.facingDir.getStepZ() * speed + ); } private void onEnteredNewBlock(BlockPos currentPos, @Nullable Player rider) { 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 13bd6e33..0fcdb4a4 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 @@ -1,8 +1,11 @@ package com.craisinlord.antarchy.content.entity; import com.craisinlord.antarchy.config.AntarchySettings; +import com.craisinlord.antarchy.content.AntarchyObjects; import com.craisinlord.antarchy.content.entity.royal.QueenEntity; import com.craisinlord.antarchy.content.gravity.AntarchyGravityApi; +import com.craisinlord.antarchy.content.gravity.AntarchyGravityDirection; +import com.craisinlord.antarchy.content.worldgen.thoraxis.ThoraxisUndersideManager; import java.util.List; import java.util.UUID; import net.minecraft.core.BlockPos; @@ -89,6 +92,11 @@ public class ManticoreEntity extends Monster implements GeoEntity { this.xpReward = 12; } + @Override + public boolean fireImmune() { + return true; + } + public static AttributeSupplier.Builder createAttributes() { return Mob.createMobAttributes() .add(Attributes.MAX_HEALTH, AntarchySettings.manticoreHealth()) @@ -206,6 +214,8 @@ public class ManticoreEntity extends Monster implements GeoEntity { return; } + this.tickQueenSummonInversionRule(); + boolean flying = !this.onGround(); if (flying != this.wasFlyingLastTick) { this.triggerAnim("controller", flying ? "fly_start" : "fly_land"); @@ -367,6 +377,24 @@ public class ManticoreEntity extends Monster implements GeoEntity { return AntarchyGravityApi.isGravityInverted(entity) ? new Vec3(0.0D, -1.0D, 0.0D) : new Vec3(0.0D, 1.0D, 0.0D); } + private void tickQueenSummonInversionRule() { + if (!this.isQueenSummoned() || this.canUseQueenSummonInversion()) { + return; + } + if (this.hasEffect(AntarchyObjects.INVERTED_EFFECT.get())) { + this.removeEffect(AntarchyObjects.INVERTED_EFFECT.get()); + } + if (AntarchyGravityApi.isGravityInverted(this) && AntarchyGravityApi.isGravityForced(this)) { + AntarchyGravityApi.setGravityDirection(this, AntarchyGravityDirection.DOWN, false); + } + this.setNoGravity(false); + } + + private boolean canUseQueenSummonInversion() { + return ThoraxisUndersideManager.isThoraxis(this.level()) + && this.getY() < ThoraxisUndersideManager.GRAVITY_FLIP_Y + 4; + } + void performSting(LivingEntity enemy) { this.swing(InteractionHand.MAIN_HAND); if (enemy.hurt(this.damageSources().mobAttack(this), (float) this.getAttributeValue(Attributes.ATTACK_DAMAGE))) { @@ -394,6 +422,14 @@ public class ManticoreEntity extends Monster implements GeoEntity { return false; } + @Override + public boolean canBeAffected(MobEffectInstance effectInstance) { + if (this.isQueenSummoned() && effectInstance.is(AntarchyObjects.INVERTED_EFFECT.get())) { + return this.canUseQueenSummonInversion(); + } + return super.canBeAffected(effectInstance); + } + @Override public boolean isInWall() { return false; diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/ReverieEntity.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/ReverieEntity.java index 6ce154af..27cf6706 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/ReverieEntity.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/ReverieEntity.java @@ -75,6 +75,7 @@ import software.bernie.geckolib.animation.RawAnimation; import software.bernie.geckolib.util.GeckoLibUtil; public class ReverieEntity extends PathfinderMob implements FlyingAnimal, GeoEntity { + private static final int DAMAGE_REACTION_DURATION_TICKS = 40; private static final EntityDataAccessor MOOD = SynchedEntityData.defineId(ReverieEntity.class, EntityDataSerializers.INT); private static final EntityDataAccessor ANIMATION_STATE = @@ -318,7 +319,7 @@ public class ReverieEntity extends PathfinderMob implements FlyingAnimal, GeoEnt this.releaseFocus(); this.clearNoticePlayer(); this.ambientTarget = null; - this.damageReactionTicks = 0; + this.damageReactionTicks = DAMAGE_REACTION_DURATION_TICKS; this.inversionReactionTicks = 0; this.transitionToMood(Mood.WHITE); } 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 b2a6b35c..4d42ccc7 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 @@ -38,6 +38,9 @@ import net.minecraft.world.entity.monster.Monster; import net.minecraft.world.entity.player.Player; import net.minecraft.world.level.Level; import net.minecraft.world.level.ServerLevelAccessor; +import net.minecraft.core.registries.Registries; +import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; import software.bernie.geckolib.animatable.GeoEntity; import software.bernie.geckolib.animatable.instance.AnimatableInstanceCache; import software.bernie.geckolib.animation.AnimatableManager; @@ -51,6 +54,10 @@ import org.jetbrains.annotations.Nullable; import java.util.UUID; public class ScorpionEntity extends Monster implements GeoEntity { + private static final ResourceKey DREAM_DUNES = ResourceKey.create( + Registries.BIOME, + ResourceLocation.fromNamespaceAndPath("antarchy", "dream_dunes") + ); private static final byte ATTACK_ANIM_EVENT = 4; private static final EntityDataAccessor TEXTURE_VARIANT = SynchedEntityData.defineId(ScorpionEntity.class, EntityDataSerializers.INT); @@ -81,6 +88,11 @@ public class ScorpionEntity extends Monster implements GeoEntity { this.xpReward = 5; } + @Override + public boolean fireImmune() { + return true; + } + public static AttributeSupplier.Builder createAttributes() { return Mob.createMobAttributes() .add(Attributes.MAX_HEALTH, AntarchySettings.scorpionHealth()) @@ -98,6 +110,12 @@ public class ScorpionEntity extends Monster implements GeoEntity { || spawnReason == MobSpawnType.COMMAND) { return true; } + // The Dream Dunes biome is the entire underside below Y=0. Its higher + // spawn weight is therefore targeted there without doubling the small + // surface-side Dream Dunes band as well. + if (pos.getY() >= 0 && level.getBiome(pos).is(DREAM_DUNES) && random.nextBoolean()) { + return false; + } if (ThoraxisUndersideManager.shouldSpawnInvertedOnDreamSand(level, pos)) { return level.getDifficulty() != Difficulty.PEACEFUL; } @@ -182,7 +200,8 @@ public class ScorpionEntity extends Monster implements GeoEntity { this.getLookControl().setLookAt(this.attackTarget, 30.0F, 30.0F); if (!this.attackDamageApplied - && this.attackAnimTicks == ATTACK_HIT_TICK + && this.attackAnimTicks > 0 + && this.attackAnimTicks <= ATTACK_HIT_TICK && this.distanceToSqr(this.attackTarget) <= this.getAttackReachSqr(this.attackTarget)) { this.attackDamageApplied = true; this.doHurtTarget(this.attackTarget); @@ -328,6 +347,10 @@ public class ScorpionEntity extends Monster implements GeoEntity { if (ScorpionEntity.this.attackTarget != null) { ScorpionEntity.this.getLookControl().setLookAt(ScorpionEntity.this.attackTarget, 30.0F, 30.0F); } + if (ScorpionEntity.this.isAttackLocked()) { + ScorpionEntity.this.getNavigation().stop(); + return; + } super.tick(); } @@ -341,8 +364,10 @@ public class ScorpionEntity extends Monster implements GeoEntity { if (ScorpionEntity.this.isAttackLocked()) { return; } - if (this.canPerformAttack(enemy) && this.isTimeToAttack() && ScorpionEntity.this.attackCooldownTicks <= 0) { - this.resetAttackCooldown(); + // Inverted melee tick handling replaces vanilla MeleeAttackGoal.tick, + // so vanilla's internal attack timer is not advanced. The Scorpion's + // own cooldown already covers its wind-up and hit timing. + if (this.canPerformAttack(enemy) && ScorpionEntity.this.attackCooldownTicks <= 0) { ScorpionEntity.this.beginAttack(enemy); return; } diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/StinkBugEntity.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/StinkBugEntity.java index 322b3571..c671999a 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/StinkBugEntity.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/StinkBugEntity.java @@ -15,6 +15,8 @@ import net.minecraft.world.entity.MobSpawnType; import net.minecraft.world.entity.ai.attributes.AttributeSupplier; import net.minecraft.world.entity.ai.attributes.Attributes; import net.minecraft.world.entity.ai.goal.FloatGoal; +import net.minecraft.world.entity.ai.goal.BreedGoal; +import net.minecraft.world.entity.ai.goal.TemptGoal; import net.minecraft.world.entity.ai.goal.LookAtPlayerGoal; import net.minecraft.world.entity.ai.goal.PanicGoal; import net.minecraft.world.entity.ai.goal.RandomLookAroundGoal; @@ -23,6 +25,8 @@ import net.minecraft.world.entity.animal.Animal; import net.minecraft.world.entity.player.Player; import net.minecraft.sounds.SoundEvent; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import net.minecraft.world.item.crafting.Ingredient; import net.minecraft.world.level.Level; import net.minecraft.world.level.ServerLevelAccessor; import net.minecraft.world.Difficulty; @@ -86,9 +90,11 @@ public class StinkBugEntity extends Animal implements GeoEntity { protected void registerGoals() { this.goalSelector.addGoal(0, new FloatGoal(this)); this.goalSelector.addGoal(1, new PanicGoal(this, 1.4D)); - this.goalSelector.addGoal(2, new WaterAvoidingRandomStrollGoal(this, 0.9D)); - this.goalSelector.addGoal(3, new LookAtPlayerGoal(this, Player.class, 6.0F)); - this.goalSelector.addGoal(4, new RandomLookAroundGoal(this)); + this.goalSelector.addGoal(2, new TemptGoal(this, 1.0D, Ingredient.of(Items.ROTTEN_FLESH), false)); + this.goalSelector.addGoal(3, new BreedGoal(this, 0.9D)); + this.goalSelector.addGoal(4, new WaterAvoidingRandomStrollGoal(this, 0.9D)); + this.goalSelector.addGoal(5, new LookAtPlayerGoal(this, Player.class, 6.0F)); + this.goalSelector.addGoal(6, new RandomLookAroundGoal(this)); } @Override @@ -118,7 +124,7 @@ public class StinkBugEntity extends Animal implements GeoEntity { @Override public boolean isFood(ItemStack stack) { - return false; + return stack.is(Items.ROTTEN_FLESH); } @Override diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/ant/AntTeleportHelper.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/ant/AntTeleportHelper.java index 3e713c36..8d132000 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/ant/AntTeleportHelper.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/ant/AntTeleportHelper.java @@ -313,7 +313,10 @@ public final class AntTeleportHelper { return new int[]{100, 200}; } if (dim == AntarchySettings.brownAntDestinationDimension()) { - return new int[]{75, 120}; + return new int[]{60, 240}; + } + if (dim == AntarchySettings.redAntDestinationDimension()) { + return new int[]{0, 220}; } return null; } @@ -328,7 +331,7 @@ public final class AntTeleportHelper { } int x = preferredPos.getX() + xOff; int z = preferredPos.getZ() + zOff; - for (int y = maxY; y >= minY + 1; y--) { + for (int y = minY + 1; y <= maxY; y++) { Vec3 safePos = tryFindSafeDismount(player, destination, new BlockPos(x, y, z)); if (safePos != null && safePos.y >= minY && safePos.y <= maxY) { return safePos; diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/kraken/KrakenEntity.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/kraken/KrakenEntity.java index 7e848017..22f3a19e 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/kraken/KrakenEntity.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/kraken/KrakenEntity.java @@ -1035,7 +1035,8 @@ public class KrakenEntity extends Monster implements GeoEntity, MultipartEntityO for (int i = 0; i < strikeBolts; i++) { this.spawnVisualLightning(serverLevel, this.getTargetStormStrikePos(target)); } - target.hurt(AntarchyDamageSources.krakenLightning(serverLevel, this), this.isPhaseTwo() ? 12.0F : 8.0F); + target.hurt(AntarchyDamageSources.krakenLightning(serverLevel, this), + this.isPhaseTwo() ? 12.0F : AntarchySettings.krakenLightningDamagePhaseOne()); } } else { this.lightningStrikeCooldown = this.isPhaseTwo() ? 30 : 60; 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 cae62d95..2e06f84e 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 @@ -1,5 +1,6 @@ package com.craisinlord.antarchy.content.entity.portal; +import com.craisinlord.antarchy.Antarchy; import com.craisinlord.antarchy.config.AntarchySettings; import com.craisinlord.antarchy.content.AntarchyObjects; import com.craisinlord.antarchy.content.AntarchySoundEvents; @@ -176,6 +177,13 @@ public class DimensionalTearEntity extends Entity implements GeoEntity { DimensionalTearEntity linked = this.findLinkedTear(); if (linked == null || !linked.isAlive()) { + Antarchy.LOGGER.warn( + "[antarchy-dimensional-tear] discarding unlinked tear uuid={} pos=({}, {}, {}) dim={} linkedTearId={} fallback=({}, {}, {}) ageTicks={} lifetimeTicks={}", + this.getUUID(), this.getX(), this.getY(), this.getZ(), this.level().dimension().location(), + this.linkedTearId, + this.linkedFallbackPos.x, this.linkedFallbackPos.y, this.linkedFallbackPos.z, + this.ageTicks, this.lifetimeTicks + ); this.discard(); return; } @@ -264,7 +272,7 @@ public class DimensionalTearEntity extends Entity implements GeoEntity { manticore.setTarget(target); } manticore.setDeltaMovement(this.getViewVector(1.0F).scale(0.2D).add(0.0D, 0.08D, 0.0D)); - manticore.setNoGravity(true); + manticore.setNoGravity(false); level.addFreshEntity(manticore); } } @@ -348,6 +356,7 @@ public class DimensionalTearEntity extends Entity implements GeoEntity { private void teleportEntity(Entity entity, DimensionalTearEntity destination) { Vec3 motion = entity.getDeltaMovement(); + Vec3 startPos = entity.position(); Vec3 exitPos = destination.exitPosition(); TELEPORT_COOLDOWNS.put(entity.getUUID(), this.level().getGameTime() + TELEPORT_COOLDOWN_TICKS); @@ -360,7 +369,15 @@ public class DimensionalTearEntity extends Entity implements GeoEntity { entity.setDeltaMovement(motion); entity.hasImpulse = true; if (entity instanceof LivingEntity living) { - living.addEffect(new MobEffectInstance(AntarchyObjects.INVERTED_EFFECT.get(), AntarchySettings.dimensionalTearInvertedDurationTicks(), 0)); + int invertedDuration = AntarchySettings.dimensionalTearInvertedDurationTicks(); + Antarchy.LOGGER.info( + "[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, + exitPos.x, exitPos.y, exitPos.z, + this.level().dimension().location(), invertedDuration + ); + living.addEffect(new MobEffectInstance(AntarchyObjects.INVERTED_EFFECT.get(), invertedDuration, 0)); } this.level().playSound(null, this.blockPosition(), SoundEvents.ENDERMAN_TELEPORT, SoundSource.NEUTRAL, 0.7F, 0.65F + this.random.nextFloat() * 0.2F); } @@ -516,6 +533,16 @@ public class DimensionalTearEntity extends Entity implements GeoEntity { this.queenManticoreCount = tag.getInt("QueenManticoreCount"); this.queenManticoreSpawned = tag.getBoolean("QueenManticoreSpawned"); } + if (!this.level().isClientSide) { + Antarchy.LOGGER.info( + "[antarchy-dimensional-tear] loaded tear uuid={} pos=({}, {}, {}) dim={} linkedTearId={} fallback=({}, {}, {}) ageTicks={} lifetimeTicks={} eventTicks={} nextEventTicks={} pendingEvent={} queenOwner={} queenManticoreCount={} queenManticoreSpawned={}", + this.getUUID(), this.getX(), this.getY(), this.getZ(), this.level().dimension().location(), + this.linkedTearId, + this.linkedFallbackPos.x, this.linkedFallbackPos.y, this.linkedFallbackPos.z, + this.ageTicks, this.lifetimeTicks, this.eventTicks, this.nextEventTicks, this.pendingEvent, + this.queenOwnerId, this.queenManticoreCount, this.queenManticoreSpawned + ); + } } @Override 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 1da0efc6..26771fbe 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 @@ -1,6 +1,7 @@ package com.craisinlord.antarchy.content.entity.royal; import com.craisinlord.antarchy.config.AntarchySettings; +import com.craisinlord.antarchy.content.AntarchyObjects; import com.craisinlord.antarchy.content.AntarchySoundEvents; import net.minecraft.sounds.SoundEvent; import net.minecraft.sounds.SoundEvents; @@ -35,7 +36,6 @@ import org.jetbrains.annotations.Nullable; 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.decree.AdvanceDecree; import com.craisinlord.antarchy.content.entity.royal.decree.CloseQuartersDecree; import com.craisinlord.antarchy.content.entity.royal.decree.ComeNoCloserDecree; import com.craisinlord.antarchy.content.entity.royal.decree.DoNotRunDecree; @@ -50,6 +50,7 @@ import com.craisinlord.antarchy.content.entity.royal.decree.ShowNoMercyDecree; import com.craisinlord.antarchy.content.entity.royal.decree.SkyIsMineDecree; import com.craisinlord.antarchy.content.entity.royal.decree.StandTallDecree; import com.craisinlord.antarchy.content.entity.royal.decree.StandYourGroundDecree; +import com.craisinlord.antarchy.content.entity.royal.attack.RoyalAttackLane; import java.util.EnumMap; import java.util.List; import java.util.HashMap; @@ -79,9 +80,10 @@ public class KingEntity extends RoyalBossEntity { private int decreeCooldownTicks; private int activeDecreeTicks; private int stompCooldownTicks; - private int stompWindupTicks; private int wingGustCooldownTicks; - private int wingGustWindupTicks; + private int fireballCooldownTicks = 80; + private int iceballCooldownTicks = 120; + private int iceSpikeCooldownTicks = 160; private boolean decreeRetreatPressure; private final Map judgmentCooldowns = new HashMap<>(); private final Map lastPlayerDamageTime = new HashMap<>(); @@ -91,9 +93,9 @@ public class KingEntity extends RoyalBossEntity { private RoyalDecree activeDecree; private static final List DECREES = List.of( new SkyIsMineDecree(), new DoNotRunDecree(), new KneelDecree(), new CloseQuartersDecree(), - new ComeNoCloserDecree(), new FightMeCowardDecree(), new NoRespiteDecree(), new NoRetreatDecree(), - new KeepYourDistanceDecree(), new StandTallDecree(), new StandYourGroundDecree(), - new HandsOffTheCrownDecree(), new AdvanceDecree(), new ShowNoMercyDecree()); + new ComeNoCloserDecree(), new FightMeCowardDecree(), new NoRespiteDecree(), + new StandTallDecree(), new StandYourGroundDecree(), + new HandsOffTheCrownDecree(), new ShowNoMercyDecree()); public KingEntity(EntityType entityType, Level level) { super(entityType, level); @@ -133,6 +135,11 @@ public class KingEntity extends RoyalBossEntity { return AntarchySoundEvents.KING_DEATH.get(); } + @Override + protected SoundEvent royalFlyLoopSound() { + return AntarchySoundEvents.KING_FLY_LOOP.get(); + } + @Override protected SoundEvent royalBiteSound() { return AntarchySoundEvents.KING_BITE.get(); @@ -160,7 +167,7 @@ public class KingEntity extends RoyalBossEntity { @Override protected RoyalBeamSettings royalBeamSettings() { return new RoyalBeamSettings(AntarchySettings.kingBeamRange(), AntarchySettings.kingBeamTracking(), 7.5D, - AntarchySettings.kingBeamDurationTicks(), AntarchySettings.kingBeamCooldownTicks(), 6.0F, 6.0F, + AntarchySettings.kingBeamDurationTicks(), AntarchySettings.kingBeamTravelTicks(), AntarchySettings.kingBeamCooldownTicks(), 6.0F, 6.0F, (float) AntarchySettings.kingBeamDamage(), 1.0F, 3, 100.0D, (float) AntarchySettings.kingBeamTerrainRadius(), 4.0F, AntarchySettings.kingBeamTerrainCap(), 1.0F, 0.08F, 15.0F, true, true); @@ -194,13 +201,13 @@ public class KingEntity extends RoyalBossEntity { return; } var particle = switch (head.slot()) { - case LEFT -> ParticleTypes.FLAME; + case LEFT -> null; case CENTER -> ParticleTypes.ELECTRIC_SPARK; case RIGHT -> ParticleTypes.SNOWFLAKE; }; Vec3 direction = end.subtract(start); double distance = direction.length(); - if (distance > 0.001D) { + if (particle != null && distance > 0.001D) { direction = direction.scale(1.0D / distance); for (double walked = 0.0D; walked <= distance; walked += 2.5D) { Vec3 point = start.add(direction.scale(walked)); @@ -311,7 +318,7 @@ public class KingEntity extends RoyalBossEntity { } if (this.patrolCooldownTicks-- <= 0) { this.patrolCooldownTicks = 100 + this.random.nextInt(100); - this.playSound(AntarchySoundEvents.KING_WING_FLAP.get(), 2.0F, 0.9F + this.random.nextFloat() * 0.15F); + this.playRoyalSound(AntarchySoundEvents.KING_WING_FLAP.get(), 0.9F + this.random.nextFloat() * 0.15F); double angle = this.random.nextDouble() * Math.PI * 2.0D; double radius = 12.0D + this.random.nextDouble() * 18.0D; double px = this.patrolCenter.x + Math.cos(angle) * radius; @@ -347,37 +354,41 @@ public class KingEntity extends RoyalBossEntity { } private void tickKingWholeBody(ServerLevel level, LivingEntity target) { - if (this.stompCooldownTicks > 0) this.stompCooldownTicks--; - if (this.wingGustCooldownTicks > 0) this.wingGustCooldownTicks--; - boolean skyIsMine = this.activeDecree instanceof SkyIsMineDecree; boolean forceFar = this.decreeRetreatPressure || this.activeDecree instanceof ComeNoCloserDecree || this.activeDecree instanceof KeepYourDistanceDecree; - if (this.stompWindupTicks > 0) { - if (--this.stompWindupTicks == 0) { - this.performStomp(level, target); - } - } else if (this.wingGustWindupTicks > 0) { - if (--this.wingGustWindupTicks == 0) { - this.performWingGust(level, target); - } - } else { + { double distance = target.distanceTo(this); - if (this.stompCooldownTicks <= 0 && (distance <= STOMP_RADIUS || skyIsMine)) { - this.stompCooldownTicks = skyIsMine ? STOMP_COOLDOWN_TICKS / 2 : STOMP_COOLDOWN_TICKS; - this.stompWindupTicks = STOMP_WINDUP_TICKS; - this.triggerAnim("body_action", "stomp"); - this.playSound(AntarchySoundEvents.KING_ROAR.get(), 3.0F, 0.7F); + if (this.attackScheduler.ready("stomp", RoyalAttackLane.BODY) + && (distance <= STOMP_RADIUS || skyIsMine) + && this.beginRoyalAttack("stomp", RoyalAttackLane.BODY, + skyIsMine ? STOMP_COOLDOWN_TICKS / 2 : STOMP_COOLDOWN_TICKS, + STOMP_WINDUP_TICKS, 1, 18, new com.craisinlord.antarchy.content.entity.royal.attack.RoyalAttackScheduler.Action() { + @Override public void onStart() { + KingEntity.this.triggerAnim("body_action", "stomp"); + KingEntity.this.playRoyalSound(AntarchySoundEvents.KING_ROAR.get(), 0.7F); + } + @Override public void onActive(int elapsedTicks) { + KingEntity.this.performStomp(level, target); + } + })) { if (skyIsMine) { this.getMoveControl().setWantedPosition(this.getX(), target.getY(), this.getZ(), 1.2D); } - } else if (this.wingGustCooldownTicks <= 0 && distance <= WING_GUST_RADIUS && (forceFar || this.random.nextInt(3) == 0)) { - this.wingGustCooldownTicks = WING_GUST_COOLDOWN_TICKS; - this.wingGustWindupTicks = WING_GUST_WINDUP_TICKS; - this.triggerAnim("body_action", "wing_gust"); - this.playSound(AntarchySoundEvents.KING_WING_FLAP.get(), 3.0F, 0.85F); + } else if (this.attackScheduler.ready("wing_gust", RoyalAttackLane.BODY) + && distance <= WING_GUST_RADIUS && (forceFar || this.random.nextInt(3) == 0) + && 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.playRoyalSound(AntarchySoundEvents.KING_WING_FLAP.get(), 0.85F); + } + @Override public void onActive(int elapsedTicks) { + KingEntity.this.performWingGust(level, target); + } + })) { } } @@ -388,6 +399,101 @@ public class KingEntity extends RoyalBossEntity { double ay = this.groundYBelow(ax, az) + FLYING_PREFERRED_HOVER + 6.0D; this.getMoveControl().setWantedPosition(ax, ay, az, 1.3D); } + + this.tickKingElementalProjectiles(level, target); + } + + private void tickKingElementalProjectiles(ServerLevel level, LivingEntity target) { + if (this.fireballCooldownTicks > 0) this.fireballCooldownTicks--; + if (this.iceballCooldownTicks > 0) this.iceballCooldownTicks--; + if (this.iceSpikeCooldownTicks > 0) this.iceSpikeCooldownTicks--; + + double distance = this.distanceTo(target); + String[] candidates = {"fireball", "iceball", "ice_spikes"}; + String selected = this.attackScheduler.chooseWeighted(candidates, id -> { + boolean ready = switch (id) { + case "fireball" -> distance >= 14.0D && distance <= 48.0D && this.fireballCooldownTicks <= 0 + && this.royalHead(RoyalHead.Slot.LEFT).readyToAttack(); + case "iceball" -> distance >= 10.0D && distance <= 42.0D && this.iceballCooldownTicks <= 0 + && this.royalHead(RoyalHead.Slot.RIGHT).readyToAttack(); + default -> distance <= 36.0D && this.iceSpikeCooldownTicks <= 0 + && this.royalHead(RoyalHead.Slot.RIGHT).readyToAttack(); + }; + if (!ready) return 0; + return switch (id) { + case "fireball" -> this.behaviorScore(Behavior.KEEPING_FAR) + 4; + case "iceball" -> this.behaviorScore(Behavior.RETREATING) + 4; + default -> this.behaviorScore(Behavior.HUGGING) + 3; + }; + }, this.random); + if (selected == null) return; + if ("fireball".equals(selected)) { + if (!this.beginRoyalAttack("fireball", RoyalAttackLane.LEFT_HEAD, + AntarchySettings.kingFireballCooldownTicks(), 14, 1, 5, + new com.craisinlord.antarchy.content.entity.royal.attack.RoyalAttackScheduler.Action() { + @Override public void onStart() { + KingEntity.this.triggerAnim(RoyalHead.Slot.LEFT.controllerName(), "shoot"); + } + @Override public void onActive(int elapsedTicks) { + Vec3 origin = KingEntity.this.headAnchor(KingEntity.this.royalHead(RoyalHead.Slot.LEFT)); + Vec3 predicted = target.position().add(target.getDeltaMovement().scale(10.0D)); + Vec3 direction = predicted.add(0.0D, target.getBbHeight() * 0.5D, 0.0D).subtract(origin).normalize(); + RoyalElementalProjectileEntity fireball = RoyalElementalProjectileEntity.create(level, KingEntity.this, + RoyalBeamElement.FIRE, origin, direction); + level.addFreshEntity(fireball); + KingEntity.this.playRoyalSound(AntarchySoundEvents.KING_FIREBALL_SHOOT.get(), 0.9F); + } + })) return; + this.fireballCooldownTicks = AntarchySettings.kingFireballCooldownTicks(); + } else if ("iceball".equals(selected)) { + if (!this.beginRoyalAttack("iceball", RoyalAttackLane.RIGHT_HEAD, + AntarchySettings.kingIceballCooldownTicks(), 16, 1, 5, + new com.craisinlord.antarchy.content.entity.royal.attack.RoyalAttackScheduler.Action() { + @Override public void onStart() { + KingEntity.this.triggerAnim(RoyalHead.Slot.RIGHT.controllerName(), "shoot"); + } + @Override public void onActive(int elapsedTicks) { + Vec3 origin = KingEntity.this.headAnchor(KingEntity.this.royalHead(RoyalHead.Slot.RIGHT)); + Vec3 direction = target.getEyePosition().subtract(origin).normalize(); + RoyalElementalProjectileEntity iceball = RoyalElementalProjectileEntity.create(level, KingEntity.this, + RoyalBeamElement.ICE, origin, direction); + level.addFreshEntity(iceball); + KingEntity.this.playRoyalSound(AntarchySoundEvents.KING_BEAM_SHOOT.get(), 1.2F); + } + })) return; + this.iceballCooldownTicks = AntarchySettings.kingIceballCooldownTicks(); + } else { + if (!this.beginRoyalAttack("ice_spikes", RoyalAttackLane.RIGHT_HEAD, + AntarchySettings.kingIceSpikeCooldownTicks(), 20, 1, 5, + new com.craisinlord.antarchy.content.entity.royal.attack.RoyalAttackScheduler.Action() { + @Override public void onStart() { + KingEntity.this.triggerAnim(RoyalHead.Slot.RIGHT.controllerName(), "shoot"); + } + @Override public void onActive(int elapsedTicks) { + KingEntity.this.raiseIceSpikes(level, target); + KingEntity.this.playRoyalSound(AntarchySoundEvents.KING_BEAM_SHOOT.get(), 0.8F); + } + })) return; + this.iceSpikeCooldownTicks = AntarchySettings.kingIceSpikeCooldownTicks(); + } + } + + private void raiseIceSpikes(ServerLevel level, LivingEntity target) { + Vec3 start = this.position(); + Vec3 end = target.position(); + Vec3 line = end.subtract(start).multiply(1.0D, 0.0D, 1.0D); + if (line.lengthSqr() < 1.0E-4D) return; + line = line.normalize(); + Vec3 side = new Vec3(-line.z, 0.0D, line.x); + for (int i = 1; i <= 5; i++) { + double distance = i * 4.0D; + for (int lane = -1; lane <= 1; lane++) { + Vec3 point = start.add(line.scale(distance)).add(side.scale(lane * (i % 2 == 0 ? 2.0D : 0.0D))); + double y = this.groundYBelow(point.x, point.z) + 0.1D; + RoyalIceSpikeEntity spike = RoyalIceSpikeEntity.create(level, new Vec3(point.x, y, point.z)); + level.addFreshEntity(spike); + } + } } private void performStomp(ServerLevel level, LivingEntity target) { @@ -406,7 +512,7 @@ public class KingEntity extends RoyalBossEntity { RoyalBlockDestruction.destroySphere(level, this, new Vec3(this.getX(), this.getY(), this.getZ()), STOMP_RADIUS * 0.5D, 96, 80.0D, 0.15F); level.sendParticles(ParticleTypes.EXPLOSION_EMITTER, this.getX(), this.getY(), this.getZ(), 6, 4.0D, 0.5D, 4.0D, 0.0D); - this.playSound(AntarchySoundEvents.KING_STOMP.get(), 3.5F, 0.8F); + this.playRoyalSound(AntarchySoundEvents.KING_STOMP.get(), 0.8F); } private void performWingGust(ServerLevel level, LivingEntity target) { @@ -434,11 +540,15 @@ public class KingEntity extends RoyalBossEntity { if (this.decreeCooldownTicks-- > 0) { return; } + if (!this.beginRoyalAttack("decree", RoyalAttackLane.DECREE, + AntarchySettings.royalDecreeCooldownTicks(), 300)) { + return; + } this.activeDecreeTicks = 300; this.activeDecree = this.pickDecree(target); this.decreeRetreatPressure = false; - this.playSound(AntarchySoundEvents.KING_DECREE_CAST.get(), 3.0F, 0.9F + this.random.nextFloat() * 0.15F); - this.playSound(AntarchySoundEvents.KING_ROAR.get(), 3.0F, 0.8F + this.random.nextFloat() * 0.12F); + 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); if (target instanceof ServerPlayer player) { player.connection.send(new ClientboundSetTitlesAnimationPacket(5, 300, 10)); player.connection.send(new ClientboundSetTitleTextPacket(Component.translatable(this.activeDecree.translationKey()) @@ -452,6 +562,11 @@ public class KingEntity extends RoyalBossEntity { level.playSound(null, target.blockPosition(), SoundEvents.BELL_BLOCK, SoundSource.HOSTILE, 0.8F, 1.0F + countdown / 500.0F); } this.activeDecree.apply(level, this, target); + if (this.activeDecree != null + && this.activeDecree.evaluate(level, this, target) == RoyalDecree.Evaluation.VIOLATED) { + this.failActiveDecree(target); + return; + } if (this.activeDecree != null && --this.activeDecreeTicks <= 0) { this.endDecree(target); } @@ -495,6 +610,15 @@ 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; + } + this.invokeJudgment(target); + this.endDecree(target); + } + public boolean blocksHealing() { return this.activeDecree instanceof NoRespiteDecree; } @@ -520,11 +644,11 @@ public class KingEntity extends RoyalBossEntity { boolean projectile = source.getDirectEntity() instanceof Projectile; this.bumpBehavior(projectile ? Behavior.RANGED : Behavior.HUGGING, 2); if (this.activeDecree instanceof CloseQuartersDecree && projectile) { - this.invokeJudgment(attacker); + this.failActiveDecree(attacker); } else if (this.activeDecree instanceof ComeNoCloserDecree && !projectile) { - this.invokeJudgment(attacker); + this.failActiveDecree(attacker); } else if (this.activeDecree instanceof HandsOffTheCrownDecree) { - this.invokeJudgment(attacker); + this.failActiveDecree(attacker); } } return super.hurt(source, amount); 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 c1fc60d1..c10e2da1 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 @@ -30,6 +30,8 @@ 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.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; @@ -61,13 +63,12 @@ public class QueenEntity extends RoyalBossEntity { private int gravityStompCooldownTicks; private int timeFieldCooldownTicks; private int momentumLockCooldownTicks; - private int momentumLockTicks; private int crushingGravityCooldownTicks; - private int crushingGravityTicks; private int accelerationCooldownTicks; - private int accelerationTicks; private int blackHoleCooldownTicks; private int idleWanderCooldownTicks; + private int finalTimeControlTicks; + private final RoyalEffectController royalEffects = new RoyalEffectController(); private final Map frozenVelocities = new HashMap<>(); public QueenEntity(EntityType entityType, Level level) { @@ -85,7 +86,12 @@ public class QueenEntity extends RoyalBossEntity { @Override protected boolean isFlyingBoss() { - return false; + return true; + } + + @Override + protected SoundEvent royalFlyLoopSound() { + return AntarchySoundEvents.QUEEN_FLY_LOOP.get(); } @Override @@ -128,7 +134,7 @@ public class QueenEntity extends RoyalBossEntity { @Override protected RoyalBeamSettings royalBeamSettings() { return new RoyalBeamSettings(AntarchySettings.queenBeamRange(), AntarchySettings.queenBeamTracking(), 7.5D, - AntarchySettings.queenBeamDurationTicks(), AntarchySettings.queenBeamCooldownTicks(), 6.0F, 6.0F, + AntarchySettings.queenBeamDurationTicks(), AntarchySettings.queenBeamTravelTicks(), AntarchySettings.queenBeamCooldownTicks(), 6.0F, 6.0F, (float) AntarchySettings.queenBeamDamage(), 1.0F, 3, 100.0D, (float) AntarchySettings.queenBeamTerrainRadius(), 4.0F, AntarchySettings.queenBeamTerrainCap(), 1.0F, 0.08F, 15.0F, true, true); @@ -156,9 +162,8 @@ public class QueenEntity extends RoyalBossEntity { } this.tickManticoreSummon(); this.tickQueenAbilities(); - this.tickRoyalAcceleration(); - this.tickMomentumLock(); - this.tickCrushingGravity(); + this.tickFinalTimeControl(); + this.royalEffects.tick(); } private void tickIdleWander() { @@ -174,7 +179,7 @@ public class QueenEntity extends RoyalBossEntity { } private int cooldown(int base) { - return this.accelerationTicks > 0 ? Math.max(20, base / 2) : base; + return this.royalEffects.active("acceleration") ? Math.max(20, base / 2) : base; } private void tickManticoreSummon() { @@ -205,7 +210,7 @@ public class QueenEntity extends RoyalBossEntity { : FAILED_SUMMON_RETRY_TICKS; if (spawned > 0) { this.triggerAnim("body_action", "minion_spawn"); - this.playSound(AntarchySoundEvents.QUEEN_ROAR.get(), 3.0F, 0.8F + this.random.nextFloat() * 0.12F); + this.playRoyalSound(AntarchySoundEvents.QUEEN_ROAR.get(), 0.8F + this.random.nextFloat() * 0.12F); serverLevel.sendParticles(ParticleTypes.PORTAL, this.getX(), this.getY() + 2.0D, this.getZ(), 36, 3.0D, 2.0D, 3.0D, 0.15D); } @@ -249,40 +254,97 @@ public class QueenEntity extends RoyalBossEntity { } Phase phase = this.phase(); - boolean majorBusy = this.momentumLockTicks > 0 || this.crushingGravityTicks > 0; + 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"}; + String selected = this.attackScheduler.chooseWeighted(candidates, id -> { + if (!this.queenAttackReady(id, phase, majorBusy, allowOverlap)) return 0; + return switch (id) { + case "black_hole", "crushing_gravity" -> phase == Phase.THREE ? 5 : 3; + case "momentum_lock", "acceleration" -> phase == Phase.THREE ? 4 : 2; + default -> 4; + }; + }, this.random); + 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), + 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"); + } + } + @Override public void onActive(int elapsedTicks) { + QueenEntity.this.performSelectedQueenAttack(selected, phase, cooldown, level, target); + } + })) return; + } - if (this.gravityStompCooldownTicks <= 0 && (allowOverlap || !majorBusy)) { - this.gravityStompCooldownTicks = this.cooldown(GRAVITY_STOMP_COOLDOWN); - this.performGravityStomp(level, target); + private void performSelectedQueenAttack(String selected, Phase phase, 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" -> { + this.crushingGravityCooldownTicks = cooldown; + this.royalEffects.start("crushing_gravity", CRUSHING_GRAVITY_DURATION, + () -> {}, this::tickCrushingGravity, () -> {}); + this.playRoyalSound(AntarchySoundEvents.QUEEN_ROAR.get(), 0.6F); + } + case "acceleration" -> { this.accelerationCooldownTicks = cooldown; this.startRoyalAcceleration(); } } - if (this.timeFieldCooldownTicks <= 0 && (allowOverlap || !majorBusy)) { - this.timeFieldCooldownTicks = this.cooldown(TIME_FIELD_COOLDOWN); - double fieldRate = phase == Phase.THREE ? 0.1D : 0.4D; - TimeDilationApi.createField(level, target.position(), 12.0D, fieldRate, 140 + phase.ordinal() * 40); - this.triggerAnim("body_action", "wing_gust"); + } + + private boolean queenAttackReady(String id, Phase phase, boolean majorBusy, boolean allowOverlap) { + 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 "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; + default -> false; + }; + } + + private void tickFinalTimeControl() { + if (this.phase() != Phase.THREE || !(this.level() instanceof ServerLevel level)) { + return; } - if (this.blackHoleCooldownTicks <= 0 && (allowOverlap || !majorBusy)) { - this.blackHoleCooldownTicks = this.cooldown(BLACK_HOLE_COOLDOWN); - this.castBlackHole(level, target); - } - if (this.momentumLockTicks <= 0 && this.momentumLockCooldownTicks <= 0 && (allowOverlap || !majorBusy)) { - this.startMomentumLock(level); - } - if (this.crushingGravityTicks <= 0 && this.crushingGravityCooldownTicks <= 0 && phase != Phase.ONE) { - this.crushingGravityCooldownTicks = CRUSHING_GRAVITY_COOLDOWN; - this.crushingGravityTicks = CRUSHING_GRAVITY_DURATION; - this.playSound(AntarchySoundEvents.QUEEN_ROAR.get(), 3.0F, 0.6F); - } - if (this.accelerationTicks <= 0 && this.accelerationCooldownTicks <= 0 - && (phase == Phase.THREE || (phase == Phase.TWO && this.random.nextInt(3) == 0))) { + 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) { this.triggerAnim("body_action", "stomp"); - this.playSound(AntarchySoundEvents.QUEEN_ROAR.get(), 3.0F, 0.7F); + 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())) { @@ -303,30 +365,36 @@ public class QueenEntity extends RoyalBossEntity { RoyalBlackHoleEntity hole = RoyalBlackHoleEntity.create(level, anchor, this.getUUID()); level.addFreshEntity(hole); this.triggerAnim("body_action", "wing_gust"); - this.playSound(AntarchySoundEvents.QUEEN_ROAR.get(), 3.0F, 1.15F); + 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); } private void startMomentumLock(ServerLevel level) { this.momentumLockCooldownTicks = MOMENTUM_LOCK_COOLDOWN; - this.momentumLockTicks = MOMENTUM_LOCK_DURATION; - this.frozenVelocities.clear(); - this.triggerAnim("body_action", "wing_gust"); - this.playSound(AntarchySoundEvents.QUEEN_ROAR.get(), 3.0F, 0.5F); - for (Entity entity : level.getEntitiesOfClass(Entity.class, - this.getBoundingBox().inflate(MOMENTUM_LOCK_RADIUS))) { - if (entity == this || isFieldImmune(entity) || entity instanceof Player player && player.isCreative()) { - continue; + this.royalEffects.start("momentum_lock", MOMENTUM_LOCK_DURATION, () -> { + this.frozenVelocities.clear(); + for (Entity entity : level.getEntitiesOfClass(Entity.class, + this.getBoundingBox().inflate(MOMENTUM_LOCK_RADIUS))) { + if (entity == this || isFieldImmune(entity) || entity instanceof Player player && player.isCreative()) { + continue; + } + this.frozenVelocities.put(entity.getUUID(), entity.getDeltaMovement()); } - this.frozenVelocities.put(entity.getUUID(), entity.getDeltaMovement()); - } + }, this::tickMomentumLockEffect, () -> { + for (Map.Entry entry : this.frozenVelocities.entrySet()) { + Entity entity = level.getEntity(entry.getKey()); + if (entity != null && entity.isAlive()) { + entity.setDeltaMovement(entry.getValue()); + entity.hasImpulse = true; + } + } + this.frozenVelocities.clear(); + }); + this.triggerAnim("body_action", "wing_gust"); + this.playRoyalSound(AntarchySoundEvents.QUEEN_ROAR.get(), 0.5F); } - private void tickMomentumLock() { - if (this.momentumLockTicks <= 0) { - return; - } - this.momentumLockTicks--; + private void tickMomentumLockEffect() { if (!(this.level() instanceof ServerLevel level)) { return; } @@ -335,29 +403,20 @@ public class QueenEntity extends RoyalBossEntity { if (entity == null || !entity.isAlive()) { continue; } - if (this.momentumLockTicks <= 0) { - entity.setDeltaMovement(entry.getValue()); - entity.hasImpulse = true; - } else { - entity.setDeltaMovement(Vec3.ZERO); - entity.fallDistance = 0.0F; - entity.hasImpulse = true; - if (this.tickCount % 6 == 0 && level instanceof ServerLevel serverLevel) { - serverLevel.sendParticles(ParticleTypes.END_ROD, entity.getX(), entity.getY() + entity.getBbHeight() * 0.5D, entity.getZ(), - 2, 0.2D, 0.2D, 0.2D, 0.0D); - } + entity.setDeltaMovement(Vec3.ZERO); + entity.fallDistance = 0.0F; + entity.hasImpulse = true; + if (this.tickCount % 6 == 0) { + level.sendParticles(ParticleTypes.END_ROD, entity.getX(), entity.getY() + entity.getBbHeight() * 0.5D, entity.getZ(), + 2, 0.2D, 0.2D, 0.2D, 0.0D); } } - if (this.momentumLockTicks <= 0) { - this.frozenVelocities.clear(); - } } private void tickCrushingGravity() { - if (this.crushingGravityTicks <= 0) { + if (!this.royalEffects.active("crushing_gravity")) { return; } - this.crushingGravityTicks--; if (!(this.level() instanceof ServerLevel level)) { return; } @@ -378,11 +437,16 @@ public class QueenEntity extends RoyalBossEntity { private void startRoyalAcceleration() { this.accelerationCooldownTicks = ACCELERATION_COOLDOWN; - this.accelerationTicks = ACCELERATION_DURATION; - this.setRoyalAccelerated(true); - this.playSound(AntarchySoundEvents.QUEEN_ROAR.get(), 3.5F, 1.4F); - applyAccelModifier(this.getAttribute(Attributes.MOVEMENT_SPEED), ACCEL_SPEED_ID); - applyAccelModifier(this.getAttribute(Attributes.FLYING_SPEED), ACCEL_FLY_ID); + this.royalEffects.start("acceleration", ACCELERATION_DURATION, () -> { + this.setRoyalAccelerated(true); + this.playRoyalSound(AntarchySoundEvents.QUEEN_ROAR.get(), 1.4F); + applyAccelModifier(this.getAttribute(Attributes.MOVEMENT_SPEED), ACCEL_SPEED_ID); + applyAccelModifier(this.getAttribute(Attributes.FLYING_SPEED), ACCEL_FLY_ID); + }, this::tickRoyalAccelerationEffect, () -> { + this.setRoyalAccelerated(false); + removeAccelModifier(this.getAttribute(Attributes.MOVEMENT_SPEED), ACCEL_SPEED_ID); + removeAccelModifier(this.getAttribute(Attributes.FLYING_SPEED), ACCEL_FLY_ID); + }); } private static void applyAccelModifier(AttributeInstance attribute, ResourceLocation id) { @@ -398,16 +462,10 @@ public class QueenEntity extends RoyalBossEntity { } } - private void tickRoyalAcceleration() { - if (this.accelerationTicks <= 0) { - if (this.isRoyalAccelerated()) { - this.setRoyalAccelerated(false); - removeAccelModifier(this.getAttribute(Attributes.MOVEMENT_SPEED), ACCEL_SPEED_ID); - removeAccelModifier(this.getAttribute(Attributes.FLYING_SPEED), ACCEL_FLY_ID); - } + private void tickRoyalAccelerationEffect() { + if (!this.isRoyalAccelerated()) { return; } - this.accelerationTicks--; if (this.level() instanceof ServerLevel level && this.tickCount % 2 == 0) { level.sendParticles(ACCEL_DUST, this.getX() + (this.random.nextDouble() - 0.5D) * this.getBbWidth(), @@ -415,11 +473,6 @@ public class QueenEntity extends RoyalBossEntity { this.getZ() + (this.random.nextDouble() - 0.5D) * this.getBbWidth(), 6, 0.4D, 0.6D, 0.4D, 0.02D); } - if (this.accelerationTicks <= 0) { - this.setRoyalAccelerated(false); - removeAccelModifier(this.getAttribute(Attributes.MOVEMENT_SPEED), ACCEL_SPEED_ID); - removeAccelModifier(this.getAttribute(Attributes.FLYING_SPEED), ACCEL_FLY_ID); - } } @Override @@ -427,6 +480,8 @@ public class QueenEntity extends RoyalBossEntity { if (!this.level().isClientSide && this.level() instanceof ServerLevel level) { DimensionalTearEntity.discardQueenOwnedTears(level, this.getUUID()); } + this.royalEffects.clear(); + this.frozenVelocities.clear(); removeAccelModifier(this.getAttribute(Attributes.MOVEMENT_SPEED), ACCEL_SPEED_ID); removeAccelModifier(this.getAttribute(Attributes.FLYING_SPEED), ACCEL_FLY_ID); super.die(damageSource); @@ -468,8 +523,6 @@ public class QueenEntity extends RoyalBossEntity { tag.putInt(SUMMON_COOLDOWN_KEY, this.manticoreSummonCooldownTicks); tag.putInt("GravityStompCooldownTicks", this.gravityStompCooldownTicks); tag.putInt("TimeFieldCooldownTicks", this.timeFieldCooldownTicks); - tag.putInt("MomentumLockCooldownTicks", this.momentumLockCooldownTicks); - tag.putInt("CrushingGravityCooldownTicks", this.crushingGravityCooldownTicks); tag.putInt("AccelerationCooldownTicks", this.accelerationCooldownTicks); tag.putInt("BlackHoleCooldownTicks", this.blackHoleCooldownTicks); } 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 3d69918d..d30b2ed4 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 @@ -1,6 +1,8 @@ package com.craisinlord.antarchy.content.entity.royal; import com.craisinlord.antarchy.content.AntarchyObjects; +import com.craisinlord.antarchy.content.time.TimeDilationApi; +import com.craisinlord.antarchy.config.AntarchySettings; import java.util.UUID; import net.minecraft.core.particles.ParticleTypes; import net.minecraft.nbt.CompoundTag; @@ -17,8 +19,16 @@ import net.minecraft.world.level.Level; import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.Vec3; import org.jetbrains.annotations.Nullable; +import software.bernie.geckolib.animatable.GeoEntity; +import software.bernie.geckolib.animatable.instance.AnimatableInstanceCache; +import software.bernie.geckolib.animation.AnimatableManager; +import software.bernie.geckolib.animation.AnimationController; +import software.bernie.geckolib.animation.AnimationState; +import software.bernie.geckolib.animation.PlayState; +import software.bernie.geckolib.animation.RawAnimation; +import software.bernie.geckolib.util.GeckoLibUtil; -public class RoyalBlackHoleEntity extends Entity { +public class RoyalBlackHoleEntity extends Entity implements GeoEntity { private static final EntityDataAccessor RADIUS = SynchedEntityData.defineId(RoyalBlackHoleEntity.class, EntityDataSerializers.FLOAT); private static final EntityDataAccessor COLLAPSING = @@ -36,6 +46,7 @@ public class RoyalBlackHoleEntity extends Entity { private UUID ownerId; private int age; private int activeTicks = DEFAULT_ACTIVE_TICKS; + private final AnimatableInstanceCache geoCache = GeckoLibUtil.createInstanceCache(this); public RoyalBlackHoleEntity(EntityType entityType, Level level) { super(entityType, level); @@ -46,14 +57,14 @@ public class RoyalBlackHoleEntity extends Entity { 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); - hole.activeTicks = Math.max(20, activeTicks); + hole.setRadius(radius > 0.0D ? radius : AntarchySettings.queenBlackHoleRadius()); + hole.activeTicks = activeTicks > 0 ? Math.max(20, activeTicks) : AntarchySettings.queenBlackHoleActiveTicks(); hole.ownerId = ownerId; return hole; } public static RoyalBlackHoleEntity create(ServerLevel level, Vec3 center, @Nullable UUID ownerId) { - return create(level, center, DEFAULT_RADIUS, DEFAULT_ACTIVE_TICKS, ownerId); + return create(level, center, AntarchySettings.queenBlackHoleRadius(), AntarchySettings.queenBlackHoleActiveTicks(), ownerId); } @Override @@ -84,6 +95,12 @@ public class RoyalBlackHoleEntity extends Entity { this.age++; if (!this.isCollapsing()) { 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); + 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.age >= this.activeTicks) { this.entityData.set(COLLAPSING, true); this.age = 0; @@ -108,7 +125,7 @@ public class RoyalBlackHoleEntity extends Entity { if (distance < 0.5D || distance > radius) { continue; } - double strength = 0.16D * (1.0D - distance / radius); + double strength = AntarchySettings.queenBlackHolePullStrength() * (1.0D - distance / radius); Vec3 pull = toCenter.scale(strength / distance); entity.setDeltaMovement(entity.getDeltaMovement().scale(0.86D).add(pull)); entity.hasImpulse = true; @@ -201,4 +218,18 @@ public class RoyalBlackHoleEntity extends Entity { tag.putUUID(OWNER_KEY, this.ownerId); } } + + @Override + public void registerControllers(AnimatableManager.ControllerRegistrar controllers) { + controllers.add(new AnimationController<>(this, "black_hole_controller", 0, this::blackHoleController)); + } + + private PlayState blackHoleController(AnimationState state) { + return state.setAndContinue(RawAnimation.begin().thenLoop("animation")); + } + + @Override + public AnimatableInstanceCache getAnimatableInstanceCache() { + return this.geoCache; + } } 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 78f9d030..dafad45f 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 @@ -8,6 +8,10 @@ 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.entity.royal.beam.RoyalBeamElement; +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.List; import net.minecraft.network.chat.Component; import net.minecraft.network.syncher.EntityDataAccessor; @@ -89,6 +93,11 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult private static final int CRUSH_MAX_BLOCKS = 24; private static final double CRUSH_MAX_RESISTANCE = 60.0D; private static final float CRUSH_DROP_CHANCE = 0.1F; + private static final int OBSTRUCTION_CLEAR_INTERVAL_TICKS = 5; + private static final double OBSTRUCTION_CLEAR_RADIUS = 3.5D; + 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; protected static final double FLYING_MIN_HOVER = 2.5D; protected static final double FLYING_PREFERRED_HOVER = 6.0D; @@ -116,6 +125,7 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult private final AnimatableInstanceCache geoCache = GeckoLibUtil.createInstanceCache(this); protected final RoyalBeamController beamController = new RoyalBeamController(this); + protected final RoyalAttackScheduler attackScheduler = new RoyalAttackScheduler(); private final ServerBossEvent bossEvent; private final RoyalHead[] heads = { new RoyalHead(RoyalHead.Slot.LEFT), @@ -127,6 +137,7 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult private RoyalHead activeBeamHead; private int beamLoopSoundTicks; private int beamHeadCursor; + private boolean beamStarted; @Nullable private Entity[] multipartParts; @@ -152,6 +163,11 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult protected abstract SoundEvent royalDeathSound(); + @Nullable + protected SoundEvent royalFlyLoopSound() { + return null; + } + protected abstract SoundEvent royalBiteSound(); protected abstract SoundEvent royalBeamShootSound(); @@ -275,7 +291,11 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult @Override protected float getSoundVolume() { - return 2.5F; + return (float) AntarchySettings.royalBossSoundVolume(); + } + + protected void playRoyalSound(SoundEvent sound, float pitch) { + this.playSound(sound, (float) AntarchySettings.royalBossSoundVolume(), pitch); } @Override @@ -292,8 +312,21 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult @Override public void tick() { super.tick(); + if (!this.level().isClientSide + && !this.isDeadOrDying() + && this.tickCount % OBSTRUCTION_CLEAR_INTERVAL_TICKS == 0 + && this.isInWall()) { + RoyalBlockDestruction.destroySphere( + (ServerLevel) this.level(), + this, + this.position().add(0.0D, this.getBbHeight() * 0.5D, 0.0D), + OBSTRUCTION_CLEAR_RADIUS, + OBSTRUCTION_CLEAR_MAX_BLOCKS, + OBSTRUCTION_CLEAR_MAX_RESISTANCE, + OBSTRUCTION_CLEAR_DROP_CHANCE); + } if (this.isFlyingBoss()) { - this.setNoGravity(true); + this.setNoGravity(!AntarchyGravityApi.isGravityInverted(this)); } if (this.level().isClientSide) { return; @@ -303,9 +336,10 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult } this.bossEvent.setProgress(this.getHealth() / this.getMaxHealth()); + this.attackScheduler.tick(); - for (RoyalHead head : this.heads) { - head.tick(); + if (this.isFlyingBoss() && this.tickCount % 40 == 0 && this.royalFlyLoopSound() != null) { + this.playRoyalSound(this.royalFlyLoopSound(), 0.92F + this.random.nextFloat() * 0.12F); } if (this.isDeadOrDying()) { @@ -326,16 +360,10 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult this.steerTowardTarget(primaryTarget); this.tickBodyCrush(); - for (RoyalHead head : this.heads) { - if (head.tickBiteHit()) { - this.applyBiteHit(head); - } - } - Phase phase = this.phase(); int activeHeadAttacks = 0; for (RoyalHead head : this.heads) { - if (head.busy()) { + if (this.attackScheduler.laneBusy(this.headLane(head))) { activeHeadAttacks++; } } @@ -344,7 +372,7 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult if (activeHeadAttacks >= phase.maxConcurrentHeadAttacks()) { break; } - if (!head.readyToAttack()) { + if (!head.readyToAttack() || this.attackScheduler.laneBusy(this.headLane(head))) { continue; } LivingEntity headTarget = head.target(this.level()); @@ -355,8 +383,14 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult } } + @Override + public boolean isInvulnerableTo(DamageSource source) { + return source.is(net.minecraft.world.damagesource.DamageTypes.IN_WALL) + || super.isInvulnerableTo(source); + } + private void tickRoyalBeam(LivingEntity primaryTarget) { - if (this.beamCooldownTicks > 0) { + if (this.beamCooldownTicks > 0 || this.attackScheduler.laneBusy(this.beamLane())) { this.beamCooldownTicks--; } @@ -364,10 +398,12 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult LivingEntity beamTarget = this.activeBeamHead.target(this.level()); if (!this.activeBeamHead.shooting() || beamTarget == null || !beamTarget.isAlive()) { this.stopRoyalBeam(); + } else if (!this.beamStarted) { + return; } else { if (this.beamLoopSoundTicks-- <= 0) { this.beamLoopSoundTicks = 20; - this.playSound(this.royalBeamLoopSound(), 2.5F, 1.0F); + this.playRoyalSound(this.royalBeamLoopSound(), 1.0F); } this.beamController.tick( this.headAnchor(this.activeBeamHead), @@ -393,24 +429,56 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult for (int offset = 0; offset < this.heads.length; offset++) { RoyalHead head = this.heads[(this.beamHeadCursor + offset) % this.heads.length]; LivingEntity target = head.target(this.level()); - if (!head.readyToAttack() || target == null || !this.canAttack(target) + if (!head.readyToAttack() || this.attackScheduler.laneBusy(this.headLane(head)) + || target == null || !this.canAttack(target) || this.headWithinBiteReach(head, target)) { continue; } - int duration = Math.max(1, this.royalBeamSettings().durationTicks()); - int cooldown = Math.max(1, Mth.floor(this.royalBeamSettings().cooldownTicks() * phase.cooldownScale())); - head.startShoot(duration, cooldown); + RoyalBeamSettings beamSettings = this.royalBeamSettings(); + int duration = Math.max(1, beamSettings.durationTicks()); + int cooldown = Math.max(1, Mth.floor(beamSettings.cooldownTicks() * phase.cooldownScale())); + int windup = Math.max(0, AntarchySettings.queenBeamWindupTicks()); + if (this instanceof KingEntity) { + windup = Math.max(0, AntarchySettings.kingBeamWindupTicks()); + } + LivingEntity committedTarget = target; + if (!this.attackScheduler.start("beam_" + head.slot().name(), this.beamLane(), cooldown, + windup, duration, 8, new RoyalAttackScheduler.Action() { + @Override + public void onStart() { + head.startShoot(); + } + + @Override + public void onActive(int elapsedTicks) { + if (elapsedTicks != 0 || RoyalBossEntity.this.beamStarted) { + return; + } + RoyalBossEntity.this.beamStarted = true; + RoyalBossEntity.this.beamController.start( + RoyalBossEntity.this.headAnchor(head), committedTarget.getEyePosition(), duration); + RoyalBossEntity.this.entityData.set(BEAM_ELEMENT, RoyalBossEntity.this.royalBeamElement(head).ordinal()); + RoyalBossEntity.this.setFiringRoyalBeam(true); + RoyalBossEntity.this.triggerAnim(head.slot().controllerName(), "shoot"); + RoyalBossEntity.this.beamLoopSoundTicks = 20; + RoyalBossEntity.this.playRoyalSound(RoyalBossEntity.this.royalBeamStartSound(head), 1.0F); + RoyalBossEntity.this.playRoyalSound(RoyalBossEntity.this.royalBeamShootSound(), 1.0F); + } + + @Override + public void onComplete() { + if (RoyalBossEntity.this.activeBeamHead == head) { + RoyalBossEntity.this.stopRoyalBeam(); + } + } + })) { + continue; + } head.setBeamActive(true); this.activeBeamHead = head; this.beamHeadCursor = (head.slot().partIndex()) % this.heads.length; this.beamCooldownTicks = cooldown; - this.beamController.start(this.headAnchor(head), target.getEyePosition(), duration); - this.entityData.set(BEAM_ELEMENT, this.royalBeamElement(head).ordinal()); - this.setFiringRoyalBeam(true); - this.triggerAnim(head.slot().controllerName(), "shoot"); - this.beamLoopSoundTicks = 20; - this.playSound(this.royalBeamStartSound(head), 3.0F, 1.0F); - this.playSound(this.royalBeamShootSound(), 3.0F, 1.0F); + this.beamStarted = false; return; } } @@ -418,11 +486,14 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult 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.playSound(this.royalBeamEndSound(), 2.5F, 1.0F); + this.playRoyalSound(this.royalBeamEndSound(), 1.0F); this.setFiringRoyalBeam(false); this.setRoyalBeamEndPosition(null); } @@ -484,8 +555,11 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult return; } + boolean inverted = AntarchyGravityApi.isGravityInverted(this); double groundBelowTarget = this.groundYBelow(target.getX(), target.getZ()); - double wantedY = Mth.clamp(target.getY() + 3.0D, + double wantedY = 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; @@ -502,6 +576,9 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult } private void tickFlyingAltitude() { + if (AntarchyGravityApi.isGravityInverted(this)) { + return; + } if (this.onGround()) { this.setDeltaMovement(this.getDeltaMovement().add(0.0D, 0.2D, 0.0D)); } @@ -535,9 +612,44 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult private void startBite(RoyalHead head, Phase phase) { int cooldown = Math.max(10, Mth.floor(AntarchySettings.royalBossBiteCooldownTicks() * phase.cooldownScale())); - head.startBite(BITE_DURATION_TICKS, BITE_HIT_TICK, cooldown); - this.triggerAnim(head.slot().controllerName(), "bite"); - this.playSound(this.royalBiteSound(), 3.0F, 0.9F + this.random.nextFloat() * 0.2F); + if (!this.attackScheduler.start("bite_" + head.slot().name(), headLane(head), cooldown, + BITE_HIT_TICK, 1, BITE_DURATION_TICKS - BITE_HIT_TICK, + new RoyalAttackScheduler.Action() { + @Override + public void onStart() { + RoyalBossEntity.this.triggerAnim(head.slot().controllerName(), "bite"); + RoyalBossEntity.this.playRoyalSound(RoyalBossEntity.this.royalBiteSound(), + 0.9F + RoyalBossEntity.this.random.nextFloat() * 0.2F); + } + + @Override + public void onActive(int elapsedTicks) { + RoyalBossEntity.this.applyBiteHit(head); + } + })) { + return; + } + } + + protected RoyalAttackLane headLane(RoyalHead head) { + return switch (head.slot()) { + case LEFT -> RoyalAttackLane.LEFT_HEAD; + case CENTER -> RoyalAttackLane.CENTER_HEAD; + case RIGHT -> RoyalAttackLane.RIGHT_HEAD; + }; + } + + protected RoyalAttackLane beamLane() { + return RoyalAttackLane.BODY; + } + + protected boolean beginRoyalAttack(String id, RoyalAttackLane lane, int cooldownTicks, int activeTicks) { + return this.attackScheduler.start(id, lane, cooldownTicks, activeTicks); + } + + 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); } private void applyBiteHit(RoyalHead head) { @@ -578,7 +690,7 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult return AntarchySettings.royalBossBiteReach(); } - private Vec3 headAnchor(RoyalHead head) { + protected Vec3 headAnchor(RoyalHead head) { int partIndex = head.slot().partIndex(); Entity[] parts = this.multipartParts; if (parts != null && partIndex < parts.length && parts[partIndex] != null) { @@ -596,6 +708,10 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult this.getZ() + forward.z * spec.forwardOffset() + right.z * spec.lateralOffset()); } + protected RoyalHead royalHead(RoyalHead.Slot slot) { + return this.heads[slot.ordinal()]; + } + @Override public void die(DamageSource damageSource) { if (!this.level().isClientSide) { @@ -667,7 +783,7 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult @Override public boolean canBeAffected(net.minecraft.world.effect.MobEffectInstance effectInstance) { if (effectInstance.is(com.craisinlord.antarchy.content.AntarchyObjects.INVERTED_EFFECT.get())) { - return false; + return ThoraxisUndersideManager.isThoraxis(this.level()); } return super.canBeAffected(effectInstance); } 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 new file mode 100644 index 00000000..7db866f5 --- /dev/null +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/RoyalElementalProjectileEntity.java @@ -0,0 +1,199 @@ +package com.craisinlord.antarchy.content.entity.royal; + +import com.craisinlord.antarchy.content.AntarchyObjects; +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.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.world.damagesource.DamageSource; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.projectile.ThrowableProjectile; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.phys.AABB; +import net.minecraft.world.phys.BlockHitResult; +import net.minecraft.world.phys.EntityHitResult; +import net.minecraft.world.phys.HitResult; +import net.minecraft.world.phys.Vec3; +import software.bernie.geckolib.animatable.GeoEntity; +import software.bernie.geckolib.animatable.instance.AnimatableInstanceCache; +import software.bernie.geckolib.animation.AnimatableManager; +import software.bernie.geckolib.animation.AnimationController; +import software.bernie.geckolib.animation.AnimationState; +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 MAX_LIFETIME = 100; + private final AnimatableInstanceCache geoCache = GeckoLibUtil.createInstanceCache(this); + + public RoyalElementalProjectileEntity(EntityType type, Level level) { + super(type, level); + this.noCulling = true; + } + + public static RoyalElementalProjectileEntity create(Level level, LivingEntity owner, RoyalBeamElement element, + Vec3 position, Vec3 direction) { + RoyalElementalProjectileEntity projectile = new RoyalElementalProjectileEntity( + 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.shoot(direction.x, direction.y, direction.z, element == RoyalBeamElement.ICE ? 0.8F : 1.05F, 0.0F); + return projectile; + } + + @Override + protected void defineSynchedData(SynchedEntityData.Builder builder) { + builder.define(ATTACK_TYPE, FIREBALL); + } + + public boolean isIceball() { + return this.entityData.get(ATTACK_TYPE) == ICEBALL; + } + + private void setAttackType(int type) { + this.entityData.set(ATTACK_TYPE, type); + } + + @Override + protected double getDefaultGravity() { + return this.isIceball() ? 0.012D : 0.018D; + } + + protected float getInertia() { + return this.isIceball() ? 0.985F : 0.96F; + } + + @Override + public void tick() { + if (!this.level().isClientSide && this.tickCount > MAX_LIFETIME) { + this.discard(); + return; + } + super.tick(); + if (this.level().isClientSide) { + this.level().addParticle(this.isIceball() ? ParticleTypes.SNOWFLAKE : ParticleTypes.FLAME, + this.getX(), this.getY(), this.getZ(), 0.0D, 0.0D, 0.0D); + } + } + + @Override + protected void onHitEntity(EntityHitResult result) { + if (!this.level().isClientSide && this.level() instanceof ServerLevel level) { + this.impact(level, result.getLocation()); + } + this.discard(); + } + + @Override + protected void onHitBlock(BlockHitResult result) { + if (!this.level().isClientSide && this.level() instanceof ServerLevel level) { + this.impact(level, result.getLocation()); + } + this.discard(); + } + + @Override + protected void onHit(HitResult result) { + super.onHit(result); + if (!this.level().isClientSide && result.getType() == HitResult.Type.MISS) { + this.discard(); + } + } + + private void impact(ServerLevel level, Vec3 center) { + if (this.isIceball()) { + impactIce(level, center); + } else { + impactFire(level, center); + } + } + + 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())) { + 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); + level.sendParticles(ParticleTypes.EXPLOSION_EMITTER, center.x, center.y, center.z, 4, 1.5D, 1.5D, 1.5D, 0.0D); + } + + 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())) { + 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)); + } + coverInIce(level, center, AntarchySettings.kingIceballRadius(), AntarchySettings.kingElementalTerrainCap()); + level.sendParticles(ParticleTypes.SNOWFLAKE, center.x, center.y, center.z, 80, 4.0D, 2.0D, 4.0D, 0.08D); + } + + private void coverInIce(ServerLevel level, Vec3 center, double radius, int cap) { + int changed = 0; + 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); + for (BlockPos pos : BlockPos.betweenClosed(minX, minY, minZ, maxX, maxY, maxZ)) { + if (changed >= cap || pos.distToCenterSqr(center.x, center.y, center.z) > radius * radius) continue; + BlockState existing = level.getBlockState(pos); + if (existing.isAir() || !existing.getFluidState().isEmpty() || existing.hasBlockEntity()) continue; + if (existing.getDestroySpeed(level, pos) < 0.0F || existing.getBlock().getExplosionResistance() > 80.0F) continue; + BlockState frozen = pos.getY() >= center.y + 1.0D ? Blocks.SNOW_BLOCK.defaultBlockState() : Blocks.PACKED_ICE.defaultBlockState(); + if (existing != frozen && level.setBlockAndUpdate(pos, frozen)) changed++; + } + } + + protected net.minecraft.core.particles.ParticleOptions getTrailParticle() { + return this.isIceball() ? ParticleTypes.SNOWFLAKE : ParticleTypes.FLAME; + } + + @Override + public void addAdditionalSaveData(CompoundTag tag) { + super.addAdditionalSaveData(tag); + tag.putInt("AttackType", this.entityData.get(ATTACK_TYPE)); + } + + @Override + public void readAdditionalSaveData(CompoundTag tag) { + super.readAdditionalSaveData(tag); + this.setAttackType(tag.getInt("AttackType")); + } + + @Override + public void registerControllers(AnimatableManager.ControllerRegistrar controllers) { + controllers.add(new AnimationController<>(this, "elemental_projectile", 0, this::animation)); + } + + private PlayState animation(AnimationState state) { + return state.setAndContinue(RawAnimation.begin().thenLoop(this.isIceball() ? "animation.ice_ball" : "animation.fire_ball")); + } + + @Override + public AnimatableInstanceCache getAnimatableInstanceCache() { + return this.geoCache; + } +} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/RoyalHead.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/RoyalHead.java index 9c403741..43e8de4b 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/RoyalHead.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/RoyalHead.java @@ -41,10 +41,7 @@ public final class RoyalHead { private final Slot slot; private int targetId = -1; - private int attackCooldown; - private int biteTicks; - private int biteHitTick = -1; - private int shootTicks; + private boolean shooting; private boolean beamActive; public RoyalHead(Slot slot) { @@ -68,22 +65,19 @@ public final class RoyalHead { } public boolean busy() { - return this.biteTicks > 0 || this.shootTicks > 0 || this.beamActive; + return this.shooting || this.beamActive; } public boolean readyToAttack() { - return !this.busy() && this.attackCooldown <= 0; + return !this.busy(); } - public void startBite(int durationTicks, int hitTick, int cooldownTicks) { - this.biteTicks = durationTicks; - this.biteHitTick = hitTick; - this.attackCooldown = cooldownTicks; + public void startShoot() { + this.shooting = true; } - public void startShoot(int durationTicks, int cooldownTicks) { - this.shootTicks = durationTicks; - this.attackCooldown = cooldownTicks; + public void stopShoot() { + this.shooting = false; } public void setBeamActive(boolean beamActive) { @@ -95,36 +89,12 @@ public final class RoyalHead { } public boolean shooting() { - return this.shootTicks > 0; - } - - public boolean tickBiteHit() { - if (this.biteTicks <= 0) { - return false; - } - this.biteTicks--; - boolean hit = this.biteTicks == this.biteHitTick; - if (hit) { - this.biteHitTick = -1; - } - return hit; - } - - public void tick() { - if (this.attackCooldown > 0) { - this.attackCooldown--; - } - if (this.shootTicks > 0) { - this.shootTicks--; - } + return this.shooting; } public void reset() { this.targetId = -1; - this.attackCooldown = 0; - this.biteTicks = 0; - this.biteHitTick = -1; - this.shootTicks = 0; + this.shooting = false; this.beamActive = false; } } 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 new file mode 100644 index 00000000..217b9606 --- /dev/null +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/RoyalIceSpikeEntity.java @@ -0,0 +1,98 @@ +package com.craisinlord.antarchy.content.entity.royal; + +import com.craisinlord.antarchy.content.AntarchyObjects; +import com.craisinlord.antarchy.config.AntarchySettings; +import net.minecraft.core.particles.ParticleTypes; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.damagesource.DamageSource; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.level.Level; +import net.minecraft.world.phys.AABB; +import net.minecraft.world.phys.Vec3; +import software.bernie.geckolib.animatable.GeoEntity; +import software.bernie.geckolib.animatable.instance.AnimatableInstanceCache; +import software.bernie.geckolib.animation.AnimatableManager; +import software.bernie.geckolib.animation.AnimationController; +import software.bernie.geckolib.animation.AnimationState; +import software.bernie.geckolib.animation.PlayState; +import software.bernie.geckolib.animation.RawAnimation; +import software.bernie.geckolib.util.GeckoLibUtil; + +public class RoyalIceSpikeEntity extends Entity implements GeoEntity { + private static final RawAnimation EMERGE_ANIMATION = RawAnimation.begin().thenPlay("ice_spike"); + private static final RawAnimation IMPACT_ANIMATION = RawAnimation.begin().thenPlay("ice_spike_impact"); + private final AnimatableInstanceCache geoCache = GeckoLibUtil.createInstanceCache(this); + private boolean impacted; + + public RoyalIceSpikeEntity(EntityType type, Level level) { + super(type, level); + this.noPhysics = true; + this.noCulling = true; + } + + public static RoyalIceSpikeEntity create(ServerLevel level, Vec3 position) { + RoyalIceSpikeEntity spike = new RoyalIceSpikeEntity(AntarchyObjects.ROYAL_ICE_SPIKE.get(), level); + spike.setPos(position.x, position.y, position.z); + return spike; + } + + @Override + protected void defineSynchedData(net.minecraft.network.syncher.SynchedEntityData.Builder builder) { + } + + @Override + public void tick() { + super.tick(); + if (!this.level().isClientSide && this.tickCount == 1) { + this.triggerAnim("ice_spike", "emerge"); + } + if (!this.level().isClientSide && this.tickCount == 6 && !this.impacted) { + this.impacted = true; + this.impact((ServerLevel) this.level()); + this.triggerAnim("ice_spike", "impact"); + } + if (!this.level().isClientSide && this.tickCount > 45) { + this.discard(); + } + if (this.level().isClientSide && this.tickCount < 10) { + this.level().addParticle(ParticleTypes.SNOWFLAKE, this.getX(), this.getY() + this.tickCount * 0.4D, this.getZ(), 0.0D, 0.03D, 0.0D); + } + } + + private void impact(ServerLevel level) { + DamageSource source = this.damageSources().magic(); + for (LivingEntity living : level.getEntitiesOfClass(LivingEntity.class, + new AABB(this.blockPosition()).inflate(2.2D), entity -> entity.isAlive())) { + living.hurt(source, (float) AntarchySettings.kingIceSpikeDamage()); + living.setTicksFrozen(Math.min(living.getTicksRequiredToFreeze() + 60, living.getTicksFrozen() + 100)); + living.setDeltaMovement(living.getDeltaMovement().add(0.0D, 0.35D, 0.0D)); + living.hasImpulse = true; + } + level.sendParticles(ParticleTypes.SNOWFLAKE, this.getX(), this.getY() + 1.5D, this.getZ(), 25, 1.5D, 1.0D, 1.5D, 0.04D); + } + + @Override + protected void readAdditionalSaveData(CompoundTag tag) { + this.impacted = tag.getBoolean("Impacted"); + } + + @Override + protected void addAdditionalSaveData(CompoundTag tag) { + tag.putBoolean("Impacted", this.impacted); + } + + @Override + public void registerControllers(AnimatableManager.ControllerRegistrar controllers) { + controllers.add(new AnimationController<>(this, "ice_spike", 0, state -> PlayState.STOP) + .triggerableAnim("emerge", EMERGE_ANIMATION) + .triggerableAnim("impact", IMPACT_ANIMATION)); + } + + @Override + public AnimatableInstanceCache getAnimatableInstanceCache() { + return this.geoCache; + } +} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/attack/RoyalAttackLane.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/attack/RoyalAttackLane.java new file mode 100644 index 00000000..ef6a6143 --- /dev/null +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/attack/RoyalAttackLane.java @@ -0,0 +1,12 @@ +package com.craisinlord.antarchy.content.entity.royal.attack; + +public enum RoyalAttackLane { + BODY, + LEFT_HEAD, + CENTER_HEAD, + RIGHT_HEAD, + HAZARD, + SUMMON, + DECREE, + MOVEMENT +} 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 new file mode 100644 index 00000000..dfa2d0ea --- /dev/null +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/attack/RoyalAttackScheduler.java @@ -0,0 +1,123 @@ +package com.craisinlord.antarchy.content.entity.royal.attack; + +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.EnumMap; +import java.util.HashMap; +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() {} + default void onActive(int elapsedTicks) {} + default void onComplete() {} + } + + private static final class RunningAction { + private final Action action; + private final int windupTicks; + private final int activeTicks; + private final int recoveryTicks; + private int elapsedTicks; + + private RunningAction(Action action, int windupTicks, int activeTicks, int recoveryTicks) { + this.action = action; + this.windupTicks = Math.max(0, windupTicks); + this.activeTicks = Math.max(1, activeTicks); + this.recoveryTicks = Math.max(0, recoveryTicks); + } + + private int totalTicks() { + return this.windupTicks + this.activeTicks + this.recoveryTicks; + } + } + private final Map cooldowns = new HashMap<>(); + private final Map active = new EnumMap<>(RoyalAttackLane.class); + private final Deque recent = new ArrayDeque<>(); + + public void tick() { + cooldowns.replaceAll((key, value) -> Math.max(0, value - 1)); + for (var iterator = active.entrySet().iterator(); iterator.hasNext();) { + RunningAction running = iterator.next().getValue(); + running.elapsedTicks++; + int activeStart = running.windupTicks; + int activeEnd = activeStart + running.activeTicks; + if (running.elapsedTicks >= activeStart && running.elapsedTicks < activeEnd) { + running.action.onActive(running.elapsedTicks - activeStart); + } + if (running.elapsedTicks >= running.totalTicks()) { + iterator.remove(); + running.action.onComplete(); + } + } + } + + public boolean ready(String id, RoyalAttackLane lane) { + return cooldowns.getOrDefault(id, 0) <= 0 && !active.containsKey(lane); + } + + public boolean laneBusy(RoyalAttackLane lane) { + return active.containsKey(lane); + } + + public boolean start(String id, RoyalAttackLane lane, int cooldownTicks, int activeTicks) { + return start(id, lane, cooldownTicks, 0, activeTicks, 0, new Action() {}); + } + + public boolean start(String id, RoyalAttackLane lane, int cooldownTicks, int windupTicks, + int activeTicks, int recoveryTicks, Action action) { + if (!ready(id, lane)) { + return false; + } + cooldowns.put(id, Math.max(1, cooldownTicks)); + active.put(lane, new RunningAction(action, windupTicks, activeTicks, recoveryTicks)); + action.onStart(); + recent.remove(id); + recent.addFirst(id); + while (recent.size() > 8) { + recent.removeLast(); + } + return true; + } + + public int repetitionPenalty(String id) { + int penalty = 0; + for (String previous : recent) { + if (previous.equals(id)) { + penalty += 3; + } + } + return penalty; + } + + public String chooseWeighted(String[] ids, ToIntFunction weightFunction, RandomSource random) { + int total = 0; + int[] weights = new int[ids.length]; + for (int i = 0; i < ids.length; i++) { + weights[i] = Math.max(0, weightFunction.applyAsInt(ids[i]) - repetitionPenalty(ids[i])); + total += weights[i]; + } + if (total <= 0) { + return null; + } + int roll = random.nextInt(total); + for (int i = 0; i < ids.length; i++) { + roll -= weights[i]; + if (roll < 0) { + return ids[i]; + } + } + return null; + } + + public int cooldown(String id) { + return cooldowns.getOrDefault(id, 0); + } + + public void cancel(RoyalAttackLane lane) { + active.remove(lane); + } +} 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 new file mode 100644 index 00000000..f0da3ab0 --- /dev/null +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/attack/RoyalEffectController.java @@ -0,0 +1,54 @@ +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() { + return new Effect(this.tick, this.end, this.duration, this.age + 1); + } + } + + private final Map active = new HashMap<>(); + + public void tick() { + for (var iterator = this.active.entrySet().iterator(); iterator.hasNext();) { + var entry = iterator.next(); + Effect effect = entry.getValue(); + effect.tick().run(); + Effect next = effect.advance(); + if (next.age() >= next.duration()) { + next.end().run(); + iterator.remove(); + } else { + entry.setValue(next); + } + } + } + + public void start(String id, int duration, Runnable onStart, Runnable tick, Runnable end) { + this.stop(id); + onStart.run(); + this.active.put(id, new Effect(tick, end, Math.max(1, duration), 0)); + } + + public boolean active(String id) { + return this.active.containsKey(id); + } + + public void stop(String id) { + Effect effect = this.active.remove(id); + if (effect != null) { + effect.end().run(); + } + } + + public void clear() { + for (Effect effect : this.active.values()) { + effect.end().run(); + } + this.active.clear(); + } +} 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 4ae93ee8..75fdfcfe 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 @@ -26,10 +26,13 @@ import net.minecraft.world.phys.Vec3; import org.jetbrains.annotations.Nullable; public final class RoyalBeamController { + private static final int INITIAL_TRACKING_DELAY_TICKS = 8; private final Mob owner; private Vec3 serverTarget; private Vec3 beamEndPosition; private int beamTicks; + private int beamAgeTicks; + private int trackingDelayTicks; private int terrainMutationsThisTick; private static final int ICE_MUTATIONS_PER_TICK = 3; private static final float ICE_IMPACT_RADIUS = 2.0F; @@ -55,12 +58,15 @@ public final class RoyalBeamController { this.serverTarget = target; this.beamEndPosition = target; this.beamTicks = Math.max(1, durationTicks); + this.beamAgeTicks = 0; + this.trackingDelayTicks = INITIAL_TRACKING_DELAY_TICKS; } public void stop() { this.serverTarget = null; this.beamEndPosition = null; this.beamTicks = 0; + this.beamAgeTicks = 0; } public void tick( @@ -76,8 +82,11 @@ public final class RoyalBeamController { } this.beamTicks--; + this.beamAgeTicks++; this.terrainMutationsThisTick = 0; - if (target != null && target.isAlive()) { + if (this.trackingDelayTicks > 0) { + this.trackingDelayTicks--; + } else if (target != null && target.isAlive()) { Vec3 targetEye = target.getEyePosition(); this.serverTarget = this.serverTarget == null ? targetEye @@ -93,7 +102,10 @@ public final class RoyalBeamController { if (direction.lengthSqr() < 1.0E-7D) { direction = this.owner.getLookAngle(); } - Vec3 clipEnd = shootFrom.add(direction.scale(settings.range())); + double committedDistance = Math.min(settings.range(), shootFrom.distanceTo(this.serverTarget)); + double travelProgress = Mth.clamp(this.beamAgeTicks / (double) settings.travelTicks(), 0.0D, 1.0D); + double currentRange = Math.max(settings.pathStep(), committedDistance * travelProgress); + Vec3 clipEnd = shootFrom.add(direction.scale(currentRange)); HitResult hit = this.owner.level().clip(new ClipContext( shootFrom, clipEnd, diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/beam/RoyalBeamSettings.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/beam/RoyalBeamSettings.java index 082338ee..240bba9b 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/beam/RoyalBeamSettings.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/beam/RoyalBeamSettings.java @@ -5,6 +5,7 @@ public record RoyalBeamSettings( double targetTracking, double pathStep, int durationTicks, + int travelTicks, int cooldownTicks, float pathDamageRadius, float impactDamageRadius, @@ -27,6 +28,7 @@ public record RoyalBeamSettings( 0.1D, 7.5D, 30, + 12, 90, 6.0F, 6.0F, @@ -50,6 +52,7 @@ public record RoyalBeamSettings( targetTracking = Math.clamp(targetTracking, 0.0D, 1.0D); pathStep = Math.max(1.0D, pathStep); durationTicks = Math.max(1, durationTicks); + travelTicks = Math.max(1, travelTicks); cooldownTicks = Math.max(1, cooldownTicks); pathDamageRadius = Math.max(0.0F, pathDamageRadius); impactDamageRadius = Math.max(0.0F, impactDamageRadius); 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 c5d0e13b..f28bf41a 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 @@ -5,11 +5,9 @@ import net.minecraft.world.entity.LivingEntity; public final class AdvanceDecree implements RoyalDecree { private static final double REQUIRED_RANGE = 14.0D; public String translationKey() { return "decree.antarchy.advance"; } - public void apply(ServerLevel level, KingEntity king, LivingEntity target) { - king.setDecreeRetreatPressure(true); - if (target.distanceTo(king) > REQUIRED_RANGE) { - king.invokeJudgment(target); - } + 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; } @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 25120910..f98e4ef9 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 @@ -5,6 +5,9 @@ import net.minecraft.world.entity.LivingEntity; 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; + } @Override public int contextWeight(KingEntity king, LivingEntity target) { return 4 + king.behaviorScore(KingEntity.Behavior.HUGGING); diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/decree/DoNotRunDecree.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/decree/DoNotRunDecree.java index 17e3f9b9..7940b4a4 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/decree/DoNotRunDecree.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/decree/DoNotRunDecree.java @@ -4,7 +4,10 @@ import net.minecraft.server.level.ServerLevel; import net.minecraft.world.entity.LivingEntity; public final class DoNotRunDecree implements RoyalDecree { public String translationKey() { return "decree.antarchy.do_not_run"; } - public void apply(ServerLevel level, KingEntity king, LivingEntity target) { if (target.isSprinting()) king.invokeJudgment(target); } + public void apply(ServerLevel level, KingEntity king, LivingEntity target) { } + @Override public Evaluation evaluate(ServerLevel level, KingEntity king, LivingEntity target) { + return target.isSprinting() ? Evaluation.VIOLATED : Evaluation.COMPLIANT; + } @Override public int contextWeight(KingEntity king, LivingEntity target) { return 4 + king.behaviorScore(KingEntity.Behavior.SPRINTING); 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 24cebe64..d541f8b7 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 @@ -13,4 +13,8 @@ public final class FightMeCowardDecree implements RoyalDecree { target.hasImpulse = true; } } + @Override + public Evaluation evaluate(ServerLevel level, KingEntity king, LivingEntity target) { + return target.distanceTo(king) > 28.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 f009eabb..cc876daa 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 @@ -5,10 +5,9 @@ import net.minecraft.world.entity.LivingEntity; public final class KeepYourDistanceDecree implements RoyalDecree { private static final double INNER_RADIUS = 16.0D; public String translationKey() { return "decree.antarchy.keep_your_distance"; } - public void apply(ServerLevel level, KingEntity king, LivingEntity target) { - if (target.distanceTo(king) <= INNER_RADIUS) { - king.invokeJudgment(target); - } + 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; } @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/KneelDecree.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/decree/KneelDecree.java index fe1514ba..2f817a00 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/decree/KneelDecree.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/decree/KneelDecree.java @@ -12,7 +12,7 @@ public final class KneelDecree implements RoyalDecree { UUID id = target.getUUID(); if (target.isCrouching()) { deadlines.remove(id); king.clearActiveDecree(target); return; } int deadline = deadlines.computeIfAbsent(id, ignored -> target.tickCount + 100); - if (target.tickCount >= deadline) { king.invokeJudgment(target); deadlines.put(id, target.tickCount + 100); } + if (target.tickCount >= deadline) { king.failActiveDecree(target); } } @Override diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/decree/RoyalDecree.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/decree/RoyalDecree.java index d0c29d5e..b75a43c1 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/decree/RoyalDecree.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/decree/RoyalDecree.java @@ -5,9 +5,19 @@ import net.minecraft.server.level.ServerLevel; import net.minecraft.world.entity.LivingEntity; public interface RoyalDecree { + enum Evaluation { + COMPLIANT, + VIOLATED, + COMPLETE + } + String translationKey(); void apply(ServerLevel level, KingEntity king, LivingEntity target); + default Evaluation evaluate(ServerLevel level, KingEntity king, LivingEntity target) { + return Evaluation.COMPLIANT; + } + default int countdownTicks(LivingEntity target) { return -1; } diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/decree/ShowNoMercyDecree.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/decree/ShowNoMercyDecree.java index 44ff3e2e..3f57deb0 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/decree/ShowNoMercyDecree.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/decree/ShowNoMercyDecree.java @@ -6,8 +6,9 @@ public final class ShowNoMercyDecree implements RoyalDecree { private static final long IDLE_DAMAGE_LIMIT_TICKS = 80L; public String translationKey() { return "decree.antarchy.show_no_mercy"; } public void apply(ServerLevel level, KingEntity king, LivingEntity target) { - if (king.ticksSincePlayerDamage(target) > IDLE_DAMAGE_LIMIT_TICKS) { - king.invokeJudgment(target); - } + } + @Override public Evaluation evaluate(ServerLevel level, KingEntity king, LivingEntity target) { + return king.ticksSincePlayerDamage(target) > IDLE_DAMAGE_LIMIT_TICKS + ? Evaluation.VIOLATED : Evaluation.COMPLIANT; } } diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/decree/SkyIsMineDecree.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/decree/SkyIsMineDecree.java index 3525844f..0fda8b16 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/decree/SkyIsMineDecree.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/decree/SkyIsMineDecree.java @@ -4,7 +4,10 @@ import net.minecraft.server.level.ServerLevel; import net.minecraft.world.entity.LivingEntity; public final class SkyIsMineDecree implements RoyalDecree { public String translationKey() { return "decree.antarchy.the_sky_is_mine"; } - public void apply(ServerLevel level, KingEntity king, LivingEntity target) { if (!target.onGround() && !target.isPassenger()) king.invokeJudgment(target); } + public void apply(ServerLevel level, KingEntity king, LivingEntity target) { } + @Override public Evaluation evaluate(ServerLevel level, KingEntity king, LivingEntity target) { + return !target.onGround() && !target.isPassenger() ? Evaluation.VIOLATED : Evaluation.COMPLIANT; + } @Override public int contextWeight(KingEntity king, LivingEntity target) { return 4 + king.behaviorScore(KingEntity.Behavior.AIRBORNE); diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/decree/StandTallDecree.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/decree/StandTallDecree.java index 7a04453d..823394ff 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/decree/StandTallDecree.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/decree/StandTallDecree.java @@ -5,8 +5,8 @@ import net.minecraft.world.entity.LivingEntity; public final class StandTallDecree implements RoyalDecree { public String translationKey() { return "decree.antarchy.stand_tall"; } public void apply(ServerLevel level, KingEntity king, LivingEntity target) { - if (target.isCrouching()) { - king.invokeJudgment(target); - } + } + @Override public Evaluation evaluate(ServerLevel level, KingEntity king, LivingEntity target) { + return target.isCrouching() ? Evaluation.VIOLATED : Evaluation.COMPLIANT; } } diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/decree/StandYourGroundDecree.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/decree/StandYourGroundDecree.java index 5e46e984..622ff408 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/decree/StandYourGroundDecree.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/decree/StandYourGroundDecree.java @@ -12,9 +12,11 @@ public final class StandYourGroundDecree implements RoyalDecree { public String translationKey() { return "decree.antarchy.stand_your_ground"; } public void apply(ServerLevel level, KingEntity king, LivingEntity target) { Vec3 anchor = this.anchors.computeIfAbsent(target.getUUID(), ignored -> target.position()); - if (target.position().distanceToSqr(anchor) > ALLOWED_DRIFT * ALLOWED_DRIFT) { - king.invokeJudgment(target); - } + } + @Override public Evaluation evaluate(ServerLevel level, KingEntity king, LivingEntity target) { + Vec3 anchor = this.anchors.computeIfAbsent(target.getUUID(), ignored -> target.position()); + return target.position().distanceToSqr(anchor) > ALLOWED_DRIFT * ALLOWED_DRIFT + ? Evaluation.VIOLATED : Evaluation.COMPLIANT; } @Override public void onEnded() { diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/vortex/WindVortexEntity.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/vortex/WindVortexEntity.java index d685d81f..4a9d34ab 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/vortex/WindVortexEntity.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/vortex/WindVortexEntity.java @@ -8,6 +8,7 @@ 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.network.protocol.game.ClientboundSetEntityMotionPacket; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.sounds.SoundEvents; @@ -66,6 +67,8 @@ public class WindVortexEntity extends Entity { private static final String TRAVEL_Z_KEY = "TravelZ"; private static final String TRAVELLING_KEY = "Travelling"; private static final String HOMING_KEY = "Homing"; + private static final String ANCHORED_KEY = "Anchored"; + private static final String BOUNCE_ON_IMPACT_KEY = "BounceOnImpact"; private static final double BASE_RADIUS = 0.35D; private static final double DRIFT_FRICTION = 0.995D; @@ -82,15 +85,20 @@ public class WindVortexEntity extends Entity { } private int age; - private int durationTicks = 140; + private int durationTicks = 240; private double pullStrength = 0.32D; private double launchStrength = 1.0D; private float damageOverride = -1.0F; private Vec3 travelVelocity = Vec3.ZERO; private boolean travelling = false; private boolean homing = false; + private boolean bounceOnImpact = false; private int travelTicksRemaining; private int fadeOutTicksRemaining; + private boolean anchoredAtBlock; + private double flowTurnRate; + private double flowTurnTarget; + private int flowTurnChangeTicks; private final Map carriedProgress = new HashMap<>(); @Nullable private UUID ownerUuid; @@ -148,6 +156,18 @@ public class WindVortexEntity extends Entity { return; } + if (this.anchoredAtBlock) { + this.setDeltaMovement(Vec3.ZERO); + if ((this.tickCount & 1) == 0) { + this.applyVortexForces(); + } + this.age++; + if (this.age >= this.durationTicks) { + this.fadeOut(); + } + return; + } + boolean lensVortex = this.getMode() != VortexMode.UPWARD; if (!lensVortex && (this.isInLava() || this.isInsideSolidBlock())) { this.fadeOut(); @@ -164,20 +184,27 @@ public class WindVortexEntity extends Entity { if (this.homing) { this.steerTowardPrey(); } + if (this.bounceOnImpact) { + this.updateFluidDrift(); + } Vec3 from = this.position(); Vec3 to = from.add(this.travelVelocity); HitResult blockHit = this.level().clip( new ClipContext(from, to, ClipContext.Block.COLLIDER, ClipContext.Fluid.NONE, this)); if (blockHit.getType() != HitResult.Type.MISS) { - this.fadeOut(); - return; - } - this.setDeltaMovement(this.travelVelocity); - this.move(net.minecraft.world.entity.MoverType.SELF, this.travelVelocity); - if (--this.travelTicksRemaining <= 0) { - this.travelling = false; - this.noPhysics = false; + if (this.bounceOnImpact) { + this.flowAroundBlock(blockHit); + } else { + this.anchorAtBlock(blockHit); + } + } else { this.setDeltaMovement(this.travelVelocity); + this.move(net.minecraft.world.entity.MoverType.SELF, this.travelVelocity); + if (--this.travelTicksRemaining <= 0) { + this.travelling = false; + this.noPhysics = false; + this.setDeltaMovement(this.travelVelocity); + } } } else if (!lensVortex) { Vec3 current = this.getDeltaMovement(); @@ -192,11 +219,15 @@ public class WindVortexEntity extends Entity { HitResult blockHit = this.level().clip( new ClipContext(from, to, ClipContext.Block.COLLIDER, ClipContext.Fluid.NONE, this)); if (blockHit.getType() != HitResult.Type.MISS) { - this.fadeOut(); - return; + if (this.bounceOnImpact) { + this.flowAroundBlock(blockHit); + } else { + this.anchorAtBlock(blockHit); + } + } else { + this.setDeltaMovement(drift); + this.move(net.minecraft.world.entity.MoverType.SELF, drift); } - this.setDeltaMovement(drift); - this.move(net.minecraft.world.entity.MoverType.SELF, drift); } else { Vec3 drift = this.getDeltaMovement().multiply(DRIFT_FRICTION, DRIFT_FRICTION, DRIFT_FRICTION); this.setDeltaMovement(drift); @@ -256,6 +287,73 @@ public class WindVortexEntity extends Entity { } } + private void anchorAtBlock(HitResult hit) { + this.travelling = false; + this.noPhysics = false; + this.travelVelocity = Vec3.ZERO; + this.setDeltaMovement(Vec3.ZERO); + if (hit instanceof net.minecraft.world.phys.BlockHitResult blockHit) { + Vec3 normal = Vec3.atLowerCornerOf(blockHit.getDirection().getNormal()); + this.setPos(blockHit.getLocation().add(normal.scale(0.05D))); + } + this.anchoredAtBlock = true; + } + + private void updateFluidDrift() { + if (--this.flowTurnChangeTicks <= 0) { + this.flowTurnChangeTicks = 24 + this.random.nextInt(32); + this.flowTurnTarget = (this.random.nextDouble() * 2.0D - 1.0D) * 0.012D; + } + this.flowTurnRate = Mth.lerp(0.08D, this.flowTurnRate, this.flowTurnTarget); + Vec3 horizontal = new Vec3(this.travelVelocity.x, 0.0D, this.travelVelocity.z); + if (horizontal.lengthSqr() > 1.0E-6D) { + Vec3 turned = horizontal.yRot((float) this.flowTurnRate); + this.travelVelocity = new Vec3(turned.x, this.travelVelocity.y, turned.z); + } + } + + private void flowAroundBlock(HitResult hit) { + if (!(hit instanceof net.minecraft.world.phys.BlockHitResult blockHit)) { + return; + } + + Vec3 normal = Vec3.atLowerCornerOf(blockHit.getDirection().getNormal()); + Vec3 incoming = this.travelVelocity.lengthSqr() > 1.0E-6D + ? this.travelVelocity + : this.getDeltaMovement(); + double speed = Mth.clamp(incoming.length(), 0.2D, 0.9D); + Vec3 flow = incoming; + if (normal.y > 0.5D) { + flow = new Vec3(incoming.x, 0.0D, incoming.z); + } else if (normal.y < -0.5D) { + flow = new Vec3(incoming.x, 0.0D, incoming.z).add(0.0D, -0.08D, 0.0D); + } else { + Vec3 tangent = incoming.subtract(normal.scale(incoming.dot(normal))); + if (tangent.lengthSqr() < 1.0E-6D) { + tangent = new Vec3(-normal.z, 0.0D, normal.x); + if (tangent.lengthSqr() < 1.0E-6D) { + tangent = new Vec3(0.0D, 0.0D, 1.0D); + } + } + flow = tangent.normalize().scale(speed); + } + if (flow.lengthSqr() < 1.0E-6D) { + this.travelVelocity = Vec3.ZERO; + this.setDeltaMovement(Vec3.ZERO); + this.travelling = false; + this.noPhysics = false; + return; + } + this.setPos(blockHit.getLocation().add(normal.scale(0.08D))); + this.travelVelocity = flow.normalize().scale(speed * 0.82D); + this.setDeltaMovement(this.travelVelocity); + this.noPhysics = true; + if (--this.travelTicksRemaining <= 0) { + this.travelling = false; + this.noPhysics = false; + } + } + @Override public void handleEntityEvent(byte id) { if (id == 60) { @@ -431,18 +529,13 @@ public class WindVortexEntity extends Entity { updatedMovement = new Vec3(updatedMovement.x, Math.max(updatedMovement.y, minRise), updatedMovement.z); entity.setOnGround(false); } + entity.setDeltaMovement(updatedMovement); if (entity instanceof Player) { - Vec3 before = entity.position(); - Vec3 playerCarry = wanted.scale(0.65D); - entity.move(net.minecraft.world.entity.MoverType.SELF, playerCarry); - if (entity.position().distanceToSqr(before) < 1.0E-6D && playerCarry.lengthSqr() > 1.0E-6D) { - entity.setPos(before.add(playerCarry)); - } entity.setOnGround(false); } - entity.setDeltaMovement(updatedMovement); entity.fallDistance = 0.0F; entity.hasImpulse = true; + this.syncPlayerMotion(entity); } private void launch(Entity entity, Vec3 radialVector, double progress, Basis basis) { @@ -452,6 +545,7 @@ public class WindVortexEntity extends Entity { double scale = this.launchStrength * Mth.clamp(this.getVortexHeight() / 5.0D, 0.5D, 2.5D); entity.setDeltaMovement(tangent.scale(0.72D * scale).add(radial.scale(0.34D * scale)).add(basis.axis.scale((0.78D + progress * 0.28D) * scale))); entity.hasImpulse = true; + this.syncPlayerMotion(entity); } private void hurtCaughtEntity(Entity entity) { @@ -487,12 +581,20 @@ public class WindVortexEntity extends Entity { entity.fallDistance = 0.0F; entity.hasImpulse = true; entity.hurtMarked = true; + this.syncPlayerMotion(entity); if (this.isDamaging() && entity instanceof LivingEntity living && !living.getType().is(AntarchyTags.Entities.WIND_VORTEX_IMMUNE)) { living.hurt(this.damageSource(), this.resolveDamage()); } } + private void syncPlayerMotion(Entity entity) { + if (entity instanceof ServerPlayer player) { + player.hurtMarked = true; + player.connection.send(new ClientboundSetEntityMotionPacket(player)); + } + } + private DamageSource damageSource() { Entity owner = this.getOwnerEntity(); if (owner instanceof LivingEntity living) { @@ -582,6 +684,10 @@ public class WindVortexEntity extends Entity { this.homing = homing; } + public void setBounceOnImpact(boolean bounceOnImpact) { + this.bounceOnImpact = bounceOnImpact; + } + public void setVortexStrengths(double pullStrength, double launchStrength) { this.pullStrength = Math.max(0.0D, pullStrength); this.launchStrength = Math.max(0.0D, launchStrength); @@ -630,6 +736,8 @@ public class WindVortexEntity extends Entity { this.setAxis(new Vec3(tag.getFloat(AXIS_X_KEY), tag.getFloat(AXIS_Y_KEY), tag.getFloat(AXIS_Z_KEY))); this.travelling = tag.getBoolean(TRAVELLING_KEY); this.homing = tag.getBoolean(HOMING_KEY); + this.bounceOnImpact = tag.getBoolean(BOUNCE_ON_IMPACT_KEY); + this.anchoredAtBlock = tag.getBoolean(ANCHORED_KEY); this.travelVelocity = new Vec3(tag.getDouble(TRAVEL_X_KEY), tag.getDouble(TRAVEL_Y_KEY), tag.getDouble(TRAVEL_Z_KEY)); if (this.travelling) { this.noPhysics = true; @@ -654,6 +762,8 @@ public class WindVortexEntity extends Entity { tag.putFloat(AXIS_Z_KEY, (float) this.getAxis().z); tag.putBoolean(TRAVELLING_KEY, this.travelling); tag.putBoolean(HOMING_KEY, this.homing); + tag.putBoolean(BOUNCE_ON_IMPACT_KEY, this.bounceOnImpact); + tag.putBoolean(ANCHORED_KEY, this.anchoredAtBlock); tag.putDouble(TRAVEL_X_KEY, this.travelVelocity.x); tag.putDouble(TRAVEL_Y_KEY, this.travelVelocity.y); tag.putDouble(TRAVEL_Z_KEY, this.travelVelocity.z); diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/gravity/GravityWalkNodeEvaluator.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/gravity/GravityWalkNodeEvaluator.java index b2e088b4..cda49d4f 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/gravity/GravityWalkNodeEvaluator.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/gravity/GravityWalkNodeEvaluator.java @@ -1,6 +1,8 @@ package com.craisinlord.antarchy.content.gravity; import net.minecraft.util.Mth; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; import net.minecraft.world.entity.Mob; import net.minecraft.world.level.PathNavigationRegion; import net.minecraft.world.level.pathfinder.Node; @@ -9,6 +11,11 @@ import net.minecraft.world.level.pathfinder.PathfindingContext; import net.minecraft.world.level.pathfinder.WalkNodeEvaluator; public class GravityWalkNodeEvaluator extends WalkNodeEvaluator { + private static final long LOG_TIME_THRESHOLD_NANOS = 25_000_000L; + + private long antarchy$prepareStartNanos; + private int antarchy$nodeProbeCount; + private static final int[] START_Y_SEARCH = {0, 1, -1, 2, -2}; private static final int[][] INVERTED_CARDINAL_NEIGHBOR_OFFSETS = { {1, 0, 0}, @@ -26,10 +33,23 @@ public class GravityWalkNodeEvaluator extends WalkNodeEvaluator { @Override public void prepare(PathNavigationRegion sourceIn, Mob mob) { super.prepare(sourceIn, mob); + this.antarchy$prepareStartNanos = System.nanoTime(); + this.antarchy$nodeProbeCount = 0; } @Override public void done() { + if (this.mob != null && AntarchyGravityApi.isGravityInverted(this.mob)) { + long elapsedNanos = System.nanoTime() - this.antarchy$prepareStartNanos; + if (elapsedNanos > LOG_TIME_THRESHOLD_NANOS) { + com.craisinlord.antarchy.Antarchy.LOGGER.warn( + "[antarchy-gravity] slow inverted pathfind: type={} uuid={} pos=({}, {}, {}) nodeProbes={} elapsedMs={}", + this.mob.getType(), this.mob.getUUID(), + this.mob.getX(), this.mob.getY(), this.mob.getZ(), + this.antarchy$nodeProbeCount, elapsedNanos / 1_000_000.0 + ); + } + } super.done(); } @@ -43,10 +63,9 @@ public class GravityWalkNodeEvaluator extends WalkNodeEvaluator { int z = Mth.floor(this.mob.getZ()); int y = Mth.floor(this.mob.getY() + this.mob.getBbHeight()) - 1; Node node = this.antarchy$findStartNode(x, y, z); - if (node == null) { - return super.getStart(); - } - + // A floor-oriented start is a different coordinate model and must not + // be mixed into an inverted search. PathFinder treats null as a clean + // no-path result. return node; } @@ -66,7 +85,8 @@ public class GravityWalkNodeEvaluator extends WalkNodeEvaluator { } private Node antarchy$createStartNode(int x, int y, int z) { - if (!this.antarchy$hasCeilingSupport(x, y, z)) { + this.antarchy$nodeProbeCount++; + if (!this.antarchy$hasCeilingSupport(this.currentContext, x, y, z)) { return null; } @@ -82,9 +102,16 @@ public class GravityWalkNodeEvaluator extends WalkNodeEvaluator { return node; } - private boolean antarchy$hasCeilingSupport(int x, int y, int z) { - PathType ceilingType = this.getPathType(this.currentContext, x, y + 1, z); - return this.mob.getPathfindingMalus(ceilingType) < 0.0F; + private boolean antarchy$hasCeilingSupport(PathfindingContext context, int x, int y, int z) { + for (int dx = 0; dx < this.entityWidth; dx++) { + for (int dz = 0; dz < this.entityDepth; dz++) { + BlockPos ceilingPos = new BlockPos(x + dx, y + 1, z + dz); + if (!context.level().getBlockState(ceilingPos).isFaceSturdy(context.level(), ceilingPos, Direction.DOWN)) { + return false; + } + } + } + return true; } @Override @@ -93,8 +120,7 @@ public class GravityWalkNodeEvaluator extends WalkNodeEvaluator { return super.getPathTypeOfMob(context, x, y, z, mob); } - PathType ceilingType = this.getPathType(context, x, y + 1, z); - if (mob.getPathfindingMalus(ceilingType) >= 0.0F) { + if (!this.antarchy$hasCeilingSupport(context, x, y, z)) { return PathType.OPEN; } @@ -145,6 +171,18 @@ public class GravityWalkNodeEvaluator extends WalkNodeEvaluator { } } + // Relative "up" for an inverted mob is world-Y down. Keep both + // vertical directions available so a one-block ceiling step can be + // represented in the path instead of trapping the mob below it. + Node stepTowardInvertedUp = this.antarchy$getNeighbor(current.x, current.y - 1, current.z); + if (stepTowardInvertedUp != null) { + neighbors[count++] = stepTowardInvertedUp; + } + Node stepTowardInvertedDown = this.antarchy$getNeighbor(current.x, current.y + 1, current.z); + if (stepTowardInvertedDown != null) { + neighbors[count++] = stepTowardInvertedDown; + } + return count; } @@ -159,12 +197,16 @@ public class GravityWalkNodeEvaluator extends WalkNodeEvaluator { } private Node antarchy$getNeighbor(int x, int y, int z) { + this.antarchy$nodeProbeCount++; PathType type = this.getPathTypeOfMob(this.currentContext, x, y, z, this.mob); - if (this.mob.getPathfindingMalus(type) < 0.0F) { + if (type == PathType.OPEN || this.mob.getPathfindingMalus(type) < 0.0F) { return null; } Node node = this.getNode(x, y, z); + if (node.closed) { + return null; + } node.type = type; node.costMalus = this.mob.getPathfindingMalus(type); return node; 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 e07ea583..731210d2 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 @@ -137,6 +137,7 @@ public class EyeOfTheStormItem extends Item implements GeoItem { vortex.setDamageOverride(AntarchySettings.eyeOfTheStormSurgeDamage()); vortex.setTravel(dir.scale(SURGE_PROJECTILE_SPEED)); vortex.setHoming(true); + vortex.setBounceOnImpact(true); level.addFreshEntity(vortex); level.playSound(null, origin.x, origin.y, origin.z, diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/item/KrakensGraspItem.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/item/KrakensGraspItem.java index fa0af81b..0ca092fc 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/item/KrakensGraspItem.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/item/KrakensGraspItem.java @@ -79,12 +79,14 @@ public class KrakensGraspItem extends TridentItem { public static void strikeLightning(LivingEntity target, ServerLevel level) { strikeLightningAt(level, target.getX(), target.getY(), target.getZ()); + target.hurt(level.damageSources().lightningBolt(), (float) AntarchySettings.krakensGraspLightningDamage()); level.sendParticles(ParticleTypes.ELECTRIC_SPARK, target.getX(), target.getY(0.7D), target.getZ(), 10, 0.25D, 0.25D, 0.25D, 0.02D); } public static void strikeLightningAt(ServerLevel level, double x, double y, double z) { LightningBolt lightningBolt = new LightningBolt(net.minecraft.world.entity.EntityType.LIGHTNING_BOLT, level); lightningBolt.moveTo(x, y, z); + lightningBolt.setVisualOnly(true); level.addFreshEntity(lightningBolt); level.sendParticles(ParticleTypes.ELECTRIC_SPARK, x, y + 0.2D, z, 10, 0.25D, 0.25D, 0.25D, 0.02D); } diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/item/ManticoreWingsItem.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/item/ManticoreWingsItem.java index fa5ca15e..edd1f17e 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/item/ManticoreWingsItem.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/item/ManticoreWingsItem.java @@ -21,7 +21,9 @@ import net.minecraft.world.level.Level; public final class ManticoreWingsItem extends ElytraItem { private static final ResourceLocation REPAIR_MATERIAL_ID = ResourceLocation.fromNamespaceAndPath("antarchy", "manticore_wing"); private static final int DAMAGE_COOLDOWN_TICKS = 10; - private static final float COLLISION_DAMAGE = 8.0F; + private static final double MIN_COLLISION_SPEED = 0.2D; + private static final float COLLISION_DAMAGE_PER_BLOCK_PER_TICK = 8.0F; + private static final float MAX_COLLISION_DAMAGE = 20.0F; private static final Map LAST_COLLISION_DAMAGE = new ConcurrentHashMap<>(); public ManticoreWingsItem(Properties properties) { @@ -41,10 +43,16 @@ public final class ManticoreWingsItem extends ElytraItem { return; } long gameTime = level.getGameTime(); + double speed = player.getDeltaMovement().length(); + if (speed < MIN_COLLISION_SPEED) { + return; + } + float collisionDamage = Math.min(MAX_COLLISION_DAMAGE, + Math.max(1.0F, (float) speed * COLLISION_DAMAGE_PER_BLOCK_PER_TICK)); for (LivingEntity target : level.getEntitiesOfClass(LivingEntity.class, player.getBoundingBox().inflate(0.45D), living -> living != player && living.isAlive() && !player.isAlliedTo(living) && !living.isAlliedTo(player))) { long availableAt = LAST_COLLISION_DAMAGE.getOrDefault(target.getUUID(), 0L); - if (availableAt <= gameTime && target.hurt(level.damageSources().playerAttack(player), COLLISION_DAMAGE)) { + if (availableAt <= gameTime && target.hurt(level.damageSources().playerAttack(player), collisionDamage)) { LAST_COLLISION_DAMAGE.put(target.getUUID(), gameTime + DAMAGE_COOLDOWN_TICKS); } } 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 6801f2cd..31734408 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,5 +1,6 @@ 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; @@ -142,8 +143,18 @@ 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/time/TimeDilationApi.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/time/TimeDilationApi.java index cf369de1..42f2faa6 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 @@ -65,6 +65,18 @@ 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 = access.antarchy$getTimeDilationRate() < TimeDilationMath.NORMAL_RATE; + access.antarchy$setTimeDilationRate(TimeDilationMath.NORMAL_RATE); + if (wasDilated) { + syncEntityRate(entity, TimeDilationMath.NORMAL_RATE); + } + } + SYNCED_CLIENT_RATES.remove(entity.getUUID()); + } + public static void clearSyncedClientRates() { SYNCED_CLIENT_RATES.clear(); } 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 c213de90..00b68271 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,19 +1,16 @@ package com.craisinlord.antarchy.content.time; import java.util.ArrayList; -import java.util.HashSet; import java.util.List; -import java.util.Set; -import net.minecraft.resources.ResourceKey; import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.entity.Entity; -import net.minecraft.world.level.Level; +import com.craisinlord.antarchy.content.effect.DilatedMobEffect; +import com.craisinlord.antarchy.content.effect.RoyalEffectEligibility; +import com.craisinlord.antarchy.content.effect.RoyalEffectHooks; public final class TimeDilationManager { - private static final Set> DILATED_LEVELS = new HashSet<>(); - private TimeDilationManager() { } @@ -21,15 +18,10 @@ public final class TimeDilationManager { for (ServerLevel level : server.getAllLevels()) { List fields = collectFields(level); syncFieldSnapshots(level, fields); - if (fields.isEmpty()) { - if (DILATED_LEVELS.remove(level.dimension())) { - resetRates(level); - } - continue; - } - DILATED_LEVELS.add(level.dimension()); updateEntities(level, fields); - TimeDilationParticles.spawnFieldBorders(level, fields); + if (!fields.isEmpty()) { + TimeDilationParticles.spawnFieldBorders(level, fields); + } } } @@ -60,32 +52,30 @@ public final class TimeDilationManager { if (!(entity instanceof TimeDilationEntityAccess access) || entity instanceof TimeDilationFieldEntity) { continue; } - if (entity.getType().is(com.craisinlord.antarchy.content.AntarchyTags.Entities.TIME_DILATION_IMMUNE)) { - if (access.antarchy$getTimeDilationRate() < TimeDilationMath.NORMAL_RATE) { - access.antarchy$setTimeDilationRate(TimeDilationMath.NORMAL_RATE); - TimeDilationApi.syncEntityRate(entity, TimeDilationMath.NORMAL_RATE); + if (entity.getType().is(com.craisinlord.antarchy.content.AntarchyTags.Entities.TIME_DILATION_IMMUNE) + || (entity instanceof net.minecraft.world.entity.LivingEntity living && !RoyalEffectEligibility.canApplyDilated(living))) { + if (RoyalEffectHooks.dilatedHolder() != null && entity instanceof net.minecraft.world.entity.LivingEntity living) { + living.removeEffect(RoyalEffectHooks.dilatedHolder()); } + TimeDilationApi.clearRate(entity); continue; } double previousRate = access.antarchy$getTimeDilationRate(); - double rate = TimeDilationFieldSampler.sample(fields, entity.position()); - access.antarchy$setTimeDilationRate(rate); - if (Math.abs(previousRate - rate) > 0.001D) { + double fieldRate = TimeDilationFieldSampler.sample(fields, entity.position()); + double effectRate = RoyalEffectHooks.dilatedHolder() != null + && entity instanceof net.minecraft.world.entity.LivingEntity living + && living.hasEffect(RoyalEffectHooks.dilatedHolder()) + ? DilatedMobEffect.RATE : TimeDilationMath.NORMAL_RATE; + double rate = Math.min(fieldRate, effectRate); + if (rate >= TimeDilationMath.NORMAL_RATE) { + TimeDilationApi.clearRate(entity); + } else { + access.antarchy$setTimeDilationRate(rate); + } + if (rate < TimeDilationMath.NORMAL_RATE && Math.abs(previousRate - rate) > 0.001D) { TimeDilationApi.syncEntityRate(entity, rate); } } } - private static void resetRates(ServerLevel level) { - for (Entity entity : level.getAllEntities()) { - if (!(entity instanceof TimeDilationEntityAccess access) || entity instanceof TimeDilationFieldEntity) { - continue; - } - if (access.antarchy$getTimeDilationRate() >= TimeDilationMath.NORMAL_RATE) { - continue; - } - access.antarchy$setTimeDilationRate(TimeDilationMath.NORMAL_RATE); - TimeDilationApi.syncEntityRate(entity, TimeDilationMath.NORMAL_RATE); - } - } } diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/worldgen/elythia/ElythiaFloraFeature.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/worldgen/elythia/ElythiaFloraFeature.java index baba8d74..0c4db5f2 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/worldgen/elythia/ElythiaFloraFeature.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/worldgen/elythia/ElythiaFloraFeature.java @@ -20,6 +20,8 @@ import net.minecraft.world.level.levelgen.Heightmap; import net.minecraft.world.level.levelgen.feature.Feature; import net.minecraft.world.level.levelgen.feature.FeaturePlaceContext; import net.minecraft.world.level.levelgen.feature.configurations.NoneFeatureConfiguration; +import java.util.HashMap; +import java.util.Map; public class ElythiaFloraFeature extends Feature { private static final String SIZEABLE_FOLIAGE_MODID = "sizeable_foliage"; @@ -38,6 +40,8 @@ public class ElythiaFloraFeature extends Feature { WorldGenLevel level = context.level(); RandomSource random = context.random(); BlockPos origin = context.origin(); + Map surfaceHeightCache = new HashMap<>(); + Map oceanFloorHeightCache = new HashMap<>(); int attempts = switch (this.variant) { case FOREST -> 185; case MEADOW -> 95; @@ -68,7 +72,7 @@ public class ElythiaFloraFeature extends Feature { case FLOWER_FOREST_MILKWEED -> 0.0F; }; if (random.nextFloat() < torchflowerChance) { - placedAny |= this.placeTorchflowerPatch(level, origin, random); + placedAny |= this.placeTorchflowerPatch(level, origin, random, surfaceHeightCache); } } @@ -79,7 +83,7 @@ public class ElythiaFloraFeature extends Feature { case FOREST -> 0.05F; case FLOWER_FOREST_MILKWEED -> 0.0F; }) { - placedAny |= this.placePitcherPatch(level, origin, random); + placedAny |= this.placePitcherPatch(level, origin, random, surfaceHeightCache); } if (random.nextFloat() < switch (this.variant) { case MEADOW -> 0.16F; @@ -88,7 +92,7 @@ public class ElythiaFloraFeature extends Feature { case FOREST -> 0.06F; case FLOWER_FOREST_MILKWEED -> 0.18F; }) { - placedAny |= this.placeMilkweedPatch(level, origin, random, AntarchyObjects.ORANGE_MILKWEED.get()); + placedAny |= this.placeMilkweedPatch(level, origin, random, AntarchyObjects.ORANGE_MILKWEED.get(), surfaceHeightCache); } if (random.nextFloat() < switch (this.variant) { case MEADOW -> 0.16F; @@ -97,20 +101,20 @@ public class ElythiaFloraFeature extends Feature { case FOREST -> 0.06F; case FLOWER_FOREST_MILKWEED -> 0.18F; }) { - placedAny |= this.placeMilkweedPatch(level, origin, random, AntarchyObjects.PINK_MILKWEED.get()); + placedAny |= this.placeMilkweedPatch(level, origin, random, AntarchyObjects.PINK_MILKWEED.get(), surfaceHeightCache); } if (this.variant == Variant.PEACH_FOREST && random.nextFloat() < 0.14F) { - placedAny |= this.placeMilkweedPatch(level, origin, random, antarchyBlock(CAMELLIA_ID)); + placedAny |= this.placeMilkweedPatch(level, origin, random, antarchyBlock(CAMELLIA_ID), surfaceHeightCache); } if (this.variant == Variant.FOREST || this.variant == Variant.PEACH_FOREST) { - placedAny |= this.placeBigBushes(level, origin, random, radius); + placedAny |= this.placeBigBushes(level, origin, random, radius, surfaceHeightCache); } for (int i = 0; i < attempts; i++) { int x = origin.getX() + random.nextInt(radius * 2 + 1) - radius; int z = origin.getZ() + random.nextInt(radius * 2 + 1) - radius; - int y = level.getHeight(Heightmap.Types.WORLD_SURFACE_WG, x, z); + int y = cachedHeight(level, Heightmap.Types.WORLD_SURFACE_WG, x, z, surfaceHeightCache); BlockPos plantPos = new BlockPos(x, y, z); BlockPos groundPos = plantPos.below(); @@ -125,7 +129,7 @@ public class ElythiaFloraFeature extends Feature { } } - placedAny |= this.placeSeagrass(level, origin, random, radius); + placedAny |= this.placeSeagrass(level, origin, random, radius, oceanFloorHeightCache); return placedAny; } @@ -227,7 +231,7 @@ public class ElythiaFloraFeature extends Feature { return block; } - private boolean placeBigBushes(WorldGenLevel level, BlockPos origin, RandomSource random, int radius) { + private boolean placeBigBushes(WorldGenLevel level, BlockPos origin, RandomSource random, int radius, Map surfaceHeightCache) { Block bushBlock = BuiltInRegistries.BLOCK.get(ResourceLocation.fromNamespaceAndPath(SIZEABLE_FOLIAGE_MODID, "big_bush")); if (!(bushBlock instanceof BigBushBlock)) { return false; @@ -241,7 +245,7 @@ public class ElythiaFloraFeature extends Feature { for (int i = 0; i < bushAttempts; i++) { int x = origin.getX() + random.nextInt(radius * 2 + 1) - radius; int z = origin.getZ() + random.nextInt(radius * 2 + 1) - radius; - int y = level.getHeight(Heightmap.Types.WORLD_SURFACE_WG, x, z); + int y = cachedHeight(level, Heightmap.Types.WORLD_SURFACE_WG, x, z, surfaceHeightCache); BlockPos plantPos = new BlockPos(x, y, z); if (!isValidPlantSpot(level, plantPos, plantPos.below())) { @@ -333,8 +337,8 @@ public class ElythiaFloraFeature extends Feature { return true; } - private boolean placeTorchflowerPatch(WorldGenLevel level, BlockPos origin, RandomSource random) { - BlockPos patchCenter = sampleSurface(level, origin, random, this.variant == Variant.FOREST ? 8 : 12); + private boolean placeTorchflowerPatch(WorldGenLevel level, BlockPos origin, RandomSource random, Map surfaceHeightCache) { + BlockPos patchCenter = sampleSurface(level, origin, random, this.variant == Variant.FOREST ? 8 : 12, surfaceHeightCache); if (patchCenter == null) { return false; } @@ -346,7 +350,7 @@ public class ElythiaFloraFeature extends Feature { boolean bushEligible = this.variant == Variant.FOREST || this.variant == Variant.MEADOW; boolean placed = false; for (int i = 0; i < plants; i++) { - BlockPos plantPos = samplePatchPos(level, patchCenter, radius, random); + BlockPos plantPos = samplePatchPos(level, patchCenter, radius, random, surfaceHeightCache); if (plantPos == null) { continue; } @@ -369,8 +373,8 @@ public class ElythiaFloraFeature extends Feature { return placed; } - private boolean placePitcherPatch(WorldGenLevel level, BlockPos origin, RandomSource random) { - BlockPos patchCenter = sampleSurface(level, origin, random, this.variant == Variant.FOREST ? 8 : 13); + private boolean placePitcherPatch(WorldGenLevel level, BlockPos origin, RandomSource random, Map surfaceHeightCache) { + BlockPos patchCenter = sampleSurface(level, origin, random, this.variant == Variant.FOREST ? 8 : 13, surfaceHeightCache); if (patchCenter == null) { return false; } @@ -379,7 +383,7 @@ public class ElythiaFloraFeature extends Feature { int plants = this.variant == Variant.BUTTERFLY_FIELDS ? 10 + random.nextInt(12) : this.variant == Variant.FLOWER_FOREST_MILKWEED ? 2 + random.nextInt(3) : 5 + random.nextInt(11); boolean placed = false; for (int i = 0; i < plants; i++) { - BlockPos plantPos = samplePatchPos(level, patchCenter, radius, random); + BlockPos plantPos = samplePatchPos(level, patchCenter, radius, random, surfaceHeightCache); if (plantPos == null) { continue; } @@ -396,8 +400,8 @@ public class ElythiaFloraFeature extends Feature { return placed; } - private boolean placeMilkweedPatch(WorldGenLevel level, BlockPos origin, RandomSource random, Block flower) { - BlockPos patchCenter = sampleSurface(level, origin, random, this.variant == Variant.FOREST ? 8 : 13); + private boolean placeMilkweedPatch(WorldGenLevel level, BlockPos origin, RandomSource random, Block flower, Map surfaceHeightCache) { + BlockPos patchCenter = sampleSurface(level, origin, random, this.variant == Variant.FOREST ? 8 : 13, surfaceHeightCache); if (patchCenter == null) { return false; } @@ -406,7 +410,7 @@ public class ElythiaFloraFeature extends Feature { int plants = this.variant == Variant.BUTTERFLY_FIELDS ? 14 + random.nextInt(16) : this.variant == Variant.FLOWER_FOREST_MILKWEED ? 2 + random.nextInt(4) : 4 + random.nextInt(9); boolean placed = false; for (int i = 0; i < plants; i++) { - BlockPos plantPos = samplePatchPos(level, patchCenter, radius, random); + BlockPos plantPos = samplePatchPos(level, patchCenter, radius, random, surfaceHeightCache); if (plantPos == null) { continue; } @@ -423,7 +427,7 @@ public class ElythiaFloraFeature extends Feature { return placed; } - private boolean placeSeagrass(WorldGenLevel level, BlockPos origin, RandomSource random, int radius) { + private boolean placeSeagrass(WorldGenLevel level, BlockPos origin, RandomSource random, int radius, Map oceanFloorHeightCache) { int attempts = switch (this.variant) { case FOREST -> 3; case MEADOW -> 5; @@ -436,7 +440,7 @@ public class ElythiaFloraFeature extends Feature { for (int i = 0; i < attempts; i++) { int x = origin.getX() + random.nextInt(radius * 2 + 1) - radius; int z = origin.getZ() + random.nextInt(radius * 2 + 1) - radius; - int y = level.getHeight(Heightmap.Types.OCEAN_FLOOR_WG, x, z); + int y = cachedHeight(level, Heightmap.Types.OCEAN_FLOOR_WG, x, z, oceanFloorHeightCache); BlockPos seagrassPos = new BlockPos(x, y, z); BlockPos floorPos = seagrassPos.below(); @@ -455,11 +459,11 @@ public class ElythiaFloraFeature extends Feature { return placed; } - private static BlockPos sampleSurface(WorldGenLevel level, BlockPos origin, RandomSource random, int radius) { + private static BlockPos sampleSurface(WorldGenLevel level, BlockPos origin, RandomSource random, int radius, Map surfaceHeightCache) { for (int attempt = 0; attempt < 8; attempt++) { int x = origin.getX() + random.nextInt(radius * 2 + 1) - radius; int z = origin.getZ() + random.nextInt(radius * 2 + 1) - radius; - int y = level.getHeight(Heightmap.Types.WORLD_SURFACE_WG, x, z); + int y = cachedHeight(level, Heightmap.Types.WORLD_SURFACE_WG, x, z, surfaceHeightCache); BlockPos plantPos = new BlockPos(x, y, z); if (isValidPlantSpot(level, plantPos, plantPos.below())) { return plantPos; @@ -468,14 +472,25 @@ public class ElythiaFloraFeature extends Feature { return null; } - private static BlockPos samplePatchPos(WorldGenLevel level, BlockPos center, int radius, RandomSource random) { + private static BlockPos samplePatchPos(WorldGenLevel level, BlockPos center, int radius, RandomSource random, Map surfaceHeightCache) { int x = center.getX() + random.nextInt(radius * 2 + 1) - radius; int z = center.getZ() + random.nextInt(radius * 2 + 1) - radius; - int y = level.getHeight(Heightmap.Types.WORLD_SURFACE_WG, x, z); + int y = cachedHeight(level, Heightmap.Types.WORLD_SURFACE_WG, x, z, surfaceHeightCache); BlockPos plantPos = new BlockPos(x, y, z); return isOpenPlantSpot(level, plantPos) ? plantPos : null; } + private static int cachedHeight(WorldGenLevel level, Heightmap.Types type, int x, int z, Map cache) { + long key = ((long) x << 32) ^ (z & 0xFFFFFFFFL); + Integer cached = cache.get(key); + if (cached != null) { + return cached; + } + int height = level.getHeight(type, x, z); + cache.put(key, height); + return height; + } + private static void normalizePeachForestGround(WorldGenLevel level, BlockPos groundPos, Variant variant) { if (variant != Variant.PEACH_FOREST) { return; 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 8b96e8e3..9dd44aad 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 @@ -7,7 +7,10 @@ import com.craisinlord.antarchy.content.block.OuranwoodLeavesBlock; import com.craisinlord.antarchy.content.entity.flying_squirrel.FlyingSquirrelEntity; import com.mojang.serialization.Codec; import java.util.ArrayList; +import java.util.ArrayDeque; +import java.util.HashMap; import java.util.List; +import java.util.Map; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.server.level.ServerLevel; @@ -1113,43 +1116,87 @@ public class OuranwoodTreeFeature extends Feature { int minY = Math.max(level.getMinBuildHeight(), origin.getY() - 2); int maxY = Math.min(level.getMaxBuildHeight() - 1, origin.getY() + height + canopyDepth + 8); List leafPositions = new ArrayList<>(); + Map distances = new HashMap<>(); + ArrayDeque queue = new ArrayDeque<>(); BlockPos.MutableBlockPos cursor = new BlockPos.MutableBlockPos(); for (int x = origin.getX() - horizontalRadius; x <= origin.getX() + horizontalRadius; x++) { for (int z = origin.getZ() - horizontalRadius; z <= origin.getZ() + horizontalRadius; z++) { for (int y = minY; y <= maxY; y++) { cursor.set(x, y, z); - if (level.getBlockState(cursor).getBlock() instanceof LeavesBlock) { - leafPositions.add(cursor.immutable()); + BlockState state = level.getBlockState(cursor); + if (state.getBlock() instanceof LeavesBlock && this.getLeafDistanceProperty(state) != null) { + BlockPos pos = cursor.immutable(); + leafPositions.add(pos); + distances.put(pos, this.getLeafDistanceLimit(state)); } } } } - for (int pass = 1; pass <= 16; pass++) { - boolean anyChanged = false; - for (BlockPos pos : leafPositions) { - BlockState state = level.getBlockState(pos); - IntegerProperty distanceProperty = this.getLeafDistanceProperty(state); - if (!(state.getBlock() instanceof LeavesBlock) || distanceProperty == null) { - continue; + for (BlockPos pos : leafPositions) { + BlockState state = level.getBlockState(pos); + int limit = this.getLeafDistanceLimit(state); + int bestDistance = limit; + for (Direction direction : Direction.values()) { + BlockPos neighborPos = pos.relative(direction); + BlockState neighbor = level.getBlockState(neighborPos); + if (neighbor.is(BlockTags.LOGS)) { + bestDistance = 1; + break; } - int computedDistance = this.computeLeafDistance(level, pos, state); - if (computedDistance == state.getValue(distanceProperty)) { - continue; + if (!distances.containsKey(neighborPos)) { + IntegerProperty neighborProperty = this.getLeafDistanceProperty(neighbor); + if (neighborProperty != null) { + bestDistance = Math.min(bestDistance, neighbor.getValue(neighborProperty) + 1); + } } - - BlockState updatedState; - if (state.getBlock() instanceof OuranwoodLeavesBlock) { - updatedState = OuranwoodLeavesBlock.setOuranwoodDistanceForWorldgen(state, computedDistance); - } else { - updatedState = state.setValue(distanceProperty, computedDistance); - } - setBlock(level, pos, updatedState); - anyChanged = true; } - if (!anyChanged) break; + 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 distanceProperty = this.getLeafDistanceProperty(state); + if (distanceProperty == null) { + continue; + } + + int computedDistance = distances.get(pos); + if (computedDistance == state.getValue(distanceProperty)) { + continue; + } + + BlockState updatedState; + if (state.getBlock() instanceof OuranwoodLeavesBlock) { + updatedState = OuranwoodLeavesBlock.setOuranwoodDistanceForWorldgen(state, computedDistance); + } else { + updatedState = state.setValue(distanceProperty, computedDistance); + } + setBlock(level, pos, updatedState); } } 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 new file mode 100644 index 00000000..fec5c963 --- /dev/null +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/worldgen/thoraxis/ThoraxisIchorLakeFeature.java @@ -0,0 +1,63 @@ +package com.craisinlord.antarchy.content.worldgen.thoraxis; + +import com.craisinlord.antarchy.Antarchy; +import com.mojang.serialization.Codec; +import net.minecraft.core.BlockPos; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.level.WorldGenLevel; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.levelgen.feature.Feature; +import net.minecraft.world.level.levelgen.feature.FeaturePlaceContext; +import net.minecraft.world.level.levelgen.feature.configurations.NoneFeatureConfiguration; + +/** + * 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; + private static final ResourceLocation ICHOR_BLOCK_ID = + ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "ichor"); + + public ThoraxisIchorLakeFeature(Codec codec) { + super(codec); + } + + @Override + public boolean place(FeaturePlaceContext context) { + WorldGenLevel level = context.level(); + BlockPos origin = context.origin(); + BlockState ichorState = BuiltInRegistries.BLOCK.get(ICHOR_BLOCK_ID).defaultBlockState(); + if (ichorState.isAir() || ichorState.getFluidState().isEmpty()) { + return false; + } + + int chunkMinX = origin.getX() >> 4 << 4; + int chunkMinZ = origin.getZ() >> 4 << 4; + boolean placedAny = false; + BlockPos.MutableBlockPos mutable = new BlockPos.MutableBlockPos(); + + for (int x = chunkMinX; x < chunkMinX + 16; x++) { + for (int z = chunkMinZ; z < chunkMinZ + 16; z++) { + for (int y = LAKE_FLOOR_Y; y <= LAKE_SURFACE_Y; y++) { + mutable.set(x, y, z); + BlockState current = level.getBlockState(mutable); + if (!isOpenForLake(current)) { + continue; + } + + level.setBlock(mutable, ichorState, 2); + placedAny = true; + } + } + } + + return placedAny; + } + + private static boolean isOpenForLake(BlockState state) { + return state.isAir() || state.canBeReplaced() || !state.blocksMotion(); + } +} 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 f27b3ba5..04e3ba75 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 @@ -35,6 +35,8 @@ public final class ThoraxisUndersideManager { private static final int DISCOVERY_INTERVAL_TICKS = 5; private static final int SIMULATION_DISTANCE_MARGIN_CHUNKS = 1; private static final int MAX_FLIPS_PER_TICK = 8; + private static final double SLOW_TICK_WARN_MS = 25.0D; + private static final long DIAGNOSTIC_INTERVAL_TICKS = 100L; private static final int ENTER_UNDERSIDE_Y = GRAVITY_FLIP_Y - 4; private static final int EXIT_UNDERSIDE_Y = GRAVITY_FLIP_Y + 4; private static final int FLIP_COOLDOWN_TICKS = 40; @@ -55,12 +57,20 @@ public final class ThoraxisUndersideManager { return; } + long startNanos = System.nanoTime(); long now = level.getGameTime(); + int discoveredLiving = 0; + int discoveredItems = 0; if (now % DISCOVERY_INTERVAL_TICKS == 0L) { - tracking.discover(level); + DiscoveryStats stats = tracking.discover(level); + discoveredLiving = stats.livingEntities(); + discoveredItems = stats.itemEntities(); } int flipsRemaining = MAX_FLIPS_PER_TICK; + int refreshedLiving = 0; + int flippedItems = 0; + int restoredItems = 0; for (Entity entity : tracking.entities) { if (!entity.isAlive() || entity.isSpectator()) { continue; @@ -71,6 +81,7 @@ public final class ThoraxisUndersideManager { int threshold = hasEffect ? EXIT_UNDERSIDE_Y : ENTER_UNDERSIDE_Y; if (living.getY() < threshold) { refreshInvertedEffect(living); + refreshedLiving++; } continue; } @@ -94,6 +105,7 @@ public final class ThoraxisUndersideManager { flipsRemaining--; tracking.lastFlipTick.put(uuid, now); AntarchyGravityApi.setForcedGravityDirection(entity, AntarchyGravityDirection.UP, TRANSITION); + flippedItems++; } continue; } @@ -108,6 +120,7 @@ public final class ThoraxisUndersideManager { if (isForcedUp) { tracking.lastFlipTick.put(uuid, now); AntarchyGravityApi.setGravityDirection(entity, AntarchyGravityDirection.DOWN, false, TRANSITION); + restoredItems++; } } } @@ -115,26 +128,44 @@ public final class ThoraxisUndersideManager { 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); + + double elapsedMs = (System.nanoTime() - startNanos) / 1_000_000.0D; + if (elapsedMs >= SLOW_TICK_WARN_MS && now - tracking.lastDiagnosticTick >= DIAGNOSTIC_INTERVAL_TICKS) { + tracking.lastDiagnosticTick = now; + Antarchy.LOGGER.warn( + "[antarchy-thoraxis] slow underside tick dim={} gameTime={} elapsedMs={} trackedEntities={} forcedItems={} discoveredLiving={} discoveredItems={} refreshedLiving={} flippedItems={} restoredItems={} players={}", + level.dimension().location(), now, elapsedMs, tracking.entities.size(), tracking.forcedItems.size(), + discoveredLiving, discoveredItems, refreshedLiving, flippedItems, restoredItems, level.players().size() + ); + } } private static final class TrackingState { private final Set entities = Collections.newSetFromMap(new IdentityHashMap<>()); private final Set forcedItems = Collections.newSetFromMap(new IdentityHashMap<>()); private final Map lastFlipTick = new java.util.HashMap<>(); + private long lastDiagnosticTick = Long.MIN_VALUE / 2L; - private void discover(ServerLevel level) { + private DiscoveryStats discover(ServerLevel level) { int radius = (level.getServer().getPlayerList().getSimulationDistance() + SIMULATION_DISTANCE_MARGIN_CHUNKS) * 16; + int discoveredLiving = 0; + int discoveredItems = 0; for (ServerPlayer player : level.players()) { AABB area = new AABB( player.getX() - radius, level.getMinBuildHeight(), player.getZ() - radius, player.getX() + radius, level.getMaxBuildHeight(), player.getZ() + radius ); entities.add(player); - entities.addAll(level.getEntitiesOfClass(LivingEntity.class, area, - entity -> !entity.isSpectator() && entity.getY() < EXIT_UNDERSIDE_Y)); - entities.addAll(level.getEntitiesOfClass(ItemEntity.class, area, - entity -> !entity.isSpectator() && entity.getY() < EXIT_UNDERSIDE_Y)); + java.util.List livingEntities = level.getEntitiesOfClass(LivingEntity.class, area, + entity -> !entity.isSpectator() && entity.getY() < EXIT_UNDERSIDE_Y); + java.util.List itemEntities = level.getEntitiesOfClass(ItemEntity.class, area, + entity -> !entity.isSpectator() && entity.getY() < EXIT_UNDERSIDE_Y); + discoveredLiving += livingEntities.size(); + discoveredItems += itemEntities.size(); + entities.addAll(livingEntities); + entities.addAll(itemEntities); } + return new DiscoveryStats(discoveredLiving, discoveredItems); } private void clear() { @@ -149,6 +180,9 @@ public final class ThoraxisUndersideManager { return last == null || now - last >= FLIP_COOLDOWN_TICKS; } + private record DiscoveryStats(int livingEntities, int itemEntities) { + } + public static void applyUndersideInversion(LivingEntity living) { refreshInvertedEffect(living); } @@ -184,4 +218,8 @@ public final class ThoraxisUndersideManager { public static boolean isThoraxis(Level level) { return level.dimension().location().equals(THORAXIS_DIMENSION); } + + public static boolean isAboveUndersideExit(Entity entity) { + return isThoraxis(entity.level()) && entity.getY() >= EXIT_UNDERSIDE_Y; + } } diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/worldgen/truffalo/TruffaloTreeConfiguration.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/worldgen/truffalo/TruffaloTreeConfiguration.java index e0654ec1..806d6eb3 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/worldgen/truffalo/TruffaloTreeConfiguration.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/worldgen/truffalo/TruffaloTreeConfiguration.java @@ -8,10 +8,14 @@ import net.minecraft.world.level.levelgen.feature.stateproviders.BlockStateProvi public record TruffaloTreeConfiguration( BlockStateProvider trunkProvider, - IntProvider height + IntProvider height, + int tuftRadius, + int tuftHeight ) implements FeatureConfiguration { public static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( BlockStateProvider.CODEC.fieldOf("trunk_provider").forGetter(TruffaloTreeConfiguration::trunkProvider), - IntProvider.codec(4, 9).fieldOf("height").forGetter(TruffaloTreeConfiguration::height) + IntProvider.codec(4, 18).fieldOf("height").forGetter(TruffaloTreeConfiguration::height), + Codec.INT.optionalFieldOf("tuft_radius", 1).forGetter(TruffaloTreeConfiguration::tuftRadius), + Codec.INT.optionalFieldOf("tuft_height", 3).forGetter(TruffaloTreeConfiguration::tuftHeight) ).apply(instance, TruffaloTreeConfiguration::new)); } diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/worldgen/truffalo/TruffaloTreeFeature.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/worldgen/truffalo/TruffaloTreeFeature.java index 25dd4374..1e570985 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/worldgen/truffalo/TruffaloTreeFeature.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/worldgen/truffalo/TruffaloTreeFeature.java @@ -58,9 +58,9 @@ public class TruffaloTreeFeature extends Feature { return false; } } - for (int dy = 0; dy <= 2; dy++) { - for (int dx = -1; dx <= 1; dx++) { - for (int dz = -1; dz <= 1; dz++) { + for (int dy = 0; dy < config.tuftHeight(); dy++) { + for (int dx = -config.tuftRadius(); dx <= config.tuftRadius(); dx++) { + for (int dz = -config.tuftRadius(); dz <= config.tuftRadius(); dz++) { if (!canReplace(level, capBase.offset(dx, dy, dz))) { return false; } @@ -74,9 +74,9 @@ public class TruffaloTreeFeature extends Feature { Block tuft = BuiltInRegistries.BLOCK.get(ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, color.getName() + "_truffalo_tuft")); BlockState tuftState = tuft.defaultBlockState(); - for (int dy = 0; dy <= 2; dy++) { - for (int dx = -1; dx <= 1; dx++) { - for (int dz = -1; dz <= 1; dz++) { + for (int dy = 0; dy < config.tuftHeight(); dy++) { + for (int dx = -config.tuftRadius(); dx <= config.tuftRadius(); dx++) { + for (int dz = -config.tuftRadius(); dz <= config.tuftRadius(); dz++) { setBlock(level, capBase.offset(dx, dy, dz), tuftState); } } diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/CommandedMobMixin.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/CommandedMobMixin.java new file mode 100644 index 00000000..0dd0ff3f --- /dev/null +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/CommandedMobMixin.java @@ -0,0 +1,52 @@ +package com.craisinlord.antarchy.mixins; + +import com.craisinlord.antarchy.content.effect.CommandedEntityAccess; +import java.util.UUID; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.entity.Mob; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; + +@Mixin(Mob.class) +public abstract class CommandedMobMixin implements CommandedEntityAccess { + @Unique + private UUID antarchy$commanderUuid; + @Unique + private boolean antarchy$commandedTargetOwned; + + @Override + public UUID antarchy$getCommanderUuid() { + return antarchy$commanderUuid; + } + + @Override + public void antarchy$setCommanderUuid(UUID uuid) { + antarchy$commanderUuid = uuid; + } + + @Override + public boolean antarchy$isCommandedTargetOwned() { + return antarchy$commandedTargetOwned; + } + + @Override + public void antarchy$setCommandedTargetOwned(boolean owned) { + antarchy$commandedTargetOwned = owned; + } + + @org.spongepowered.asm.mixin.injection.Inject(method = "addAdditionalSaveData", at = @org.spongepowered.asm.mixin.injection.At("TAIL")) + private void antarchy$saveCommandedData(CompoundTag tag, org.spongepowered.asm.mixin.injection.callback.CallbackInfo ci) { + if (antarchy$commanderUuid != null) { + tag.putUUID("AntarchyCommander", antarchy$commanderUuid); + tag.putBoolean("AntarchyCommandedTargetOwned", antarchy$commandedTargetOwned); + } + } + + @org.spongepowered.asm.mixin.injection.Inject(method = "readAdditionalSaveData", at = @org.spongepowered.asm.mixin.injection.At("TAIL")) + private void antarchy$loadCommandedData(CompoundTag tag, org.spongepowered.asm.mixin.injection.callback.CallbackInfo ci) { + if (tag.hasUUID("AntarchyCommander")) { + antarchy$commanderUuid = tag.getUUID("AntarchyCommander"); + antarchy$commandedTargetOwned = tag.getBoolean("AntarchyCommandedTargetOwned"); + } + } +} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/LivingEntityRoyalEffectsMixin.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/LivingEntityRoyalEffectsMixin.java new file mode 100644 index 00000000..f9e325c6 --- /dev/null +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/LivingEntityRoyalEffectsMixin.java @@ -0,0 +1,90 @@ +package com.craisinlord.antarchy.mixins; + +import com.craisinlord.antarchy.content.effect.CommandedEntityAccess; +import com.craisinlord.antarchy.content.effect.RoyalEffectEligibility; +import java.util.UUID; +import net.minecraft.world.effect.MobEffectInstance; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.Mob; +import net.minecraft.world.entity.AreaEffectCloud; +import net.minecraft.world.entity.projectile.Projectile; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +@Mixin(LivingEntity.class) +public abstract class LivingEntityRoyalEffectsMixin { + @Unique + private UUID antarchy$previousCommander; + + @Inject(method = "canBeAffected", at = @At("HEAD"), cancellable = true) + private void antarchy$checkRoyalEffectBlacklist(MobEffectInstance instance, CallbackInfoReturnable cir) { + if (!RoyalEffectEligibility.canApply((LivingEntity) (Object) this, instance)) { + cir.setReturnValue(false); + } + } + + @Inject(method = "addEffect(Lnet/minecraft/world/effect/MobEffectInstance;Lnet/minecraft/world/entity/Entity;)Z", at = @At("HEAD"), cancellable = true) + private void antarchy$prepareCommandedSource(MobEffectInstance instance, Entity source, CallbackInfoReturnable cir) { + if (!RoyalEffectEligibility.isCommanded(instance)) { + return; + } + LivingEntity entity = (LivingEntity) (Object) this; + if (!RoyalEffectEligibility.canApplyCommanded(entity) || !(entity instanceof Mob mob)) { + cir.setReturnValue(false); + return; + } + PlayerSource playerSource = antarchy$resolvePlayer(source); + if (playerSource == null) { + cir.setReturnValue(false); + return; + } + CommandedEntityAccess access = (CommandedEntityAccess) mob; + UUID current = access.antarchy$getCommanderUuid(); + if (current != null && !current.equals(playerSource.player().getUUID())) { + cir.setReturnValue(false); + return; + } + antarchy$previousCommander = current; + access.antarchy$setCommanderUuid(playerSource.player().getUUID()); + } + + @Inject(method = "addEffect(Lnet/minecraft/world/effect/MobEffectInstance;Lnet/minecraft/world/entity/Entity;)Z", at = @At("RETURN")) + private void antarchy$finishCommandedSource(MobEffectInstance instance, Entity source, CallbackInfoReturnable cir) { + if (!RoyalEffectEligibility.isCommanded(instance) || cir.getReturnValue()) { + return; + } + LivingEntity entity = (LivingEntity) (Object) this; + if (entity instanceof CommandedEntityAccess access) { + access.antarchy$setCommanderUuid(antarchy$previousCommander); + } + } + + @Inject(method = "onEffectRemoved", at = @At("HEAD")) + private void antarchy$clearCommandedState(MobEffectInstance instance, org.spongepowered.asm.mixin.injection.callback.CallbackInfo ci) { + if (RoyalEffectEligibility.isCommanded(instance) && (Object) this instanceof Mob mob) { + com.craisinlord.antarchy.content.effect.CommandedBehavior.clear(mob); + } + } + + @Unique + private PlayerSource antarchy$resolvePlayer(Entity source) { + if (source instanceof net.minecraft.world.entity.player.Player player) { + return new PlayerSource(player); + } + if (source instanceof Projectile projectile && projectile.getOwner() instanceof net.minecraft.world.entity.player.Player player) { + return new PlayerSource(player); + } + if (source instanceof AreaEffectCloud cloud && cloud.getOwner() instanceof net.minecraft.world.entity.player.Player player) { + return new PlayerSource(player); + } + return null; + } + + @Unique + private record PlayerSource(net.minecraft.world.entity.player.Player player) { + } +} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/MobCommandedTargetMixin.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/MobCommandedTargetMixin.java new file mode 100644 index 00000000..25e33859 --- /dev/null +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/MobCommandedTargetMixin.java @@ -0,0 +1,29 @@ +package com.craisinlord.antarchy.mixins; + +import com.craisinlord.antarchy.content.effect.CommandedBehavior; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.Mob; +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(Mob.class) +public abstract class MobCommandedTargetMixin { + @Inject(method = "setTarget", at = @At("HEAD"), cancellable = true) + private void antarchy$protectCommandedAllies(LivingEntity target, CallbackInfo ci) { + Mob mob = (Mob) (Object) this; + if (target != null && CommandedBehavior.isProtectedAlly(mob, target)) { + ci.cancel(); + } + } + + @Inject(method = "doHurtTarget", at = @At("HEAD"), cancellable = true) + private void antarchy$preventCommandedFriendlyFire(net.minecraft.world.entity.Entity target, + org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable cir) { + Mob mob = (Mob) (Object) this; + if (CommandedBehavior.isProtectedAlly(mob, target)) { + cir.setReturnValue(false); + } + } +} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/gravity/EntityGravityMixin.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/gravity/EntityGravityMixin.java index 72db7976..620a4975 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/gravity/EntityGravityMixin.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/gravity/EntityGravityMixin.java @@ -5,10 +5,16 @@ 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.content.gravity.AntarchyGravityTransition; +import com.craisinlord.antarchy.content.entity.BedBugEntity; +import com.craisinlord.antarchy.content.entity.portal.DimensionalTearEntity; +import com.craisinlord.antarchy.content.worldgen.thoraxis.ThoraxisUndersideManager; import net.minecraft.nbt.CompoundTag; import net.minecraft.core.BlockPos; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.effect.MobEffectInstance; import net.minecraft.world.level.material.FluidState; import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.Mob; import net.minecraft.world.entity.player.Player; import net.minecraft.world.phys.AABB; import org.spongepowered.asm.mixin.Mixin; @@ -71,6 +77,51 @@ public abstract class EntityGravityMixin implements AntarchyGravityAccess { this.antarchy$gravityForced = tag.getBoolean(ANTARCHY_GRAVITY_FORCED_TAG); this.antarchy$gravityTransitionDuration = Math.max(0, tag.getInt(ANTARCHY_GRAVITY_TRANSITION_DURATION_TAG)); this.antarchy$gravityTransitionRemaining = Math.max(0, tag.getInt(ANTARCHY_GRAVITY_TRANSITION_REMAINING_TAG)); + + Entity entity = this.antarchy$entity(); + if (this.antarchy$gravityDirection.isInverted() && this.antarchy$gravityForced + && entity instanceof Mob mob) { + MobEffectInstance invertedEffect = mob.getEffect(com.craisinlord.antarchy.content.AntarchyObjects.INVERTED_EFFECT.get()); + boolean aboveUndersideExit = ThoraxisUndersideManager.isAboveUndersideExit(mob); + int nearbyDimensionalTears = this.antarchy$countNearbyDimensionalTears(mob); + if (invertedEffect != null + && invertedEffect.isAmbient() + && !invertedEffect.isVisible() + && aboveUndersideExit) { + this.antarchy$clearLoadedInversion(mob); + com.craisinlord.antarchy.Antarchy.LOGGER.warn( + "[antarchy-gravity] cleared stale underside inversion from loaded mob type={} uuid={} pos=({}, {}, {}) dim={} effectDuration={} nearbyDimensionalTears={}", + mob.getType(), mob.getUUID(), mob.getX(), mob.getY(), mob.getZ(), + mob.level() != null ? mob.level().dimension().location() : "?", + invertedEffect.getDuration(), nearbyDimensionalTears + ); + return; + } + if (invertedEffect != null + && mob instanceof BedBugEntity + && aboveUndersideExit + && ThoraxisUndersideManager.isThoraxis(mob.level())) { + this.antarchy$clearLoadedInversion(mob); + com.craisinlord.antarchy.Antarchy.LOGGER.warn( + "[antarchy-gravity] cleared loaded inverted bed bug above underside exit type={} uuid={} pos=({}, {}, {}) dim={} effectDuration={} effectAmbient={} effectVisible={} nearbyDimensionalTears={}", + mob.getType(), mob.getUUID(), mob.getX(), mob.getY(), mob.getZ(), + mob.level() != null ? mob.level().dimension().location() : "?", + invertedEffect.getDuration(), invertedEffect.isAmbient(), invertedEffect.isVisible(), nearbyDimensionalTears + ); + return; + } + com.craisinlord.antarchy.Antarchy.LOGGER.info( + "[antarchy-gravity] loaded inverted mob type={} uuid={} pos=({}, {}, {}) dim={} hasInvertedEffect={} effectDuration={} effectAmbient={} effectVisible={} aboveUndersideExit={} nearbyDimensionalTears={}", + mob.getType(), mob.getUUID(), mob.getX(), mob.getY(), mob.getZ(), + mob.level() != null ? mob.level().dimension().location() : "?", + invertedEffect != null, + invertedEffect != null ? invertedEffect.getDuration() : -1, + invertedEffect != null && invertedEffect.isAmbient(), + invertedEffect != null && invertedEffect.isVisible(), + aboveUndersideExit, + nearbyDimensionalTears + ); + } } @Inject(method = "baseTick", at = @At("TAIL")) @@ -134,6 +185,11 @@ public abstract class EntityGravityMixin implements AntarchyGravityAccess { } this.antarchy$gravityForced = forced; + if (changed && this.antarchy$entity() instanceof Mob mob) { + // Any existing path is expressed in the previous gravity frame. + // Keep goals from following stale nodes after a flip. + mob.getNavigation().stop(); + } if (changed && !this.antarchy$entity().level().isClientSide) { AntarchyGravityApi.notifyGravityStateChanged(this.antarchy$entity()); } @@ -172,6 +228,29 @@ public abstract class EntityGravityMixin implements AntarchyGravityAccess { this.setBoundingBox(this.makeBoundingBox()); } + @Unique + private void antarchy$clearLoadedInversion(Mob mob) { + mob.removeEffect(com.craisinlord.antarchy.content.AntarchyObjects.INVERTED_EFFECT.get()); + this.antarchy$gravityDirection = AntarchyGravityDirection.DOWN; + this.antarchy$prevGravityDirection = AntarchyGravityDirection.DOWN; + this.antarchy$gravityForced = false; + this.antarchy$gravityTransitionDuration = 0; + this.antarchy$gravityTransitionRemaining = 0; + this.antarchy$refreshGravityBounds(); + } + + @Unique + private int antarchy$countNearbyDimensionalTears(Mob mob) { + if (!(mob.level() instanceof ServerLevel serverLevel)) { + return -1; + } + return serverLevel.getEntitiesOfClass( + DimensionalTearEntity.class, + mob.getBoundingBox().inflate(48.0D), + Entity::isAlive + ).size(); + } + @Unique private void antarchy$adjustPositionForGravityChange(AntarchyGravityDirection oldDirection, AntarchyGravityDirection newDirection) { if (oldDirection == newDirection) { diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/gravity/GroundPathNavigationMixin.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/gravity/GroundPathNavigationMixin.java index fc10d605..6442a003 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/gravity/GroundPathNavigationMixin.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/gravity/GroundPathNavigationMixin.java @@ -1,26 +1,89 @@ package com.craisinlord.antarchy.mixins.gravity; -import com.craisinlord.antarchy.content.gravity.AntarchyGravityApi; import com.craisinlord.antarchy.content.gravity.GravityWalkNodeEvaluator; +import com.craisinlord.antarchy.content.gravity.AntarchyGravityApi; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.world.entity.EntityDimensions; +import net.minecraft.world.entity.Pose; +import net.minecraft.world.entity.Mob; import net.minecraft.world.entity.ai.navigation.GroundPathNavigation; import net.minecraft.world.level.pathfinder.NodeEvaluator; import net.minecraft.world.level.pathfinder.PathFinder; +import net.minecraft.world.phys.AABB; +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.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; @Mixin(GroundPathNavigation.class) -/* - * Makes ground pathing read gravity-aware floor checks. - */ public abstract class GroundPathNavigationMixin { @Inject(method = "createPathFinder", at = @At("HEAD"), cancellable = true) private void antarchy$useGravityEvaluator(int maxVisitedNodes, CallbackInfoReturnable cir) { var accessor = (PathNavigationAccessor) (Object) this; - var mob = accessor.antarchy$getMob(); NodeEvaluator evaluator = new GravityWalkNodeEvaluator(); + // GroundPathNavigation enables door passing on its vanilla evaluator. + // Preserve that setting when swapping in the gravity-aware evaluator. + evaluator.setCanPassDoors(true); accessor.antarchy$setNodeEvaluator(evaluator); cir.setReturnValue(new PathFinder(evaluator, maxVisitedNodes)); } + + /** + * GroundPathNavigation normalizes entity destinations toward the floor + * before delegating to PathNavigation. For inverted mobs, replace that + * final destination with a bounded, collision-free ceiling anchor. + */ + @org.spongepowered.asm.mixin.injection.ModifyArg( + method = "createPath(Lnet/minecraft/core/BlockPos;I)Lnet/minecraft/world/level/pathfinder/Path;", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/world/entity/ai/navigation/PathNavigation;createPath(Lnet/minecraft/core/BlockPos;I)Lnet/minecraft/world/level/pathfinder/Path;" + ), + index = 0 + ) + private BlockPos antarchy$projectInvertedDestination(BlockPos normalizedDestination) { + Mob mob = ((PathNavigationAccessor) (Object) this).antarchy$getMob(); + if (!AntarchyGravityApi.isGravityInverted(mob)) { + return normalizedDestination; + } + + EntityDimensions dimensions = mob.getDimensions(mob.getPose()); + int centerX = normalizedDestination.getX(); + int centerZ = normalizedDestination.getZ(); + int baseY = normalizedDestination.getY(); + int minY = Math.max(mob.level().getMinBuildHeight(), baseY - 32); + int maxY = Math.min(mob.level().getMaxBuildHeight() - 2, baseY + 32); + for (int candidateY = baseY; candidateY <= maxY; candidateY++) { + BlockPos support = new BlockPos(centerX, candidateY + 1, centerZ); + if (!mob.level().getBlockState(support).isFaceSturdy(mob.level(), support, Direction.DOWN)) { + continue; + } + + double topY = candidateY + 1.0D - 1.0E-3D; + double halfWidth = dimensions.width() / 2.0D; + AABB body = new AABB( + centerX + 0.5D - halfWidth, + topY - dimensions.height(), + centerZ + 0.5D - halfWidth, + centerX + 0.5D + halfWidth, + topY, + centerZ + 0.5D + halfWidth + ).deflate(1.0E-4D); + if (mob.level().noCollision(mob, body)) { + return new BlockPos(centerX, candidateY, centerZ); + } + } + + return normalizedDestination; + } + + @Inject(method = "getTempMobPos", at = @At("HEAD"), cancellable = true) + private void antarchy$useInvertedMobAnchor(CallbackInfoReturnable cir) { + Mob mob = ((PathNavigationAccessor) (Object) this).antarchy$getMob(); + if (AntarchyGravityApi.isGravityInverted(mob)) { + cir.setReturnValue(new Vec3(mob.getX(), mob.getY(), mob.getZ())); + } + } } diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/gravity/MeleeAttackGoalGravityMixin.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/gravity/MeleeAttackGoalGravityMixin.java new file mode 100644 index 00000000..d69998dc --- /dev/null +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/gravity/MeleeAttackGoalGravityMixin.java @@ -0,0 +1,101 @@ +package com.craisinlord.antarchy.mixins.gravity; + +import com.craisinlord.antarchy.content.gravity.AntarchyGravityApi; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.PathfinderMob; +import net.minecraft.world.entity.ai.goal.MeleeAttackGoal; +import net.minecraft.world.entity.player.Player; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +@Mixin(MeleeAttackGoal.class) +public abstract class MeleeAttackGoalGravityMixin { + @Shadow + @Final + protected PathfinderMob mob; + + @Shadow + @Final + private double speedModifier; + + @Shadow + @Final + private boolean followingTargetEvenIfNotSeen; + + @Shadow + private int ticksUntilNextAttack; + + @Shadow + protected abstract void checkAndPerformAttack(LivingEntity enemy); + + @Inject(method = "canUse", at = @At("HEAD"), cancellable = true) + private void antarchy$useInvertedMelee(CallbackInfoReturnable cir) { + if (!AntarchyGravityApi.isGravityInverted(this.mob)) { + return; + } + + LivingEntity target = this.mob.getTarget(); + cir.setReturnValue(this.antarchy$isValidInvertedMeleeTarget(target)); + } + + @Inject(method = "canContinueToUse", at = @At("HEAD"), cancellable = true) + private void antarchy$continueInvertedMelee(CallbackInfoReturnable cir) { + if (!AntarchyGravityApi.isGravityInverted(this.mob)) { + return; + } + + LivingEntity target = this.mob.getTarget(); + cir.setReturnValue(this.antarchy$isValidInvertedMeleeTarget(target)); + } + + @Inject(method = "start", at = @At("HEAD"), cancellable = true) + private void antarchy$startInvertedMelee(CallbackInfo ci) { + if (!AntarchyGravityApi.isGravityInverted(this.mob)) { + return; + } + + LivingEntity target = this.mob.getTarget(); + this.mob.getNavigation().stop(); + this.mob.setAggressive(true); + this.ticksUntilNextAttack = 0; + ci.cancel(); + } + + @Inject(method = "tick", at = @At("HEAD"), cancellable = true) + private void antarchy$tickInvertedMelee(CallbackInfo ci) { + if (!AntarchyGravityApi.isGravityInverted(this.mob)) { + return; + } + + LivingEntity target = this.mob.getTarget(); + if (!this.antarchy$isValidInvertedMeleeTarget(target)) { + this.mob.getNavigation().stop(); + ci.cancel(); + return; + } + + this.mob.getLookControl().setLookAt(target, 30.0F, 30.0F); + // Do not call PathNavigation.moveTo here: that starts the unsafe vanilla + // path acquisition path. MoveControl receives a world-space target and + // the gravity movement mixin converts it into the inverted frame. + this.mob.getMoveControl().setWantedPosition(target.getX(), target.getY(), target.getZ(), this.speedModifier); + this.ticksUntilNextAttack = Math.max(0, this.ticksUntilNextAttack - 1); + this.checkAndPerformAttack(target); + ci.cancel(); + } + + private boolean antarchy$isValidInvertedMeleeTarget(LivingEntity target) { + if (target == null || !target.isAlive()) { + return false; + } + if (this.followingTargetEvenIfNotSeen && !this.mob.isWithinRestriction(target.blockPosition())) { + return false; + } + return !(target instanceof Player player) || (!player.isSpectator() && !player.isCreative()); + } +} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/gravity/MoveControlGravityMixin.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/gravity/MoveControlGravityMixin.java new file mode 100644 index 00000000..60116734 --- /dev/null +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/gravity/MoveControlGravityMixin.java @@ -0,0 +1,48 @@ +package com.craisinlord.antarchy.mixins.gravity; + +import com.craisinlord.antarchy.content.gravity.AntarchyGravityApi; +import com.craisinlord.antarchy.content.gravity.AntarchyGravityRotationUtil; +import net.minecraft.world.entity.Mob; +import net.minecraft.world.entity.ai.control.MoveControl; +import net.minecraft.world.phys.Vec3; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(MoveControl.class) +public abstract class MoveControlGravityMixin { + @Shadow + @Final + protected Mob mob; + + @Shadow + protected double wantedX; + + @Shadow + protected double wantedY; + + @Shadow + protected double wantedZ; + + @Shadow + protected double speedModifier; + + @Inject(method = "setWantedPosition", at = @At("TAIL")) + private void antarchy$convertInvertedWantedPosition(double x, double y, double z, double speed, CallbackInfo ci) { + if (!AntarchyGravityApi.isGravityInverted(this.mob)) { + return; + } + + Vec3 worldOffset = new Vec3(x - this.mob.getX(), y - this.mob.getY(), z - this.mob.getZ()); + Vec3 localOffset = AntarchyGravityRotationUtil.vecWorldToPlayer( + worldOffset, + AntarchyGravityApi.getGravityDirection(this.mob)); + this.wantedX = this.mob.getX() + localOffset.x; + this.wantedY = this.mob.getY() + localOffset.y; + this.wantedZ = this.mob.getZ() + localOffset.z; + this.speedModifier = speed; + } +} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/gravity/RandomStrollGoalGravityMixin.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/gravity/RandomStrollGoalGravityMixin.java new file mode 100644 index 00000000..a82324be --- /dev/null +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/gravity/RandomStrollGoalGravityMixin.java @@ -0,0 +1,161 @@ +package com.craisinlord.antarchy.mixins.gravity; + +import com.craisinlord.antarchy.content.gravity.AntarchyGravityApi; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.util.Mth; +import net.minecraft.world.entity.PathfinderMob; +import net.minecraft.world.entity.Pose; +import net.minecraft.world.entity.ai.goal.RandomStrollGoal; +import net.minecraft.world.entity.EntityDimensions; +import net.minecraft.world.level.Level; +import net.minecraft.world.phys.AABB; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +@Mixin(RandomStrollGoal.class) +public abstract class RandomStrollGoalGravityMixin { + @Unique + private static final int ANTARCHY$INVERTED_STROLL_ATTEMPTS = 12; + @Unique + private static final int ANTARCHY$INVERTED_STROLL_HORIZONTAL_RANGE = 7; + @Unique + private static final int ANTARCHY$INVERTED_STROLL_VERTICAL_RANGE = 2; + + @Shadow + @Final + protected PathfinderMob mob; + + @Shadow + protected double wantedX; + + @Shadow + protected double wantedY; + + @Shadow + protected double wantedZ; + + @Shadow + @Final + protected double speedModifier; + + @Shadow + protected int interval; + + @Shadow + protected boolean forceTrigger; + + @Shadow + @Final + private boolean checkNoActionTime; + + @Unique + private int antarchy$invertedStrollTicks; + + @Inject(method = "canUse", at = @At("HEAD"), cancellable = true) + private void antarchy$useInvertedRandomStroll(CallbackInfoReturnable cir) { + if (!AntarchyGravityApi.isGravityInverted(this.mob)) { + return; + } + + if (this.mob.hasControllingPassenger()) { + cir.setReturnValue(false); + return; + } + + if (!this.forceTrigger) { + if (this.checkNoActionTime && this.mob.getNoActionTime() >= 100) { + cir.setReturnValue(false); + return; + } + int chance = Math.max(1, this.interval); + if (this.mob.getRandom().nextInt(chance) != 0) { + cir.setReturnValue(false); + return; + } + } + + cir.setReturnValue(this.antarchy$findInvertedStrollTarget()); + } + + @Inject(method = "start", at = @At("HEAD"), cancellable = true) + private void antarchy$startInvertedRandomStroll(CallbackInfo ci) { + if (!AntarchyGravityApi.isGravityInverted(this.mob)) { + return; + } + + this.mob.getNavigation().stop(); + this.mob.getMoveControl().setWantedPosition(this.wantedX, this.wantedY, this.wantedZ, this.speedModifier); + this.antarchy$invertedStrollTicks = 0; + this.forceTrigger = false; + ci.cancel(); + } + + @Inject(method = "canContinueToUse", at = @At("HEAD"), cancellable = true) + private void antarchy$continueInvertedRandomStroll(CallbackInfoReturnable cir) { + if (!AntarchyGravityApi.isGravityInverted(this.mob)) { + return; + } + + double dx = this.wantedX - this.mob.getX(); + double dy = this.wantedY - this.mob.getY(); + double dz = this.wantedZ - this.mob.getZ(); + boolean arrived = dx * dx + dy * dy + dz * dz <= 1.0D; + boolean timedOut = this.antarchy$invertedStrollTicks++ >= 200; + if (!arrived && !timedOut && !this.mob.hasControllingPassenger()) { + // RandomStrollGoal inherits Goal.tick(), so continuation is the + // declared per-tick hook available for refreshing direct movement. + this.mob.getMoveControl().setWantedPosition(this.wantedX, this.wantedY, this.wantedZ, this.speedModifier); + } + cir.setReturnValue(!this.mob.hasControllingPassenger() && !arrived && !timedOut); + } + + @Unique + private boolean antarchy$findInvertedStrollTarget() { + int baseX = Mth.floor(this.mob.getX()); + int baseY = Mth.floor(this.mob.getY() + this.mob.getBbHeight()) - 1; + int baseZ = Mth.floor(this.mob.getZ()); + + for (int attempt = 0; attempt < ANTARCHY$INVERTED_STROLL_ATTEMPTS; attempt++) { + int x = baseX + this.mob.getRandom().nextInt(ANTARCHY$INVERTED_STROLL_HORIZONTAL_RANGE * 2 + 1) - ANTARCHY$INVERTED_STROLL_HORIZONTAL_RANGE; + int y = baseY + this.mob.getRandom().nextInt(ANTARCHY$INVERTED_STROLL_VERTICAL_RANGE * 2 + 1) - ANTARCHY$INVERTED_STROLL_VERTICAL_RANGE; + int z = baseZ + this.mob.getRandom().nextInt(ANTARCHY$INVERTED_STROLL_HORIZONTAL_RANGE * 2 + 1) - ANTARCHY$INVERTED_STROLL_HORIZONTAL_RANGE; + if (this.antarchy$isValidInvertedStrollTarget(x, y, z)) { + this.wantedX = x + 0.5D; + this.wantedY = y + 1.0D - 1.0E-3D; + this.wantedZ = z + 0.5D; + return true; + } + } + + return false; + } + + @Unique + private boolean antarchy$isValidInvertedStrollTarget(int x, int y, int z) { + Level level = this.mob.level(); + BlockPos ceilingPos = new BlockPos(x, y + 1, z); + if (!level.getBlockState(ceilingPos).isFaceSturdy(level, ceilingPos, Direction.DOWN)) { + return false; + } + + EntityDimensions dimensions = this.mob.getDimensions(Pose.STANDING); + double halfWidth = dimensions.width() / 2.0D; + double topY = y + 1.0D - 1.0E-3D; + AABB bodyBox = new AABB( + x + 0.5D - halfWidth, + topY - dimensions.height(), + z + 0.5D - halfWidth, + x + 0.5D + halfWidth, + topY, + z + 0.5D + halfWidth + ).deflate(1.0E-4D); + return level.noCollision(this.mob, bodyBox); + } +} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/time/LivingEntityTimeDilationCleanupMixin.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/time/LivingEntityTimeDilationCleanupMixin.java new file mode 100644 index 00000000..d4dd7984 --- /dev/null +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/time/LivingEntityTimeDilationCleanupMixin.java @@ -0,0 +1,17 @@ +package com.craisinlord.antarchy.mixins.time; + +import com.craisinlord.antarchy.content.time.TimeDilationApi; +import net.minecraft.world.damagesource.DamageSource; +import net.minecraft.world.entity.LivingEntity; +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(LivingEntity.class) +public abstract class LivingEntityTimeDilationCleanupMixin { + @Inject(method = "die", at = @At("HEAD")) + private void antarchy$clearTimeDilationOnDeath(DamageSource damageSource, CallbackInfo ci) { + TimeDilationApi.clearRate((LivingEntity) (Object) this); + } +} diff --git a/common-1.21.1/src/main/resources/antarchy-common.mixins.json b/common-1.21.1/src/main/resources/antarchy-common.mixins.json index 89355d20..48c690eb 100644 --- a/common-1.21.1/src/main/resources/antarchy-common.mixins.json +++ b/common-1.21.1/src/main/resources/antarchy-common.mixins.json @@ -28,6 +28,9 @@ "GlowVinesUnderLeavesMixin", "GrindstoneMenuMixin", "LivingEntityJumpingAccessor", + "CommandedMobMixin", + "LivingEntityRoyalEffectsMixin", + "MobCommandedTargetMixin", "RoyalNoRespiteMixin", "MantisClawWallClimbMixin", "MilkBucketSizeResetMixin", @@ -64,12 +67,15 @@ "gravity.FireworkRocketGravityMixin", "gravity.GroundPathNavigationMixin", "gravity.LivingEntityMixin", + "gravity.MeleeAttackGoalGravityMixin", + "gravity.MoveControlGravityMixin", "gravity.MobHurtMixin", "gravity.PathNavigationAccessor", "gravity.PlayerAttackMixin", "gravity.PlayerGravityMixin", "gravity.PlayerPoseFitMixin", "gravity.PlayerSneakMixin", + "gravity.RandomStrollGoalGravityMixin", "gravity.ProjectileGravityMixin", "gravity.ServerChunkCacheAccessor", "gravity.ServerEntityAccessor", @@ -85,7 +91,8 @@ "time.LivingEntityKnockbackTimeDilationMixin", "time.CreeperTimeDilationMixin", "time.EndCrystalTimeDilationMixin", - "time.LivingEntityTimeDilationMixin", + "time.LivingEntityTimeDilationMixin", + "time.LivingEntityTimeDilationCleanupMixin", "time.LivingEntityAttackCooldownTimeDilationMixin", "time.LookControlTimeDilationMixin", "time.MeleeAttackGoalTimeDilationMixin", diff --git a/common-1.21.1/src/main/resources/assets/antarchy/animations/black_hole.animation.json b/common-1.21.1/src/main/resources/assets/antarchy/animations/black_hole.animation.json new file mode 100644 index 00000000..93a78a4e --- /dev/null +++ b/common-1.21.1/src/main/resources/assets/antarchy/animations/black_hole.animation.json @@ -0,0 +1,381 @@ +{ + "format_version": "1.8.0", + "animations": { + "animation": { + "loop": true, + "animation_length": 20.125, + "bones": { + "black_hole": { + "position": { + "0.0": { + "vector": [0, -100, 0] + }, + "19.125": { + "vector": [0, 505, 0] + } + }, + "scale": { + "0.0": { + "vector": [1, 1, 1] + }, + "19.125": { + "vector": [10, 10, 10] + }, + "19.25": { + "vector": [0, 0, 0] + } + } + }, + "blocks": { + "position": { + "0.0": { + "post": { + "vector": [0, 0, 0] + }, + "lerp_mode": "catmullrom" + }, + "0.875": { + "post": { + "vector": [-141, 71, 0] + }, + "lerp_mode": "catmullrom" + }, + "2.375": { + "post": { + "vector": [-141, 104, 0] + }, + "lerp_mode": "catmullrom" + } + }, + "scale": { + "0.0": { + "vector": [1, 1, 1] + }, + "2.4583": { + "vector": [0, 0, 0] + } + } + }, + "blocks2": { + "position": { + "0.0": { + "post": { + "vector": [0, 0, 0] + }, + "lerp_mode": "catmullrom" + }, + "0.875": { + "post": { + "vector": [116, 72, 0] + }, + "lerp_mode": "catmullrom" + }, + "2.375": { + "post": { + "vector": [116, 124, 0] + }, + "lerp_mode": "catmullrom" + } + }, + "scale": { + "0.0": { + "vector": [1, 1, 1] + }, + "2.4583": { + "vector": [0, 0, 0] + } + } + }, + "blocks3": { + "position": { + "0.0": { + "post": { + "vector": [0, 0, 0] + }, + "lerp_mode": "catmullrom" + }, + "1.4167": { + "post": { + "vector": [-69, 96, -164] + }, + "lerp_mode": "catmullrom" + } + }, + "scale": { + "0.0": { + "vector": [1, 1, 1] + }, + "2.4583": { + "vector": [0, 0, 0] + } + } + }, + "blocks4": { + "position": { + "0.0": { + "post": { + "vector": [0, 0, 0] + }, + "lerp_mode": "catmullrom" + }, + "0.9583": { + "post": { + "vector": [35, 96, 135] + }, + "lerp_mode": "catmullrom" + } + }, + "scale": { + "0.0": { + "vector": [1, 1, 1] + }, + "2.4583": { + "vector": [0, 0, 0] + } + } + }, + "blocks5": { + "position": { + "0.0": { + "post": { + "vector": [0, 0, 0] + }, + "lerp_mode": "catmullrom" + }, + "2.375": { + "post": { + "vector": [113, 118, -188] + }, + "lerp_mode": "catmullrom" + } + }, + "scale": { + "0.0": { + "vector": [1, 1, 1] + }, + "2.4583": { + "vector": [0, 0, 0] + } + } + }, + "blocks6": { + "position": { + "0.0": { + "post": { + "vector": [0, 0, 0] + }, + "lerp_mode": "catmullrom" + }, + "0.7083": { + "post": { + "vector": [-114, 64, 112] + }, + "lerp_mode": "catmullrom" + }, + "1.2083": { + "post": { + "vector": [-146, 81, 162] + }, + "lerp_mode": "catmullrom" + }, + "1.9583": { + "post": { + "vector": [-146, 94, 162] + }, + "lerp_mode": "catmullrom" + } + }, + "scale": { + "0.0": { + "vector": [1, 1, 1] + }, + "2.4583": { + "vector": [0, 0, 0] + } + } + }, + "blocks7": { + "position": { + "0.0": { + "post": { + "vector": [0, 0, 0] + }, + "lerp_mode": "catmullrom" + }, + "1.9583": { + "post": { + "vector": [187, 123, 137] + }, + "lerp_mode": "catmullrom" + } + }, + "scale": { + "0.0": { + "vector": [1, 1, 1] + }, + "2.4583": { + "vector": [0, 0, 0] + } + } + }, + "blocks8": { + "position": { + "0.0": { + "post": { + "vector": [0, 0, 0] + }, + "lerp_mode": "catmullrom" + }, + "2.375": { + "post": { + "vector": [236, 124, 0] + }, + "lerp_mode": "catmullrom" + } + }, + "scale": { + "0.0": { + "vector": [1, 1, 1] + }, + "2.4583": { + "vector": [0, 0, 0] + } + } + }, + "blocks9": { + "position": { + "0.0": { + "post": { + "vector": [0, 0, 0] + }, + "lerp_mode": "catmullrom" + }, + "1.9583": { + "post": { + "vector": [-239, 106, 0] + }, + "lerp_mode": "catmullrom" + } + }, + "scale": { + "0.0": { + "vector": [1, 1, 1] + }, + "2.4583": { + "vector": [0, 0, 0] + } + } + }, + "blocks10": { + "position": { + "0.0": { + "post": { + "vector": [0, 0, 0] + }, + "lerp_mode": "catmullrom" + }, + "0.375": { + "post": { + "vector": [-10, 65, 0] + }, + "lerp_mode": "catmullrom" + }, + "2.375": { + "post": { + "vector": [-10, 119, 0] + }, + "lerp_mode": "catmullrom" + } + }, + "scale": { + "0.0": { + "vector": [1, 1, 1] + }, + "2.4583": { + "vector": [0, 0, 0] + } + } + }, + "blocks11": { + "position": { + "0.0": { + "post": { + "vector": [0, 0, 0] + }, + "lerp_mode": "catmullrom" + }, + "2.375": { + "post": { + "vector": [-223, 106, -145] + }, + "lerp_mode": "catmullrom" + } + }, + "scale": { + "0.0": { + "vector": [1, 1, 1] + }, + "2.4583": { + "vector": [0, 0, 0] + } + } + }, + "beam": { + "scale": { + "0.0": { + "post": { + "vector": [0.2, 0.5, 0.5] + }, + "lerp_mode": "catmullrom" + }, + "0.4167": { + "post": { + "vector": [-0.2, 3.6, 0.1] + }, + "lerp_mode": "catmullrom" + }, + "1.5833": { + "post": { + "vector": [-0.1, 3.6, 0.1] + }, + "lerp_mode": "catmullrom" + }, + "13.0833": { + "post": { + "vector": [0.4, 122.1, -0.4] + }, + "lerp_mode": "catmullrom" + } + } + }, + "group": { + "position": { + "0.0": { + "vector": [0, 0, 0] + }, + "18.9167": { + "vector": [0, 0, 0] + }, + "19.125": { + "vector": [0, 445, 0] + } + }, + "scale": { + "0.0": { + "vector": [0, 0, 0] + }, + "18.9167": { + "vector": [0, 0, 0] + }, + "19.5833": { + "vector": [83.7, 1, 0] + }, + "20.125": { + "vector": [0, 0, 0] + } + } + } + } + } + } +} \ No newline at end of file diff --git a/common-1.21.1/src/main/resources/assets/antarchy/animations/fire_ball.animation.json b/common-1.21.1/src/main/resources/assets/antarchy/animations/fire_ball.animation.json new file mode 100644 index 00000000..2d171838 --- /dev/null +++ b/common-1.21.1/src/main/resources/assets/antarchy/animations/fire_ball.animation.json @@ -0,0 +1,33 @@ +{ + "format_version": "1.8.0", + "animations": { + "idle": { + "loop": true, + "animation_length": 0.25, + "bones": { + "fireball": { + "scale": { + "0.0": { + "post": { + "vector": [1, 1, 1] + }, + "lerp_mode": "catmullrom" + }, + "0.125": { + "post": { + "vector": [1, 1, 1.2] + }, + "lerp_mode": "catmullrom" + }, + "0.25": { + "post": { + "vector": [1, 1, 1] + }, + "lerp_mode": "catmullrom" + } + } + } + } + } + } +} \ No newline at end of file diff --git a/common-1.21.1/src/main/resources/assets/antarchy/animations/ice_ball.animation.json b/common-1.21.1/src/main/resources/assets/antarchy/animations/ice_ball.animation.json new file mode 100644 index 00000000..01453f7a --- /dev/null +++ b/common-1.21.1/src/main/resources/assets/antarchy/animations/ice_ball.animation.json @@ -0,0 +1,56 @@ +{ + "format_version": "1.8.0", + "animations": { + "animation": { + "loop": true, + "animation_length": 0.5, + "bones": { + "ice_ball": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "0.25": { + "vector": [180, 0, 52.5] + }, + "0.5": { + "vector": [360, 0, 0] + } + }, + "scale": { + "0.0": { + "post": { + "vector": [1, 1, 1] + }, + "lerp_mode": "catmullrom" + }, + "0.125": { + "post": { + "vector": [1, 1.1, 1] + }, + "lerp_mode": "catmullrom" + }, + "0.25": { + "post": { + "vector": [1, 1, 1] + }, + "lerp_mode": "catmullrom" + }, + "0.375": { + "post": { + "vector": [1, 1.1, 1] + }, + "lerp_mode": "catmullrom" + }, + "0.5": { + "post": { + "vector": [1, 1, 1] + }, + "lerp_mode": "catmullrom" + } + } + } + } + } + } +} \ No newline at end of file diff --git a/common-1.21.1/src/main/resources/assets/antarchy/animations/ice_spike.animation.json b/common-1.21.1/src/main/resources/assets/antarchy/animations/ice_spike.animation.json new file mode 100644 index 00000000..87bfd1db --- /dev/null +++ b/common-1.21.1/src/main/resources/assets/antarchy/animations/ice_spike.animation.json @@ -0,0 +1,363 @@ +{ + "format_version": "1.8.0", + "animations": { + "animation": { + "animation_length": 5.0833, + "bones": { + "ice_spike": { + "position": { + "0.0": { + "vector": [0, -80, 0] + }, + "0.0417": { + "vector": [-6, -66.67, 0] + }, + "0.0833": { + "vector": [0, -53.34, 0] + }, + "0.125": { + "vector": [-5, -40.01, 0] + }, + "0.1667": { + "vector": [-0.33, -26.67, 0] + }, + "0.25": { + "vector": [0, 0, 0] + }, + "0.8333": { + "vector": [0, 0, 0] + }, + "1.0": { + "vector": [0, -47, 0] + } + }, + "scale": { + "0.0": { + "vector": [1, 1, 1] + }, + "0.2083": { + "vector": [1, 1, 1] + }, + "0.25": { + "vector": [0, 0, 0] + }, + "0.3333": { + "vector": [1, 1, 1] + }, + "0.8333": { + "vector": [1, 1, 1] + }, + "1.0": { + "vector": [1, 0, 1] + } + } + }, + "ice_spike_impact": { + "scale": { + "0.0": { + "vector": [0, 0, 0] + }, + "0.0417": [0, 0, 0], + "0.2083": { + "vector": [0, 0, 0] + }, + "0.25": { + "vector": [1, 1, 1] + }, + "0.2917": { + "vector": [1, 1.3, 1] + }, + "0.3333": { + "vector": [0, 0, 0] + } + } + }, + "sparkle": { + "scale": { + "0.0": { + "vector": [0, 0, 0] + }, + "0.2917": { + "vector": [0, 0, 0] + }, + "0.4583": { + "vector": [1.2, 2.5, 1] + }, + "0.625": { + "vector": [0, 1, 1] + } + } + }, + "ice_spike_impact2": { + "scale": { + "1.0": { + "vector": [0, 0, 0] + }, + "1.0417": [0, 0, 0], + "1.2083": { + "vector": [0, 0, 0] + }, + "1.25": { + "vector": [1, 1, 1] + }, + "1.2917": { + "vector": [1, 1.3, 1] + }, + "1.3333": { + "vector": [0, 0, 0] + } + } + }, + "ice_spike2": { + "position": { + "1.0": { + "vector": [0, -80, 0] + }, + "1.0417": { + "vector": [-6, -66.67, 0] + }, + "1.0833": { + "vector": [0, -53.34, 0] + }, + "1.125": { + "vector": [-5, -40.01, 0] + }, + "1.1667": { + "vector": [-0.33, -26.67, 0] + }, + "1.25": { + "vector": [0, 0, 0] + }, + "1.8333": { + "vector": [0, 0, 0] + }, + "2.0": { + "vector": [0, -47, 0] + } + }, + "scale": { + "1.0": { + "vector": [1, 1, 1] + }, + "1.2083": { + "vector": [1, 1, 1] + }, + "1.25": { + "vector": [0, 0, 0] + }, + "1.3333": { + "vector": [1, 1, 1] + }, + "1.8333": { + "vector": [1, 1, 1] + }, + "2.0": { + "vector": [1, 0, 1] + } + } + }, + "sparkle2": { + "scale": { + "1.0": { + "vector": [0, 0, 0] + }, + "1.2917": { + "vector": [0, 0, 0] + }, + "1.4583": { + "vector": [1.2, 2.5, 1] + }, + "1.625": { + "vector": [0, 1, 1] + } + } + }, + "ice_spike_impact3": { + "scale": { + "2.0": { + "vector": [0, 0, 0] + }, + "2.0417": [0, 0, 0], + "2.2083": { + "vector": [0, 0, 0] + }, + "2.25": { + "vector": [1, 1, 1] + }, + "2.2917": { + "vector": [1, 1.3, 1] + }, + "2.3333": { + "vector": [0, 0, 0] + } + } + }, + "ice_spike3": { + "position": { + "2.0": { + "vector": [0, -80, 0] + }, + "2.0417": { + "vector": [-6, -66.67, 0] + }, + "2.0833": { + "vector": [0, -53.34, 0] + }, + "2.125": { + "vector": [-5, -40.01, 0] + }, + "2.1667": { + "vector": [-0.33, -26.67, 0] + }, + "2.25": { + "vector": [0, 0, 0] + }, + "2.8333": { + "vector": [0, 0, 0] + }, + "3.0": { + "vector": [0, -47, 0] + } + }, + "scale": { + "2.0": { + "vector": [1, 1, 1] + }, + "2.2083": { + "vector": [1, 1, 1] + }, + "2.25": { + "vector": [0, 0, 0] + }, + "2.3333": { + "vector": [1, 1, 1] + }, + "2.8333": { + "vector": [1, 1, 1] + }, + "3.0": { + "vector": [1, 0, 1] + } + } + }, + "sparkle3": { + "scale": { + "2.0": { + "vector": [0, 0, 0] + }, + "2.2917": { + "vector": [0, 0, 0] + }, + "2.4583": { + "vector": [1.2, 2.5, 1] + }, + "2.625": { + "vector": [0, 1, 1] + } + } + }, + "ice_spike_impact4": { + "scale": { + "3.0": { + "vector": [0, 0, 0] + }, + "3.0417": [0, 0, 0], + "3.2083": { + "vector": [0, 0, 0] + }, + "3.25": { + "vector": [1, 1, 1] + }, + "3.2917": { + "vector": [1, 1.3, 1] + }, + "3.3333": { + "vector": [0, 0, 0] + } + } + }, + "ice_spike4": { + "position": { + "3.0": { + "vector": [0, -80, 0] + }, + "3.0417": { + "vector": [-6, -66.67, 0] + }, + "3.0833": { + "vector": [0, -53.34, 0] + }, + "3.125": { + "vector": [-5, -40.01, 0] + }, + "3.1667": { + "vector": [-0.33, -26.67, 0] + }, + "3.25": { + "vector": [0, 0, 0] + }, + "3.8333": { + "vector": [0, 0, 0] + }, + "4.0": { + "vector": [0, -47, 0] + } + }, + "scale": { + "3.0": { + "vector": [1, 1, 1] + }, + "3.2083": { + "vector": [1, 1, 1] + }, + "3.25": { + "vector": [0, 0, 0] + }, + "3.3333": { + "vector": [1, 1, 1] + }, + "3.8333": { + "vector": [1, 1, 1] + }, + "4.0": { + "vector": [1, 0, 1] + } + } + }, + "sparkle4": { + "scale": { + "3.0": { + "vector": [0, 0, 0] + }, + "3.2917": { + "vector": [0, 0, 0] + }, + "3.4583": { + "vector": [1.2, 2.5, 1] + }, + "3.625": { + "vector": [0, 1, 1] + } + } + } + }, + "particle_effects": { + "0.0": { + "effect": "particles" + }, + "1.0": { + "effect": "particles", + "locator": "spike_2" + }, + "2.0": { + "effect": "particles", + "locator": "spike_3" + }, + "3.0": { + "effect": "particles", + "locator": "spike_4" + } + } + } + } +} \ No newline at end of file diff --git a/common-1.21.1/src/main/resources/assets/antarchy/blockstates/dimensional_tear_marker.json b/common-1.21.1/src/main/resources/assets/antarchy/blockstates/dimensional_tear_marker.json new file mode 100644 index 00000000..f17f93e2 --- /dev/null +++ b/common-1.21.1/src/main/resources/assets/antarchy/blockstates/dimensional_tear_marker.json @@ -0,0 +1,7 @@ +{ + "variants": { + "": { + "model": "minecraft:block/air" + } + } +} diff --git a/common-1.21.1/src/main/resources/assets/antarchy/blockstates/portal_gun_portal_base.json b/common-1.21.1/src/main/resources/assets/antarchy/blockstates/portal_gun_portal_base.json new file mode 100644 index 00000000..f17f93e2 --- /dev/null +++ b/common-1.21.1/src/main/resources/assets/antarchy/blockstates/portal_gun_portal_base.json @@ -0,0 +1,7 @@ +{ + "variants": { + "": { + "model": "minecraft:block/air" + } + } +} diff --git a/common-1.21.1/src/main/resources/assets/antarchy/blockstates/portal_gun_portal_master.json b/common-1.21.1/src/main/resources/assets/antarchy/blockstates/portal_gun_portal_master.json new file mode 100644 index 00000000..f17f93e2 --- /dev/null +++ b/common-1.21.1/src/main/resources/assets/antarchy/blockstates/portal_gun_portal_master.json @@ -0,0 +1,7 @@ +{ + "variants": { + "": { + "model": "minecraft:block/air" + } + } +} diff --git a/common-1.21.1/src/main/resources/assets/antarchy/blockstates/typhonite_spike.json b/common-1.21.1/src/main/resources/assets/antarchy/blockstates/typhonite_spike.json index c069e566..b22e8a99 100644 --- a/common-1.21.1/src/main/resources/assets/antarchy/blockstates/typhonite_spike.json +++ b/common-1.21.1/src/main/resources/assets/antarchy/blockstates/typhonite_spike.json @@ -1,31 +1,41 @@ { "variants": { "thickness=base,vertical_direction=down": { - "model": "antarchy:block/typhonite_spike_down_base" + "model": "antarchy:block/typhonite_spike_down_base", + "x": 180, + "uvlock": true }, "thickness=base,vertical_direction=up": { "model": "antarchy:block/typhonite_spike_up_base" }, "thickness=frustum,vertical_direction=down": { - "model": "antarchy:block/typhonite_spike_down_frustum" + "model": "antarchy:block/typhonite_spike_down_frustum", + "x": 180, + "uvlock": true }, "thickness=frustum,vertical_direction=up": { "model": "antarchy:block/typhonite_spike_up_frustum" }, "thickness=middle,vertical_direction=down": { - "model": "antarchy:block/typhonite_spike_down_middle" + "model": "antarchy:block/typhonite_spike_down_middle", + "x": 180, + "uvlock": true }, "thickness=middle,vertical_direction=up": { "model": "antarchy:block/typhonite_spike_up_middle" }, "thickness=tip,vertical_direction=down": { - "model": "antarchy:block/typhonite_spike_down_tip" + "model": "antarchy:block/typhonite_spike_down_tip", + "x": 180, + "uvlock": true }, "thickness=tip,vertical_direction=up": { "model": "antarchy:block/typhonite_spike_up_tip" }, "thickness=tip_merge,vertical_direction=down": { - "model": "antarchy:block/typhonite_spike_down_tip_merge" + "model": "antarchy:block/typhonite_spike_down_tip_merge", + "x": 180, + "uvlock": true }, "thickness=tip_merge,vertical_direction=up": { "model": "antarchy:block/typhonite_spike_up_tip_merge" diff --git a/common-1.21.1/src/main/resources/assets/antarchy/geo/black_hole.geo.json b/common-1.21.1/src/main/resources/assets/antarchy/geo/black_hole.geo.json new file mode 100644 index 00000000..7bc78ad5 --- /dev/null +++ b/common-1.21.1/src/main/resources/assets/antarchy/geo/black_hole.geo.json @@ -0,0 +1,118 @@ +{ + "format_version": "1.12.0", + "minecraft:geometry": [ + { + "description": { + "identifier": "geometry.unknown", + "texture_width": 300, + "texture_height": 300, + "visible_bounds_width": 33, + "visible_bounds_height": 16, + "visible_bounds_offset": [0, 7, 0] + }, + "bones": [ + { + "name": "blocks", + "pivot": [136, -9, 0], + "cubes": [ + {"origin": [128, -17, -8], "size": [16, 16, 16], "uv": [0, 214]} + ] + }, + { + "name": "blocks11", + "pivot": [230, -9, 133], + "cubes": [ + {"origin": [222, -17, 125], "size": [16, 16, 16], "uv": [0, 214]} + ] + }, + { + "name": "blocks10", + "pivot": [18, -9, 0], + "cubes": [ + {"origin": [10, -17, -8], "size": [16, 16, 16], "uv": [0, 214]} + ] + }, + { + "name": "blocks9", + "pivot": [245, -9, 0], + "cubes": [ + {"origin": [237, -17, -8], "size": [16, 16, 16], "uv": [0, 214]} + ] + }, + { + "name": "blocks6", + "pivot": [155, -9, -152], + "cubes": [ + {"origin": [147, -17, -160], "size": [16, 16, 16], "uv": [0, 214]} + ] + }, + { + "name": "blocks3", + "pivot": [66, -9, 175], + "cubes": [ + {"origin": [58, -17, 167], "size": [16, 16, 16], "uv": [0, 214]} + ] + }, + { + "name": "blocks2", + "pivot": [-111, -9, 0], + "cubes": [ + {"origin": [-119, -17, -8], "size": [16, 16, 16], "uv": [0, 214]} + ] + }, + { + "name": "blocks8", + "pivot": [-248, -9, 0], + "cubes": [ + {"origin": [-256, -17, -8], "size": [16, 16, 16], "uv": [0, 214]} + ] + }, + { + "name": "blocks5", + "pivot": [-111, -9, 176], + "cubes": [ + {"origin": [-119, -17, 168], "size": [16, 16, 16], "uv": [0, 214]} + ] + }, + { + "name": "blocks4", + "pivot": [-35, -9, -146], + "cubes": [ + {"origin": [-43, -17, -154], "size": [16, 16, 16], "uv": [0, 214]} + ] + }, + { + "name": "blocks7", + "pivot": [-193, -9, -146], + "cubes": [ + {"origin": [-201, -17, -154], "size": [16, 16, 16], "uv": [0, 214]} + ] + }, + { + "name": "black_hole", + "pivot": [0, 151, 0], + "cubes": [ + {"origin": [-75, 151, -75], "size": [150, 0, 150], "uv": [-150, 0]}, + {"origin": [-16, 130, -16], "size": [32, 32, 32], "uv": [0, 150]}, + {"origin": [-75, 151, -75], "size": [150, 0, 150], "pivot": [0, 151, 0], "rotation": [90, 0, 0], "uv": [-150, 0]} + ] + }, + { + "name": "beam", + "parent": "black_hole", + "pivot": [0, 147, 0], + "cubes": [ + {"origin": [-16, 123, -16], "size": [32, 48, 32], "uv": [172, 152]} + ] + }, + { + "name": "group", + "pivot": [0, 147, 0], + "cubes": [ + {"origin": [-16, 123, -16], "size": [32, 48, 32], "uv": [172, 152]} + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/common-1.21.1/src/main/resources/assets/antarchy/geo/eye_of_the_storm.geo.json b/common-1.21.1/src/main/resources/assets/antarchy/geo/eye_of_the_storm.geo.json index d448465f..0526dfce 100644 --- a/common-1.21.1/src/main/resources/assets/antarchy/geo/eye_of_the_storm.geo.json +++ b/common-1.21.1/src/main/resources/assets/antarchy/geo/eye_of_the_storm.geo.json @@ -1,5 +1,5 @@ { - "format_version": "1.21.110", + "format_version": "1.12.0", "minecraft:geometry": [ { "description": { @@ -214,4 +214,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/common-1.21.1/src/main/resources/assets/antarchy/geo/fallen_king_crown.geo.json b/common-1.21.1/src/main/resources/assets/antarchy/geo/fallen_king_crown.geo.json index c8f22646..afa9b466 100644 --- a/common-1.21.1/src/main/resources/assets/antarchy/geo/fallen_king_crown.geo.json +++ b/common-1.21.1/src/main/resources/assets/antarchy/geo/fallen_king_crown.geo.json @@ -1,5 +1,5 @@ { - "format_version": "1.21.110", + "format_version": "1.12.0", "minecraft:geometry": [ { "description": { @@ -80,4 +80,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/common-1.21.1/src/main/resources/assets/antarchy/geo/fire_ball.geo.json b/common-1.21.1/src/main/resources/assets/antarchy/geo/fire_ball.geo.json new file mode 100644 index 00000000..dc9ff1f0 --- /dev/null +++ b/common-1.21.1/src/main/resources/assets/antarchy/geo/fire_ball.geo.json @@ -0,0 +1,28 @@ +{ + "format_version": "1.12.0", + "minecraft:geometry": [ + { + "description": { + "identifier": "geometry.unknown", + "texture_width": 128, + "texture_height": 128, + "visible_bounds_width": 7, + "visible_bounds_height": 4, + "visible_bounds_offset": [0, 1, 0] + }, + "bones": [ + { + "name": "fireball", + "pivot": [0, 8, 8.1], + "cubes": [ + {"origin": [-6, -25, -8], "size": [0, 36, 52], "pivot": [0, -7, 18], "rotation": [0, 0, 90], "uv": [0, 40]}, + {"origin": [-24, -25, -8], "size": [0, 36, 52], "pivot": [0, -7, 18], "rotation": [0, 0, 90], "uv": [0, 40]}, + {"origin": [-23, -14, -8], "size": [15, 14, 13], "pivot": [0, -7, 18], "rotation": [0, 0, 90], "uv": [0, 0]}, + {"origin": [8, -10, -8], "size": [0, 36, 52], "uv": [0, 40], "mirror": true}, + {"origin": [-8, -10, -8], "size": [0, 36, 52], "uv": [0, 40]} + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/common-1.21.1/src/main/resources/assets/antarchy/geo/ice_ball.geo.json b/common-1.21.1/src/main/resources/assets/antarchy/geo/ice_ball.geo.json new file mode 100644 index 00000000..9e51b94b --- /dev/null +++ b/common-1.21.1/src/main/resources/assets/antarchy/geo/ice_ball.geo.json @@ -0,0 +1,24 @@ +{ + "format_version": "1.12.0", + "minecraft:geometry": [ + { + "description": { + "identifier": "geometry.unknown", + "texture_width": 64, + "texture_height": 64, + "visible_bounds_width": 2, + "visible_bounds_height": 2.5, + "visible_bounds_offset": [0, 0.75, 0] + }, + "bones": [ + { + "name": "ice_ball", + "pivot": [0, 8, 0], + "cubes": [ + {"origin": [-8, 0, -8], "size": [16, 16, 16], "uv": [0, 0]} + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/common-1.21.1/src/main/resources/assets/antarchy/geo/ice_spike.geo.json b/common-1.21.1/src/main/resources/assets/antarchy/geo/ice_spike.geo.json new file mode 100644 index 00000000..89c4648d --- /dev/null +++ b/common-1.21.1/src/main/resources/assets/antarchy/geo/ice_spike.geo.json @@ -0,0 +1,214 @@ +{ + "format_version": "1.12.0", + "minecraft:geometry": [ + { + "description": { + "identifier": "geometry.unknown", + "texture_width": 256, + "texture_height": 256, + "visible_bounds_width": 17, + "visible_bounds_height": 6.5, + "visible_bounds_offset": [0, 2.75, 0] + }, + "bones": [ + { + "name": "bb_main", + "pivot": [0, 0, 0], + "locators": { + "spike_2": [0, 0, -39], + "spike_3": [0, 0, -80], + "spike_4": [0, 0, -121] + } + }, + { + "name": "group", + "pivot": [0, 0, 0], + "rotation": [0, 90, 0] + }, + { + "name": "ice_spike_impact", + "parent": "group", + "pivot": [-1.4, 46.7, 0], + "cubes": [ + {"origin": [-7, 0, -7], "size": [14, 29, 14], "inflate": -0.03, "uv": [0, 108]}, + {"origin": [-6, 29, -6], "size": [12, 10, 12], "inflate": -0.03, "uv": [0, 151]}, + {"origin": [-5, 39, -5], "size": [10, 10, 10], "inflate": -0.03, "uv": [56, 108]}, + {"origin": [-3, 59, -3], "size": [6, 10, 6], "inflate": -0.03, "uv": [50, 146]}, + {"origin": [-4, 49, -4], "size": [8, 10, 8], "inflate": -0.03, "uv": [56, 128]}, + {"origin": [-2, 69, -2], "size": [4, 10, 4], "inflate": -0.03, "uv": [48, 162]}, + {"origin": [5, 39, 0], "size": [19, 16, 0], "inflate": -0.03, "uv": [74, 146]}, + {"origin": [-25, 9, 0], "size": [18, 26, 0], "inflate": -0.03, "uv": [0, 173]}, + {"origin": [-16, 40, 0], "size": [18, 17, 0], "inflate": -0.03, "uv": [0, 199]}, + {"origin": [-8, 61, 0], "size": [5, 8, 0], "inflate": -0.03, "uv": [65, 162]} + ] + }, + { + "name": "ice_spike", + "parent": "group", + "pivot": [-1.4, 46.7, 0], + "cubes": [ + {"origin": [-7, 0, -7], "size": [14, 29, 14], "uv": [0, 0]}, + {"origin": [-6, 29, -6], "size": [12, 10, 12], "uv": [0, 43]}, + {"origin": [-5, 39, -5], "size": [10, 10, 10], "uv": [56, 0]}, + {"origin": [-3, 59, -3], "size": [6, 10, 6], "uv": [50, 38]}, + {"origin": [-4, 49, -4], "size": [8, 10, 8], "uv": [56, 20]}, + {"origin": [-2, 69, -2], "size": [4, 10, 4], "uv": [48, 54]}, + {"origin": [5, 39, 0], "size": [19, 16, 0], "uv": [74, 38]}, + {"origin": [-25, 9, 0], "size": [18, 26, 0], "uv": [0, 65]}, + {"origin": [-16, 40, 0], "size": [18, 17, 0], "uv": [0, 91]}, + {"origin": [-8, 61, 0], "size": [5, 8, 0], "uv": [65, 54]} + ] + }, + { + "name": "sparkle", + "parent": "ice_spike", + "pivot": [2.5, 79.5, -3], + "cubes": [ + {"origin": [1, 77, -3], "size": [3, 5, 0], "uv": [38, 46]} + ] + }, + { + "name": "group2", + "pivot": [0, 0, -40], + "rotation": [0, 90, 0] + }, + { + "name": "ice_spike_impact2", + "parent": "group2", + "pivot": [-1.4, 46.7, -40], + "cubes": [ + {"origin": [-7, 0, -47], "size": [14, 29, 14], "inflate": -0.03, "uv": [0, 108]}, + {"origin": [-6, 29, -46], "size": [12, 10, 12], "inflate": -0.03, "uv": [0, 151]}, + {"origin": [-5, 39, -45], "size": [10, 10, 10], "inflate": -0.03, "uv": [56, 108]}, + {"origin": [-3, 59, -43], "size": [6, 10, 6], "inflate": -0.03, "uv": [50, 146]}, + {"origin": [-4, 49, -44], "size": [8, 10, 8], "inflate": -0.03, "uv": [56, 128]}, + {"origin": [-2, 69, -42], "size": [4, 10, 4], "inflate": -0.03, "uv": [48, 162]}, + {"origin": [5, 39, -40], "size": [19, 16, 0], "inflate": -0.03, "uv": [74, 146]}, + {"origin": [-25, 9, -40], "size": [18, 26, 0], "inflate": -0.03, "uv": [0, 173]}, + {"origin": [-16, 40, -40], "size": [18, 17, 0], "inflate": -0.03, "uv": [0, 199]}, + {"origin": [-8, 61, -40], "size": [5, 8, 0], "inflate": -0.03, "uv": [65, 162]} + ] + }, + { + "name": "ice_spike2", + "parent": "group2", + "pivot": [-1.4, 46.7, -40], + "cubes": [ + {"origin": [-7, 0, -47], "size": [14, 29, 14], "uv": [0, 0]}, + {"origin": [-6, 29, -46], "size": [12, 10, 12], "uv": [0, 43]}, + {"origin": [-5, 39, -45], "size": [10, 10, 10], "uv": [56, 0]}, + {"origin": [-3, 59, -43], "size": [6, 10, 6], "uv": [50, 38]}, + {"origin": [-4, 49, -44], "size": [8, 10, 8], "uv": [56, 20]}, + {"origin": [-2, 69, -42], "size": [4, 10, 4], "uv": [48, 54]}, + {"origin": [5, 39, -40], "size": [19, 16, 0], "uv": [74, 38]}, + {"origin": [-25, 9, -40], "size": [18, 26, 0], "uv": [0, 65]}, + {"origin": [-16, 40, -40], "size": [18, 17, 0], "uv": [0, 91]}, + {"origin": [-8, 61, -40], "size": [5, 8, 0], "uv": [65, 54]} + ] + }, + { + "name": "sparkle2", + "parent": "ice_spike2", + "pivot": [2.5, 79.5, -43], + "cubes": [ + {"origin": [1, 77, -43], "size": [3, 5, 0], "uv": [38, 46]} + ] + }, + { + "name": "group3", + "pivot": [0, 0, -80], + "rotation": [0, 90, 0] + }, + { + "name": "ice_spike_impact3", + "parent": "group3", + "pivot": [-1.4, 46.7, -80], + "cubes": [ + {"origin": [-7, 0, -87], "size": [14, 29, 14], "inflate": -0.03, "uv": [0, 108]}, + {"origin": [-6, 29, -86], "size": [12, 10, 12], "inflate": -0.03, "uv": [0, 151]}, + {"origin": [-5, 39, -85], "size": [10, 10, 10], "inflate": -0.03, "uv": [56, 108]}, + {"origin": [-3, 59, -83], "size": [6, 10, 6], "inflate": -0.03, "uv": [50, 146]}, + {"origin": [-4, 49, -84], "size": [8, 10, 8], "inflate": -0.03, "uv": [56, 128]}, + {"origin": [-2, 69, -82], "size": [4, 10, 4], "inflate": -0.03, "uv": [48, 162]}, + {"origin": [5, 39, -80], "size": [19, 16, 0], "inflate": -0.03, "uv": [74, 146]}, + {"origin": [-25, 9, -80], "size": [18, 26, 0], "inflate": -0.03, "uv": [0, 173]}, + {"origin": [-16, 40, -80], "size": [18, 17, 0], "inflate": -0.03, "uv": [0, 199]}, + {"origin": [-8, 61, -80], "size": [5, 8, 0], "inflate": -0.03, "uv": [65, 162]} + ] + }, + { + "name": "ice_spike3", + "parent": "group3", + "pivot": [-1.4, 46.7, -80], + "cubes": [ + {"origin": [-7, 0, -87], "size": [14, 29, 14], "uv": [0, 0]}, + {"origin": [-6, 29, -86], "size": [12, 10, 12], "uv": [0, 43]}, + {"origin": [-5, 39, -85], "size": [10, 10, 10], "uv": [56, 0]}, + {"origin": [-3, 59, -83], "size": [6, 10, 6], "uv": [50, 38]}, + {"origin": [-4, 49, -84], "size": [8, 10, 8], "uv": [56, 20]}, + {"origin": [-2, 69, -82], "size": [4, 10, 4], "uv": [48, 54]}, + {"origin": [5, 39, -80], "size": [19, 16, 0], "uv": [74, 38]}, + {"origin": [-25, 9, -80], "size": [18, 26, 0], "uv": [0, 65]}, + {"origin": [-16, 40, -80], "size": [18, 17, 0], "uv": [0, 91]}, + {"origin": [-8, 61, -80], "size": [5, 8, 0], "uv": [65, 54]} + ] + }, + { + "name": "sparkle3", + "parent": "ice_spike3", + "pivot": [2.5, 79.5, -83], + "cubes": [ + {"origin": [1, 77, -83], "size": [3, 5, 0], "uv": [38, 46]} + ] + }, + { + "name": "group4", + "pivot": [0, 0, -120], + "rotation": [0, 90, 0] + }, + { + "name": "ice_spike_impact4", + "parent": "group4", + "pivot": [-1.4, 46.7, -120], + "cubes": [ + {"origin": [-7, 0, -127], "size": [14, 29, 14], "inflate": -0.03, "uv": [0, 108]}, + {"origin": [-6, 29, -126], "size": [12, 10, 12], "inflate": -0.03, "uv": [0, 151]}, + {"origin": [-5, 39, -125], "size": [10, 10, 10], "inflate": -0.03, "uv": [56, 108]}, + {"origin": [-3, 59, -123], "size": [6, 10, 6], "inflate": -0.03, "uv": [50, 146]}, + {"origin": [-4, 49, -124], "size": [8, 10, 8], "inflate": -0.03, "uv": [56, 128]}, + {"origin": [-2, 69, -122], "size": [4, 10, 4], "inflate": -0.03, "uv": [48, 162]}, + {"origin": [5, 39, -120], "size": [19, 16, 0], "inflate": -0.03, "uv": [74, 146]}, + {"origin": [-25, 9, -120], "size": [18, 26, 0], "inflate": -0.03, "uv": [0, 173]}, + {"origin": [-16, 40, -120], "size": [18, 17, 0], "inflate": -0.03, "uv": [0, 199]}, + {"origin": [-8, 61, -120], "size": [5, 8, 0], "inflate": -0.03, "uv": [65, 162]} + ] + }, + { + "name": "ice_spike4", + "parent": "group4", + "pivot": [-1.4, 46.7, -120], + "cubes": [ + {"origin": [-7, 0, -127], "size": [14, 29, 14], "uv": [0, 0]}, + {"origin": [-6, 29, -126], "size": [12, 10, 12], "uv": [0, 43]}, + {"origin": [-5, 39, -125], "size": [10, 10, 10], "uv": [56, 0]}, + {"origin": [-3, 59, -123], "size": [6, 10, 6], "uv": [50, 38]}, + {"origin": [-4, 49, -124], "size": [8, 10, 8], "uv": [56, 20]}, + {"origin": [-2, 69, -122], "size": [4, 10, 4], "uv": [48, 54]}, + {"origin": [5, 39, -120], "size": [19, 16, 0], "uv": [74, 38]}, + {"origin": [-25, 9, -120], "size": [18, 26, 0], "uv": [0, 65]}, + {"origin": [-16, 40, -120], "size": [18, 17, 0], "uv": [0, 91]}, + {"origin": [-8, 61, -120], "size": [5, 8, 0], "uv": [65, 54]} + ] + }, + { + "name": "sparkle4", + "parent": "ice_spike4", + "pivot": [2.5, 79.5, -123], + "cubes": [ + {"origin": [1, 77, -123], "size": [3, 5, 0], "uv": [38, 46]} + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/common-1.21.1/src/main/resources/assets/antarchy/geo/krakens_grasp.geo.json b/common-1.21.1/src/main/resources/assets/antarchy/geo/krakens_grasp.geo.json index a8e4f668..b3df7394 100644 --- a/common-1.21.1/src/main/resources/assets/antarchy/geo/krakens_grasp.geo.json +++ b/common-1.21.1/src/main/resources/assets/antarchy/geo/krakens_grasp.geo.json @@ -1,5 +1,5 @@ { - "format_version": "1.21.110", + "format_version": "1.12.0", "minecraft:geometry": [ { "description": { @@ -199,4 +199,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/common-1.21.1/src/main/resources/assets/antarchy/geo/portal_gun.geo.json b/common-1.21.1/src/main/resources/assets/antarchy/geo/portal_gun.geo.json index 9219f398..0cc853e2 100644 --- a/common-1.21.1/src/main/resources/assets/antarchy/geo/portal_gun.geo.json +++ b/common-1.21.1/src/main/resources/assets/antarchy/geo/portal_gun.geo.json @@ -1,5 +1,5 @@ { - "format_version": "1.21.110", + "format_version": "1.12.0", "minecraft:geometry": [ { "description": { @@ -165,4 +165,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/common-1.21.1/src/main/resources/assets/antarchy/geo/royal_assailant_staff.geo.json b/common-1.21.1/src/main/resources/assets/antarchy/geo/royal_assailant_staff.geo.json index e7818125..78cabd84 100644 --- a/common-1.21.1/src/main/resources/assets/antarchy/geo/royal_assailant_staff.geo.json +++ b/common-1.21.1/src/main/resources/assets/antarchy/geo/royal_assailant_staff.geo.json @@ -1,5 +1,5 @@ { - "format_version": "1.21.110", + "format_version": "1.12.0", "minecraft:geometry": [ { "description": { @@ -111,4 +111,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/common-1.21.1/src/main/resources/assets/antarchy/geo/royal_guardian_sword.geo.json b/common-1.21.1/src/main/resources/assets/antarchy/geo/royal_guardian_sword.geo.json index 24e29db7..378a1c1a 100644 --- a/common-1.21.1/src/main/resources/assets/antarchy/geo/royal_guardian_sword.geo.json +++ b/common-1.21.1/src/main/resources/assets/antarchy/geo/royal_guardian_sword.geo.json @@ -1,5 +1,5 @@ { - "format_version": "1.21.110", + "format_version": "1.12.0", "minecraft:geometry": [ { "description": { @@ -222,4 +222,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/common-1.21.1/src/main/resources/assets/antarchy/geo/rpo_launcher.geo.json b/common-1.21.1/src/main/resources/assets/antarchy/geo/rpo_launcher.geo.json index 07451faf..794ca38d 100644 --- a/common-1.21.1/src/main/resources/assets/antarchy/geo/rpo_launcher.geo.json +++ b/common-1.21.1/src/main/resources/assets/antarchy/geo/rpo_launcher.geo.json @@ -1,5 +1,5 @@ { - "format_version": "1.21.110", + "format_version": "1.12.0", "minecraft:geometry": [ { "description": { @@ -284,4 +284,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/common-1.21.1/src/main/resources/assets/antarchy/geo/water_cannon.geo.json b/common-1.21.1/src/main/resources/assets/antarchy/geo/water_cannon.geo.json index 4240f801..6b844883 100644 --- a/common-1.21.1/src/main/resources/assets/antarchy/geo/water_cannon.geo.json +++ b/common-1.21.1/src/main/resources/assets/antarchy/geo/water_cannon.geo.json @@ -1,5 +1,5 @@ { - "format_version": "1.21.110", + "format_version": "1.12.0", "minecraft:geometry": [ { "description": { @@ -92,4 +92,4 @@ ] } ] -} \ No newline at end of file +} 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 4bbfff86..2c631b4e 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 @@ -751,6 +751,10 @@ "effect.antarchy.paralyzed.description": "Prevents targets from moving.", "effect.antarchy.inverted": "Inverted", "effect.antarchy.inverted.description": "Inverts the target's gravity.", + "effect.antarchy.commanded": "Commanded", + "effect.antarchy.commanded.description": "Fights for the player who commanded it.", + "effect.antarchy.dilated": "Dilated", + "effect.antarchy.dilated.description": "Slows the passage of time for the affected entity.", "effect.antarchy.stinky": "Stinky", "effect.antarchy.stinky.description": "Repels passive and neutral mobs, attracts arthropods.", "effect.antarchy.gooped": "Gooped", @@ -1072,6 +1076,22 @@ "item.minecraft.lingering_potion.effect.long_inversion": "Lingering Potion of Inversion", "item.minecraft.tipped_arrow.effect.inversion": "Arrow of Inversion", "item.minecraft.tipped_arrow.effect.long_inversion": "Arrow of Inversion", + "item.minecraft.potion.effect.command": "Potion of Command", + "item.minecraft.potion.effect.long_command": "Potion of Command", + "item.minecraft.splash_potion.effect.command": "Splash Potion of Command", + "item.minecraft.splash_potion.effect.long_command": "Splash Potion of Command", + "item.minecraft.lingering_potion.effect.command": "Lingering Potion of Command", + "item.minecraft.lingering_potion.effect.long_command": "Lingering Potion of Command", + "item.minecraft.tipped_arrow.effect.command": "Arrow of Command", + "item.minecraft.tipped_arrow.effect.long_command": "Arrow of Command", + "item.minecraft.potion.effect.time_dilation": "Potion of Time Dilation", + "item.minecraft.potion.effect.long_time_dilation": "Potion of Time Dilation", + "item.minecraft.splash_potion.effect.time_dilation": "Splash Potion of Time Dilation", + "item.minecraft.splash_potion.effect.long_time_dilation": "Splash Potion of Time Dilation", + "item.minecraft.lingering_potion.effect.time_dilation": "Lingering Potion of Time Dilation", + "item.minecraft.lingering_potion.effect.long_time_dilation": "Lingering Potion of Time Dilation", + "item.minecraft.tipped_arrow.effect.time_dilation": "Arrow of Time Dilation", + "item.minecraft.tipped_arrow.effect.long_time_dilation": "Arrow of Time Dilation", "item.minecraft.potion.effect.paralysis": "Potion of Paralysis", "item.minecraft.potion.effect.long_paralysis": "Potion of Paralysis", "item.minecraft.splash_potion.effect.paralysis": "Splash Potion of Paralysis", diff --git a/common-1.21.1/src/main/resources/assets/antarchy/models/block/nadir_hanging_sign.json b/common-1.21.1/src/main/resources/assets/antarchy/models/block/nadir_hanging_sign.json index c101c4af..cc1d471b 100644 --- a/common-1.21.1/src/main/resources/assets/antarchy/models/block/nadir_hanging_sign.json +++ b/common-1.21.1/src/main/resources/assets/antarchy/models/block/nadir_hanging_sign.json @@ -1,5 +1,5 @@ { "textures": { - "particle": "antarchy:block/nadir/nadir_hanging" + "particle": "antarchy:block/nadir/nadir_planks" } } diff --git a/common-1.21.1/src/main/resources/assets/antarchy/models/block/nadir_wall_hanging_sign.json b/common-1.21.1/src/main/resources/assets/antarchy/models/block/nadir_wall_hanging_sign.json index c101c4af..cc1d471b 100644 --- a/common-1.21.1/src/main/resources/assets/antarchy/models/block/nadir_wall_hanging_sign.json +++ b/common-1.21.1/src/main/resources/assets/antarchy/models/block/nadir_wall_hanging_sign.json @@ -1,5 +1,5 @@ { "textures": { - "particle": "antarchy:block/nadir/nadir_hanging" + "particle": "antarchy:block/nadir/nadir_planks" } } diff --git a/common-1.21.1/src/main/resources/assets/antarchy/models/block/typhonite_spike_down_base.json b/common-1.21.1/src/main/resources/assets/antarchy/models/block/typhonite_spike_down_base.json index 9bb5a57b..7254ce81 100644 --- a/common-1.21.1/src/main/resources/assets/antarchy/models/block/typhonite_spike_down_base.json +++ b/common-1.21.1/src/main/resources/assets/antarchy/models/block/typhonite_spike_down_base.json @@ -1,6 +1,5 @@ { "parent": "minecraft:block/pointed_dripstone", - "x": 180, "textures": { "cross": "antarchy:block/typhonite/typhonite_spike_base" } diff --git a/common-1.21.1/src/main/resources/assets/antarchy/models/block/typhonite_spike_down_frustum.json b/common-1.21.1/src/main/resources/assets/antarchy/models/block/typhonite_spike_down_frustum.json index aff88525..13db257a 100644 --- a/common-1.21.1/src/main/resources/assets/antarchy/models/block/typhonite_spike_down_frustum.json +++ b/common-1.21.1/src/main/resources/assets/antarchy/models/block/typhonite_spike_down_frustum.json @@ -1,6 +1,5 @@ { "parent": "minecraft:block/pointed_dripstone", - "x": 180, "textures": { "cross": "antarchy:block/typhonite/typhonite_spike_middle" } diff --git a/common-1.21.1/src/main/resources/assets/antarchy/models/block/typhonite_spike_down_middle.json b/common-1.21.1/src/main/resources/assets/antarchy/models/block/typhonite_spike_down_middle.json index aff88525..13db257a 100644 --- a/common-1.21.1/src/main/resources/assets/antarchy/models/block/typhonite_spike_down_middle.json +++ b/common-1.21.1/src/main/resources/assets/antarchy/models/block/typhonite_spike_down_middle.json @@ -1,6 +1,5 @@ { "parent": "minecraft:block/pointed_dripstone", - "x": 180, "textures": { "cross": "antarchy:block/typhonite/typhonite_spike_middle" } diff --git a/common-1.21.1/src/main/resources/assets/antarchy/models/block/typhonite_spike_down_tip.json b/common-1.21.1/src/main/resources/assets/antarchy/models/block/typhonite_spike_down_tip.json index 10c0420f..969fe71c 100644 --- a/common-1.21.1/src/main/resources/assets/antarchy/models/block/typhonite_spike_down_tip.json +++ b/common-1.21.1/src/main/resources/assets/antarchy/models/block/typhonite_spike_down_tip.json @@ -1,6 +1,5 @@ { "parent": "minecraft:block/pointed_dripstone", - "x": 180, "textures": { "cross": "antarchy:block/typhonite/typhonite_spike_tip" } diff --git a/common-1.21.1/src/main/resources/assets/antarchy/models/block/typhonite_spike_down_tip_merge.json b/common-1.21.1/src/main/resources/assets/antarchy/models/block/typhonite_spike_down_tip_merge.json index 10c0420f..969fe71c 100644 --- a/common-1.21.1/src/main/resources/assets/antarchy/models/block/typhonite_spike_down_tip_merge.json +++ b/common-1.21.1/src/main/resources/assets/antarchy/models/block/typhonite_spike_down_tip_merge.json @@ -1,6 +1,5 @@ { "parent": "minecraft:block/pointed_dripstone", - "x": 180, "textures": { "cross": "antarchy:block/typhonite/typhonite_spike_tip" } diff --git a/common-1.21.1/src/main/resources/assets/antarchy/textures/block/verdant_nyxium.png b/common-1.21.1/src/main/resources/assets/antarchy/textures/block/verdant_nyxium.png index ed655b7f99f96bfb93e91c6a7deaf3968f197038..a0c1b67f744d8dd906f34dfe229817cbf122ef83 100644 GIT binary patch delta 543 zcmV+)0^t4n1h)i`BYy$xNkl-LhTe=(n zNKUi(K)>ANSz1rCQuDo`zW%s%9X*UG+dE>NF{J^8^~DZ<|801FE{~sV1@`BQ?q54k zs+21#mmZ~#7=N7UFd@(8$ISe8yvC;-yOS{vEI4Zl7hv!vW+j)0P>Jtg^GCpg-F&R?@^Fy|KCYw<(JZSgA z*zpe`1+?X*131O35+>`mH-}|HFgT{OBO=C`y*vSl5PwsSS-L2RU^*~3v<@KvK?miW zAo!*m!Gts+$hjLhCJr$s_rM- z6#K9n-~`}{8Pf~F;3(#}NI-0|3q?TqQ$%uulL0002ovPDHLkV1m@x?$`hT delta 610 zcmV-o0-gQ01o{M!BYyw^b5ch_0Itp)=>Px%G)Y83R5*=YlRs}0K@`M)@ttmOIq%t# zE|Ftw2`7Oj6i5*jX{pJl;xo9UqE1OgmCkaPR2W$z$Vn_CeE)p6D^8)f+!C|RuAXMz zyqTxn>3{z4kYDBi(5ncJLq?+q=JOo@@>xP~9NIT`4#8Q#Xn*tofD0gR3aaTE=d?>s z!ko~8!67H%^?SwD41%+O{4(ciRsqoB$*HDmyz!EJRukyeAUQG2=R3%WsV6!MkQ0M9 zUed~E3DtCs@D;*WnDYYwzJL4$fOm2GZ~Vt*4F|wgp9<3R13F(T+9DwB<#^*I6(H%- z4)-aq3nYf(t$&9%ULxsI0m4_bd{46XNS?nd@y1I#O*LK976FlptYT9)=LbY8sxxSd zfK2Cj7q9a z5pn+qW+#U7y0D<8vBbtQKqxwO1K*R@8~3pA7L=y2wT7JOoOqMkTB2iGna*j8 zfZ^`IdRuF}uhJEZWMRprDOe;65*?H2`UAS9Dd>lN;!V1x1Q4l+;qJf&mgtyOx*|@N w0F=JMja0N1Orvxd9c`uEyaBSu_4=OVFImhn^`FU`BLDyZ07*qoM6N<$f;00ZCjbBd diff --git a/common-1.21.1/src/main/resources/assets/antarchy/textures/block/verdant_nyxium_old.png b/common-1.21.1/src/main/resources/assets/antarchy/textures/block/verdant_nyxium_old.png new file mode 100644 index 0000000000000000000000000000000000000000..15e56c68cdde25c6a5adbb29fd3f68db2b9e3da7 GIT binary patch literal 588 zcmV-S0<-;zP)Q zk{`RO{~c?v-czS@!_UF*fwWh1zulf1Qfjc46Wa!9oOk1tY~jLYY+}K6^a& z+yIauwZ+A`V(zGah=yqfj|;P3`NL*_#PmVL!9m?1fCCxr8DX!00030|2f9o(*OVf21!Ig aR09CyYExFZp$VY?0000=B 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

kA$Ewt~`&8S= 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 X(#oN(Aax3@00000NkvXXu0mjf#YmiF literal 0 HcmV?d00001 diff --git a/common-1.21.1/src/main/resources/assets/antarchy/textures/entity/king/ice_spike.png b/common-1.21.1/src/main/resources/assets/antarchy/textures/entity/king/ice_spike.png new file mode 100644 index 0000000000000000000000000000000000000000..7ea12b564e6b1339dc0bdddcf1154e5210bc7b6b GIT binary patch literal 6720 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 literal 0 HcmV?d00001 diff --git a/common-1.21.1/src/main/resources/assets/antarchy/textures/entity/queen/black_hole.png b/common-1.21.1/src/main/resources/assets/antarchy/textures/entity/queen/black_hole.png new file mode 100644 index 0000000000000000000000000000000000000000..17d72ada1c2ca94a673f18a43f4106c82928b61c GIT binary patch literal 83862 zcmZ^~cQl+|)HaL}DLRSXqQ)<3WH7o&ln4?M5xqtjC5%2sNfDy=UJ^k>CxgK#(SsR; z8J%cDbi?R;x945Y`quZZ_5C$>*=O%-@3YUj&+K!Zh^P8mv^O|zkdcwm>S#YSBqJji zBO|*Jb&U#mV*9$!hK!7XOy{Bcb3gL+GxzoKSN^k~R9ckbui4l>8NQ1SeLrOvbHyY^ zKbl>!h}M3cj7hua@8KhRvjIhsohphK@vYr|@>gzhlq%eKUY>YpAvpP)8XqGTdf#VK z^r4fTl$$9>l(|;*ovty;g{rGmY0B_}qjhEc41Vjl47urjVRe8A9y-UEu9@N+a1G7) zT*7!T%4hhZBZQ3s#A%~O3l#_*mgx*(lg}QhOuAyLM&lAj4lW(|;C!m#pibkG4cwGz zJ>6mjUA0xCT~ljIDY3O)qxo**s{AgXg-u(!(kPt+rw_VY*U`$u{^o}b`uxcK@LM;} z^;84fJa4K%+I#M+=z;sCxNs*??SM!&lf=*z=bDJHTi1^|UaFE52IaKJ$PrkJp;R@R z^$>Dr@;&d56QJ`c2F3H!=R{$S6klPcrGt4cCgu5N8~`s%zgaU4?>St=0(FfUg_hfl zD(v+TXSb6h|E!MYk)rlGTI1RCjLDESFL*NEm>?AWH;pJ1txMVl8}ZiGExv}-d`mXv zVTKJoo<3t{OnUcAtLQX@*`Qj4PQZaY*;bxF=vSNl4O0A}cyMQQ^BZxhWov7f=%})x z;y~xGHnbkgxYa%O;PP7!&N2&gR7q&nGadRs*(V76%sRV4RF1|noCrRCkF{ihc5fk7 zc*iS`8?X8^zAhj^OIBUe{fH>&w>+9>2ja8)wT=APRg1rEBBJ_Aj+(JMeQ&DKF~%~+ z8WuqT%GjcTMvMWv#iTuBSlxx4k%i1`%c)c}?>Tuu5UE3p*UFIVTm5HQsC=wrZ8Zs* zFz*=7J4UX7p@Yfx|&$faj3`FJGuofjLs=noJclPpct$bPSxcvUZkSM z+fOrpUzHcP`2kO+|r&fIQSm%XFRINlHx?HLEpz)r+_b=^C)r1s&2@ZJ&kQY0%> z2xs`y4rwxv{>i$bS7rIr##?#p+UI}+tKc~q9*-|jreFN8fKP`Ju;%RY6YuWnBHYHs`}v{dC+^3 zQ7xw_>loG5RPwP#7e}(CrMC6xZ=J}mUBLhIpVY;sHgD}1AnN^uKDzYmSlu6-D?G9Z zl1|AP?|inpZh7Dg6)kW)m67Ph+R{cE^j%XyhCs6TyGU$}q#?$CY$ggr|14h=A(!g# z{`FbLFZGTP&bK-gUZY1M9bHZ2ZSO!RuTvSssU{}Evjs5w17B4B&Hp#>qP#ZF%;H|5 z!EC%~w#-a?KXG=P12_0dgXZC*{)O|}GLpDj5ZdvLxvBjPtL$KI>+(RyH?Hq#2TpQ4xvZ1@1CMGz zuW4Kr}oOV~3@A?`Pl5Oy~Pf)D{OQMj%*T(bYy z-j()u_xF{A?vB54b^r0J@5q7VdS4kzcIw6{1+g1>Br7+c-E{qJ1v(ibWx*Lg5Q^Hs z?!0!FdBG`~u4*Kc`!xl+xuXy%Cnj+a(-kuiM62{S$P!ljA{%u>V8Ve`iT>1-L9L4? zPn|6;R^#aBbbHTq2y@eQDY?q4E(2`%p2AjhpCUgqUe0FC)23KjtCHrhlQFgu#RePN zvmXR*(!)ir$J3x4KHt}oyT#Ps>|>QRSYO^}8Z5y3@@D3(WuX(Li_9kavPFA~YF{=$ zBb1%5bTk?wZ+QnsA5TOF6nGCP7ZBH@-AvfnZ*0|z*ndZZF=xN~nio#5SWjdY{gxFpPBi< z)ZC0>LVk&}o27q@xJ>O6QU-0NO?ShuxO!Y&Q821e#bL&X#d4-GV`;-q&txlw9bpdp zVYd{0pp?mJ!QF)5gVVLPqQq#S>DzD(>Y_ES=wDKc7SNn3UzwDdp-%P9^+6Z}E?mb| zHMA%mTg75mR=z_T-=4eA<EnD90rF#YR%L)qalzj61*#(fLUIt z4JkLb<9%{wY-QJ9RI6HmF66`|#b}x#C}KBLgEzIX5Z>T=nC)Wf`wdyT5gWrBhS zj^gSFn8iitv6frB9nyUz-wX=78<5Xw{I5=j@HWSwU38tvWzb|=;Lz@ z9Rql`UF+_al4J6f68uR_^;`=&q(*X}OjJ%79FL6EmEXefw2NnQP!s z?8UF6RCO0`_oxUq{K0(#kxhfPwY_u#leoy1o#H>t(SSYOB!td$cg5W~`!jcSl07`ONW>L3 zd|_@Pelv51V_~qa8|GoLMqNb?IUBM3@2WiSQqSYcCj0)?g3W__s@UrBM^US};^a_2qh{#S8K=pBPHU6d(Qz=4&WOFHGLQ3;A@7x1ADqSO9s?m7Lh6fN5L zmEgU;;B8sWIYO}-lOO2V!fzhI+ftHH?duEss?+oyX|sMS9$U`?4aN|6$~=@8-4>FE za$}(_a-&nTe_^Hev%4wM|NP<2RHsTgJ8Pncdn6YX2lup6o1OY*`n7*!Ico20<|OEY zQa^%2$@|HVqj-wdPR`^Fgc8OF&QKXyN)r-g8+4p{a^-q6jvroXY(V}T*lmn|C%BF& z{aeHpV$=QQ#y4saxCs|5deUj54kPH=_6xLm;uG1AK+bh#d;~WI1l}6&rGr)?n zq>bx75_0+zmG$39p%yXldA?hdRAx2HcuK=ilEMn3d217kXyZ<)P+so>2TF*R7+#c% z-f@LIVoz-OH;e^*{{hPBE zv-L;o;vpdq?^o)#iQJEH*Iv!&_hQp4^x&bI8++Q&23%viarUyNmQwrzj%37}JrGYx z!`gNz)m>tObk9;YuO)R4W8+PE;p+=?-dwfTN&IUZdVBwTFAW$ zlFD$yLp&MMf(A@}ds_)K`+T*qf;F?`^Q@VR6P$Bp?4-@w=WUdxS``R<9c+W39C;j% z&@a8%IdU_6RG@22K;ZoC8bzJ^xqj>Hahy+HFCd}#nd>fkzc`;5a|{(!lDXem-Sl*~ zEt+GHAfLjeX<@C8J8q6mX!e0L6OR}n?rN`54ydsHxZgUHy3vbD+8h4q)2j}$2S`Wn z-Zv*fKQn3-9QRI)u)rGOyh{GvA5lN$87QtM_i4-jP)PN4r!C2|A;DLM>{FM>REj@8;?KY9)kWD()F~tA>3jaVtZYlKo(GJz_JMDivtHB zyep~Kxpe4Fh^usI_*T}8TYQAmEacl*a%^-iTw=?q$g$&ErS3WzOwEOySS;ODK1zWk z|DHT@Vu*Z1YZ#dYIyW~@B|UC9pIY({0z0+;wu1Q=bzQ$E^X=L`ON+-sI(>C{RiveO zxmDxV^9N)*+=a6t+;3^E)sve$S7ckh?v8nrm>HRz~yb#Hq}sdN6G;W|>9fPjB=hWzU9hFNC^h zNMgF+zzE3&RVnXo_81Q9wDZdgMQZ(4Z&BTE67C_)LQrRanu|AuQEI8mx_&pv(wcKK z#a=!=FOn^UD>xb3@^!!ZY{yHzOg71)xD1H{@zu?~j1l)lu9@p$e8*R6a(_6L1b3;_ zI(q?`2TqmvVi#&hxub6nzRrLhE!U}(m^Pm`vw5rXLc2%VP-o!)teWji>)?VIK9k5+o>45#irZ;kWsPk&Cb+$5{jk4901 zP+O@L*Cgm+$+FrJzda?43w29F%Lp8(cEUZP-^<4=-p)9Pgfj;7rN*TF$I*xOHtoR* z^gK9R@mYyu7^bBY`e$5=sdP&SmD{j~6gWGlq4^u69d**Oj=7%N=u63cQC1fHofRtW ztLN%Uz#PO&;wq1oZhWQ5ihRgV^qV_A+p6Awo-6j+{YO>O;7%f3yY0!o{y3^#ElNFV zh`UVjLcEg{)1`2+kDP-F?sJq#mne26%}i7aa~glXDR6)DHlfZf{)DV*hUw0=Q!=xF zgPhsNVq{h0KXQ$k57o=Qo%R49Q)r*;pGv+PwgPePSIUBz+&{Lk%%YHKUB@=l{1qrU z*B>@ZS!!;%g8jOF$GN9H*UejetaMy!(T4Bv$5N9E_J=n>1!+mP{+)v#C^m&YsYj0| z(1QtP!%np}(+%Eh(RG;bLM6+IzbH=if(f~!8Lp;VovJ&!b-^2MF{)d`KNmnej$G`H zpeOgeJ3Plc8}{lmCMt;|T$SF?1wq=#Ss|$?Z-(l5Nk80#*p)5{XlOYS@`)!m4mOrN zu*CM}c34VZ%B1L)E;xQzjvcHmJ`u3;1`+D+-q_t z784wtf@|OAdN@qcB8X#SwCK9!slmDe=YBjt_=fXLh<^Fi#l@`Rwz;rTZx6gm?3#w! z=cg_YUH%$LH~X(l+<3ckB#@)Xg!(6J&YpkNEAQd+&6vNKhQTs_h;Mx2=4Ui`ay#o=yeojwWp^X*#o3x`PJ-uRjNu z3h^W~F3etBoX%GqD{IT8DV4_)i2+?H(tB%)D&pC9rckU=f8Cz+r_4((2bI#B$sEgd zniAn<{a1nYWL^=evH8YvuFMH4{?>(iEdSray3}}sWAtJ5Emac6zck&%sG}F3lUu_Y z|6}#}#tF>g#8M7(RZfg%NkYFDZO6*W%Dbd=NY8s789d%P(Vm#hk6SxNT|8c2GRxXR zhnRlu&8gos0yWnL4!m+HRbkij`RhD}uiYpLAOn`#a4_3wWj&t?U-^&@Ev6rJf`b;(Zh=;6fMu)`UQ#qq~jo#-be?FOI<>(hoRA>C;Kwj(MkUg4O zD5bqS>H(uuzt*!M5ZggIOKch&Q@A5l>8*+jQssJwoUPhBONACrmHKgluczVj3+p2TGAU5rwkr8>r3T+=!Fqt$bbgjP@g=O4Yb&#a@LBF2>SSsb0p?He3 z-#WU0SaZKLgDw-s=AYZL)>-tZw@rN0qQz%+!~G@v;+wm)Y!|9CuTzc=VU%n7g{ru7 z!~%lCm#vapw)m*%w*NpY{TpRq#Fp0a1j7c3@@@?tPTGHo4CN??+Ty&iDI~L!-d;{) zSI7O9J~GkPs|j>l_@J46D8ovENg(+34F6;IljKyyr}Lg)zt!({=G|;e&PAN0KIl0s z_f2xXgX2M7?=P&dNG#}@u#8j4Ow^mH^kn@jScwp5)m&WXG|}L)wtTQz;-)wE*att> zca#v&$7!{}-u6|%z-!*}c0K!)lUs_wTHG+Th%2JHURKeP?n~a@ z3mgWilCTYS7rVu9W!W_;=*Gf+jC}0W!%Za-Aq?8mmF6rRw>-iGzOwGxy!@n#ZOJ2h zSX~zU+TFy0ULUfF?KtB-{%M;y!Q`-mOu-=;2dGt-ujGOK1lQ3$d{1=be`Y+21!g9w{-d-6+&kZ z=ezsmW5ssCxBt)<@Qr(e51v3hC-FC6Yy~M~#AfZ$w~ujnx72fc+LCS9XrgZiwvmT$ z5PVcnahz8M6D(5ES%0#Ye<3J&mQ>J3f3cCL>$`PgSHCU2T{X_(h|1UoFG5RVgPT*% zlqT-%hkc(!=9JoaTYS*S)2;Y|j1wZsQ;` zO(L0gkpDWr-<(821dDn%MwC&e+YMmw(*8nL2Guhad_YxLvS*0I#T@d)(n)*uAv-0# zPb$W)iXlSiRFgDz~2$+^1s>U!bMQ8d^+$$@kjp7uKT^(1BG5G31zrS z!x#bSMjdB_g2zyEpWc~BGN~;z#R^YiIgkl(9(VA9j~rNQ?OVa zl1P@tETSBriT4ij_66f_xAYb&-n~@t@&*If} zKd#6<|IMmbSMv4=HkS8nm$zx4B36m_*GIxsH~+55XPc3MGN@-n7Uk=9LTRr{*1X>9 z5@&ODr~R<~{Y-37EzZ5U0rxKeZOErX26EJ<1FaQ53(#GaXyy({h#?)zkQL=!^!{;r zBi6WCeQNg*znd8EeM4h*xTx@W#Q9&eQI9dOEYv@&C_-E#)j}yAKk(bW&i2=pl#Oq? zJ+a8F%KvXHphxb8Vk5q)LuWDB+lnWwV`P?*7>UHHFI%dDd5@{Abep>jeSh+sHQDS; z77rbbq0T14f5zA3yul&)D)Wqk|5M(iv>?qJsJmtBOFqfgxn*1|Wh5ITsOyn9&!Epw zkQQEf$Y92>1gk(X)1bQXaib~GV?=P8setb2y=gh@&rFQ&tT#ReCs3l&+6&CH#fpP?})7jH~7yDDWgj_z^ zxoX_#3%$~c91dZ2*vnGx5^>IrN42#>i&4cKnmG@0XW|sL9A40_?PG)6MQG2Z?zL>n z7(h-XFV?AJ3tZILrf01c;{6iwQIuRGD|ACgp{y;t0(`zH6jFJ__p19H8^I64C?E?y zH43b_#Fpc8Eh}3!>_XGUaqK_Vab=-Sh*CLDhby<^Uf(U(+w8`KEhQJ_5MM}tioF=;MHDd}=Bix${e-1Q^f1h-^IdzM zbOJu1l01Ov-q4hk92f$7ow+d|6uFFx)Ned{dNZc%EIbP#`KL)0^!8e6abnN-0bE+M zHZ`(%Bayj<^cg>O&i!b&W6?sOAuLP6m(x?BXXuM&8r(!#+R$$O8EnL|Tp~Wdjs@eM zz4=eVaqM8F;fNc17EGKz|A>u@bTr`Os#b04Qhh-6%g0lPOx=?R`68r?Kg>FwZeu#z zQrUZ#I&~{Y1a)G$x6tyo&ESO+=HKxU8AwIAKjOu|SJJgC@V+a-g?DpZW^*H8o1kYo zrbf&m$|bj!ebx1{JsP_(4}jv>{gZaQ6tQ&PKOcHgI=rUk*na#!TZQh@<>Lz_T-G@h zu~fxq33kf!SgZ5*?ka0%zI7fRJBP@~$k=Q`FE8a)LAm#pvMYa0JbOioT{|8QOefwY ztI4lYrt*}IlqNyuV0NWiJ2_d(&SZ-ucl;nK1#?oj_L(Cehgq9Z{q7p4sO?VXp-M`q zk2x+?1n;)nlS{jxOltVx<->tync24RO7(n0?~>sF^ITe6pI^c5d5JRqWdX8 z%##F*h;=&cKfBq{H>6?p-LVz6jM`&~gDbamE3r13mqKK&IIkxX`Mi0N4+h94yA7<@ zqy><4{Zz1kcV(`7==Z4G1yDy9FwNC&_(+#cEuI@qE{CVPIz9VyJa1pE&3v9K z*iJb(?<%4_#pqC~60RPIEjbn&Jhewr)_frFrpY{9JJkN`hq9{KI=Ei}Kp;RmDWEN} zG$vVWugqxD;S4a_Er))idy9;VT;WGO6T8XM$$xsjDw`Pzkj*N&1pkhQtNngm?r0KQ zCGE}fs>&wagA>B=Y$CD&R&GRzSal3O+-xyaSlrBLQTmO4!_+$INUQzt<{+}ek#3%Y zUG@$i=w3+HGd4^JVersl&lA{Ev0HoY5^n<<^Vh^J-XJnLdzO2tBom7#?u@5FD@N`D zTOw1OpJ^jCET|e3_+?Yx(MMgubRAMP^Jt})jhEN2|NUyR&b)5Oxs8Npvoi=+&fm(^1dCm;scxNw7w?NdWvNdP>W3KYUKK8tekPS0w_ zl;@0d>moF!S?=&CPVN>b%k*HJ3BY8~hhqm53q-_>4CvbTunYyM&{Qs-Eu z6OEG5iZ7Cv9%rBv(~l}3TAUQusC1_ldD%8s>#yFvyQFdg4dEg8^fhx~gDLdX(B9>V zg3{H+n9m*F{4ewydD*d+nA$30{e1$4=U|&XDbS8L$qP3naG)ltHLi=6P9=&?BOQE# zqaK-O^4L||EYT$`uKQazUD>0xwmK_7d{|KNC5>*`HG-#`n@OWq6SK{zv6sR6!E>_w z-n?@6_!|v)h!&alf#d`hKfN{IolRvq8Ys#2U$MQ*k3glgsMFi-# zuYb(~d$tDNp^__|h=lHkDLjRh3$K6`z`g?oGD+Qaf%xp;dBhua>=HJ~XF!gKtl0Q6 zMM67%57KkPt>R zkkZWkibbl>E!E@BP0;A2?>nx_Lc58HBAhIr7Rb#eAFb@Q4Z;wccUB82VrY^uWmR>u zFQJE16WQWjA?8=&OOnL?^`m%xCui5p*t$nS6J=g=vSf1Iubg4V(cBBY_fS7xAX1=O zr3djRd{+B-%tcR*po=*uKhVOsZeVZA_#Gt>!7yJ5)iSJ9%H|!}QolI0N3U}C$B2l{ z$E!?lG@OIo*v~=12Igk1E_)3blI$lO$mebp1~e;JFHW9!b4dk)*C8g2b2D?4$E4g8qf(JJ7P%dn+J>_p=WF z#3sequicA_d?r!2*6f3xJi!H8omtYZAzF@dbT69WwBqOLs670b(<_mED4Qs|N(ZHZ z$FEl*&6P@^xAgM^(#Fy$g}Dlu3F+r4TG)t+ebd}yQj#UGwqtze9xgDXSpwC4$mw4+ z;QDY`J&x8AGnH!-!?WA$cWT6|&$F7zry~%34c5FExD_C4-i}&5LiG+xr}fABbZL@Y z+ytu8HpLuU)0ZW+TV(I13TiqL#J;W-kj{;-N=W_H0(+pO*48TjtzN&^urBYaAv}ds zw$Q6qjPoBp&T_cXM%`sL^7tRDJ7rMB8ZvEO-LA1o0~hLW=YU6P?1>(m&v$Pn%VRnV zy}Bk}&uFopmEG?8vUY~pADGDt>-;-G4c)Iqu`VXah+uC~29B|F#_xyT+Rj<9ZcYdG zkks+@NXAzWB{Weo5dWWzMaa%CeMP3|r0h0YYCCh9G%dcX&w`S`*E!>Nm02hppJ1`^ zo#1K?uG@4Ph^VY%WNtew>ejswOUhk=ck&@gTRrN|6~@aP#vAb^0ZRPyKT-rOXyW!( zq$rjdA(v}GFJ8uKf_Yc zfXa5YK>rRf5jX(Z;8GzgTv*{@Ni|Zrymat+Uz9rJYv14BomZI`{nS-%-7@O({2{k7 z9m9cp$5>10cwfv9-SU_iN@Fs`CT$}CvkAIvNT@#!6IDGckxaIYC-3~Gjh)YQHla4m z(G6O(ZYx^(!9xR7{}mDvi@bz5u^}8dKRWm;zBYjX_PAYVnvVJ^%MEE|iJ8P79JrjX zqE;QP81|>_6aFDfIc#IhAZHW!=fXfnkfNg+ZHYkZ1PjPEj~lQ$5%JNIpQ=OcDj>m4 z>10L|9_qvPwB1>RC7^eLX61YtP~xoeFdz>gM0JI5q)2~x28jHBp0;|?_KA^r-LqqX z%HYAi2JoJP%Jhv1cq=IeXk|P1Ev!J00i?`U??D_l)6%5>@%q_|_Kc4>G-q{M%|c&}>aAFAPby)$oGGmzaF!S$gnEdqxj$IwQ!|x*KS`Az$yQN3$Lch@&4p zKMxP(mH}Wc%*n20@CZ{;U7CuM=Vk_^{Q}M=i$4iC;S72WV2_%m8Et?*lS=pR{5D1FeAJ zrSU;?n$qqNw{mNsSKkR1hXXQHp!=m}grd?2;{K=eD&d1E#GBov(^|GX8`rhYfHDBX zCkJau+OWh-L}R>xr+6@E!0yWbbWVO;ZNs2zw+IDqky=1;dA za^>xM;F?>Gv};b|5(D@&@bL*f3#i)xpuW{tr4nrO5LCaYM=L3An*v@BN&sLtx-=yL zuzWyg-nuqqc$r<72_+}HU?og#YwdU$Qn5TwEt>UoK%cVK%jSsCY@k<>H6g#V^Y6W- zX&J4IVRZBtrSfS5Vj}VDjz~wVG?1VQnpsN)Y-3jd8+Gm9F963YuK;nXWe`GNCg>!f zc)C#i;>j{_ExXnJsAmxJQ%_MVsY+xvig42#FnT`JjrKzG>tqN2#qeCfeD z8}4Xc;Aw}N2GA0GlScUwpz$7H$sy?H|NA%!7-k3Ip}5P@BF-NW3k3ERps{-7&E*Kd z0r9rT;ssL?7D~?9h(6gCV6TQIb}HFj1`eEN&JRZ*LT|k;5#*5XTG6;%CLrw;D{s@i<=30M%V$++0iNlG>{nj@)1xf4rI}&r`Mh=@InHJ zyLSYgxUjuiVMpsCU_wv7nC3EnSuaz{al!7bO7p*1X?McaEXl@ z2&}h~jxHcXbs0;(YXR>7h2DVB=#HScv=l9<`rc>2uV;I!mB0&KV2~jNCmSvsJOhNH zzZ!f37@8&tp{_u&JblRGGJm^){Ds{fdJp7p58zg^ou((?VTK(rOi*zv>b`7)!JVxy zsNR^cI_|`K_*kg|f@2hbo8nDI?l(bziY$QeNeTRx4yf&)Ih%|=*SX9q{2+B{tFias2fPz5*6Gq5J+nW0H`rtNI5I;!N~z^ z2b6yk1#;jwV5E$T%wkp;6!0PwxIwbBq8|fJQUY488wxT3;+Kg%5fy0(?DVL;3w_05 zJkJvW^b-RpA8?pM?+eZ4sJ{uw>q?iwffsB80LC{r=-t#dBSWuT_EemU1<<%v9#GV^ zQ}hS$U+i*feP_S+pLC@w6)qUffjgESqy`#)2jW$daHlu|(5V9GTk4G@ZaRu*|_7?>_`CRE;*H0BO!3F$ycG9paUZvoy10G100 z&sl&3O^X0DrH3cK0y?$OF|RWWj86TZ)ujX=KkwZQ6`->_yr$_Us1caap{zDj z<;(#nwiO`w#=N!PfXS!z$7SFiw@6&ZlO7O9j3tx<$lBS{=Du;@Cz;O0a*_yhEVP0aRJI9 zuLUpdK!C>?)Db*_Ku288`&=>!my-<_NYTY)z@zjk5Hl%8K%ALTR0fD~|K;lBlxvL& z&?gKe8I+&j;q@&luhlldkg5D!EMm(b_@3=wk(Gwf%Y_Tio5$V%=hIo=+^y{vr|^mE z8%fYCm)sU)D5*|o2nTj`0gZcEmetz#-s}GAr%;X?Pw}&%3cvCl#iIN)|8BMo&0A+= z*#0r0StPqo(EEJI7up@vB&seh1-Ci>`SMQ%4oGy5{?%jCcRXsL_N{>)EoIRz;^ zQ=;0D76*0RUHF-D;2@5BV=lS0F;9D!@J7G+CZo};-MP|vF+Ke~*DLn<>=DpJ=sH$y zaTq5{LcXc09Xn8-{Sk!8%?i|4Ww|8z`fOlz9!A!}J|-rn1`f_RPtoWDzsR<2wVby)HqGvsvs#@d zbtf|u|^Izb~vk~onHD_$!SJ#%hQHTmVZr27GhD>QM;qG(% z-&#QSfkdg=F2UNMO3>zhdY2Phh|&93{hc7x>KpSuyG->UZO*J+&1 zHRwlHd{(Wy2MZcg5yVuF8@ZJrXO}NmoxSRQEI>!`l@%K(gxNQn0i9Bvhr)d5ToX<>+oAq}8;L6DP`TK8&kqT32-Qp4Rfz^2#0x;=CL< zn*p!%^u5MS;IsI`it)p8!!oVuFKdjnB^Sx31;gRz#uG~Fw#^H~7WiMQgDzI|X**+( z6>&p`VWDpV{zyNL)3SESztjq+dkm1`e~HFYKytZ zmUM8H#%lo>eWYrB>IrHoZYgQD#@`9+Whg*Ub9N6et(ApB5j~vi{bICtea0lT_-|_{ zHbV9RR>Ig%{2TS!Azs;ZAr82T|Er?y(4w5vy+7EQLs@#s8kw$Xa(w08EsXJmftm(&7GW)u~{^_9dcw{)KTUYCcss~XVO&2XhI4zMz(U352( zb%0$r@V)!@Z0PnMApXHUhhKRwK2qya1?SAc50#gJfA97oGEpHPK*9TuBroi6ArI}N z_=@wz$}21yN%{;+9s}9Wj7_^gM6L~wmu)9O^$cL6qq&D3nW>bQ;08GXy~h!LN#%N_)IURKn6NbDJ4 zA>M4bA?B@FeIFzwf#4F8b-L>$3Q?0FbH7`Lrgfr5HUzX!Xnxl(I&|_qhn$J{DfaeU zYYrlgV=0!m5QiTt9K>$3P(WMR-BZ1-I#fS$;}ILkQeH&lTC=;{X2g|k!otMX_>`@I z1X*IHMIU!!_tZU9R|!q&M5XD<*h^8ANQ703>hY3l{XtzV)|nT+OFtXTvw6;!9(rBO z!*xTeg0R#-4oaEp2@ClJrh&8+$SjL2r1`c(rru{|59h=}0$L{{Ak47;ma|kRe+6!2 zSd`feEFJz2shHP#K_gR_i;3nmQsiPAkNWuGekGNPQ#xpoYR@%?<9~K*`R2H;EPB^Z zR)BdiYKu(ef4Mf*dp)CW6GE~9!WZp&qT&>-iPoa0&3(tSAQ;{N)X$=hw&1s$K#uLLHB9N zy5DXXFjLPt1nG-GKuuH21vM(40^P>PlcJRSUTMxb^wSn|RHDnP3Q5k+^@XfnXrWDm znacSq9u^C4l>Yc;pNLyfOOZ~q_6_+WVA|+VRRdx{?%7}ZLXu!+47q&RT2iNZQ_#_= zo_v97T|c2b_XG|D&v|!YQok=9b^$40UV~FG`SUklpmf8iQ_~)NSu9>kO=LG10ykg+VPrGkO8*T~OfZW950f!Fjy z@8o)NgP8HfjoK7(6P)}DiGDq(2^p};=tRN{H`Vg8*Y7O8mu=apgbPl@=e5&b8^sY> zHlK#s;&BcZZWa->T#->95=Ul%+9;*U%(LT%VnWN4st1Zg&j|_#I=Q=|LTgQP);C0B z4DY?^^}t%v6|kjx<|i4U8Xu!FiK31rf?H;hBMNyWU7zmt0bHfuO*+u>O9sl7P`+~0 z<#&4>Cn`a^RwV-_qH~4qe&_q-=cl6P|e<|MizF6nX)USUcNT_)0m)D+AMR`t9HhV$X3f?2B-ZW|d zs9HxrdDu*)Rm3YMo($QxuGlE3gbC|LBX;?{&&32p+Wa%lpT$p#EbzO2vE*1w^*&qO z$%vFK-NB-rZ?j&z7_Cn-$5%}IQoIzWPwb!^n0DPgbo9GJ=N8ll4_^2a;(z0RwngWc z0wXKEW0)tcg3Qk~v7v;p)Q6jK>~ma|wI)(mS?)%1-=mj|^`F}h>+gLQr?|F7LT&9U zi&!=n94&JivyQ98J*!J1dserV@d5EC_=xO?vH(PIo#>$KScf zevM{nsexDzs%}0)ZfB2e%CG>gBWtXPR1}x&S-V#&y6M)|CBbRD$=r2KWTh1ci=N%{ zJs-aw*JI)Plz|N0t-4)!H`qhr<&$Qzjm!_x1;3VZlz#=v(z$fJ#NL8xd^Q6X-{ggv zoDtGhRFLfA#YKrb<7+=chsRS6GN2}cSvJ=^RGt!Xz|LaevF61!&y~Bmt zsdGYD2eul@;*;X%icetf%Uku25X9-&(&ia z*_}!^h+kj>{izwUeIpG%;;_?mg9j_Ik}MNn4Wu_AtBS)qMZSpiF!gD+bZ7CAEmK|e z+E!6u=`EzBlfBm-|HVF2q!Utuo)3oc_wOaeil*t7J4ggtr_J zfj#-}#_wQ(ZC}%`{^`r9!07+Re?0AH0FQX}`_ly6PQ1&zEL(IR zplui_FuM-yL@;l$HIyHH&_@iX-iWHJK2nPGxNador(+#@x+>l`KgxV;{Ls(3^)RWqW!`zTIi z@EZK`K+>;eln1p{^vUT6Kg)BmdM^3|D+fMLRunp5KIfZ3|7NzSk8$EjF~{Rk(;vdA z`^&qodf?JNgT9JGM~C1Q>XrkEIQ+zHjAZH5R=@^!ZB==9%3r^zrC2#~geEU+0&Z~- znc-~08*qG=H^P9FOBqMgM7MadKSQ|57Cy_gJ;}-s)c{Utz}a9bg^O>mezK--#ve04 zKblhb%-}P64Z%)uXp|mU?AkV^KZSDDihqaNAsUT=b+S<3FPH1pTFTF_@Nj+YIW>OH z(`^`iII#;RK&x))0|T1@R6wlb9)K%VnEKtj zl4c^uCHqZud%u+I19GX zA3mCrVh+m#j+3(eNaAA`Idv924GUJp3c_q^j{_473UZJX>cfd z_Jyx4Nw&^~TB4OprGu@D4eLQLmn{}oD|tRfUO7{ujzhTIXLh68!e0Wt=*Dq|t^RP$o7Yg2oB8QKPaPP$8X>Ebu^;wQymUJM!sxXE>A;U1P`Jc zJ#v)Zo}J&l@Y;UWG!dItFI}Ze-;Nz_-q?xEm#zEtbLg<(Zfc7%MW8hc*+UF zbdPn7<<|&%BGtwHRbAaq%Z`?(rU5t?#Kr$PeZ%m({Wg=-Rbj{GD;*!o{bgEoL5~|= zQtx>E5bsHtMz6E8%Fd?}qP!`y-rw0WrH}AP9e^AlXrGydoWvQy`$H>qaM1kXuq*5g zl!vO6*4dzrWzv&ooCxsbLEk=yxxd_a3LoJ!Y5z2w zJpdpX8|L-Vz%mD~;mYJ@f@k-w9yQBqk9~2kG-$|BmHcThO}Af!gn_fz4_G2^t3>sN z-ac5uJY^mU+c?RPPUmfO#;lb~yEoV*U;FEx2MTg*zuJ*|+sRqQ<22oPne>N=bD^=} zMn$JV_e#6@n#3$@iXAVD*>8?mx0Kc>Pb9vS#ZWkZF#Ynn_0`W_-T}GN=iRX%RDpAe zEzLTlt11dF-+~6XcgKe0qrgnia6#=+j@^{O!1i}y2KJxdc!}`^UU|LICK78^r+@|M zAHnk{kf80zo)FZRHO{cr4gV;vR>o}=xWw7i+M?~ew`p3D=ah4svq1A7d_qOlcf9uq zAH)CF0)`vL!#Xb@AtE;(D7OK#Tyg8z%f;tv8|IPuX9}Ou(eEfqMK$NB;~@7!V%{pW zT?ye_cn@6D=##@V7H1aAVp1kk*3PqwGqz7gZX%Z)sz|3Z6U5h?I5yk$#7-AM3MaFH zsGYok7uEGBIzPk*-nKJX;DS^l;bsUFnOgG1y4YbB=O$k!Q}qk6#V6 z9IkBX-ui&}eez&F%If6TE^O}uoW{@XbP5(xTNVws4|^WvA_*o9geFCEutW5KvzD9c zMXO=AS~7eP&R!eLCKOYKbB{k-UrB;Xo}A80_pE9MeFB(&Z+)~9&fT3_ZkGH+`WGzT zgIF{alnBoUm7)?hz$6x^$!Wpk*cGt|+iiG!t9IpTfd49CgECDGq^Q^}e;^`ee&pMY zoK-wl@?^#v5oLVW|A+lJvk7g*hv8#)O`zNM+k~_e)7LRB*MYOzhIIby-~1dGGL+A` zt_y@e>;@|cndDs$&~fj#9I#Sz-C%}CwP-=im}@e|=$HCDah~-!{Zhsj64C)UhJB2z zu=r!p>*D^~g9MyZ|MruR9hH5d8h9vq?r=@cZ+ZekepN74NPS~Gfrq4~^9h~Np(Xh% z$hU7%&Ru1Xm)-K>R4$NdY-UKSo~(;LpCIVDdhnQ-F%}QXvoFH(Kt3ybvg|>i4aSmD zBXPa#cG0(WzdWxgKqr;mu71U791Q|1obs;(pOjt1P6o@5gx7k6|Jk9ru68e^{$=B` z*!Q6dox>z`*&x{Sb~! zQEm){=Y6E=!`DbV0pz1-@9`hSqa4}g@W&_Fv?X4CKhSCSgEk^Qj0K-1OCmzx7pX<( z(--||6uEq&{?Sp3QeLY7FCG~eo8#wxfA`+U?|bj>FL8Iy zocFxnGjq<)Jm>W;#REsY4u=lTcRAq>+l4M9ZMlP2J<}_WAm>Ik(KY zk`IK<^!Vj!kQg9Kq2P7#1YL0;L0LQ7TwtJgLbW+0TC7&2uHc9oEqhlf(fyDlAP zqPQ60O+vjr@{Fc;AH^m_H|*9lG{&ysGgHkEmBtB#vS3)+vURXS91E@P(mV}S?wyA^ zf)cr~+((f@S>`uT^1&h44e=DD(N4*#&Qq5>$hgC(j!cM{>k19$>nr-e zLgCB;C(UVKx%!Y>@sHMwAG=7v`rc8Fp8R;GTxRQeJi`4z5G)dLHCl=+CrDJ^Q1NP`Rc^HhE4Ac zs?l;<%ITi%Kmn&@MncqCdW*a;_jwp?Ttr@Vr*ZavO~L$%z?8|1$y>e>_dsMTU2DMgA*Hk^yG423mZsDf8kstb2+kTjG8o(p z;AKLBjzP5*vA5g&=gLji7Dh8&dmoi&XpFS8hp;QJ=S`Lqcx)3d(!iNPKT@_UY4I*Wm;0YwX4jiD zTGbn(lR^PW#T#+kr!OQ2ZN_fg3q&@eo4ONuVI)OHCW)n8X)o~6j(J&mU9>q8)>!j? zxmv85Y9Wq$m+Uw=xI|wnTs!l}XnWw@><|eT5vx$L>*k1iq3nEs8M$#PLZpXXTY;!< zL~j1-qMSB%j zq_ogF6DTo5zGp4D?$#-!wiFbtGE($`%|G-p(YG;-+^4Ii2ssjh4h@z&Ok7D3c~Lly z1#->yaJg&A4TZhJN%7V=Sqb#n#~cwQHDwlg*6t{F#|E3HwQxk994ERJOSDsA4L2tP zr)F~%bDu|^gpUyEH!;!~O5WgrYY9{4_O`;_nr^q+cql%P%5AE@lnA zhHR`2efQ8vb6@k&em1RHd#fG!bEU1|ocKU7cmdFra2I>0SiXzwkBquKvT*{d^gOzG-J`f-WHse9kI2q_)~uSbYU9>g7>s?-Xwm}fa0N=U}Zae2|? zy%k0J7*Q?pg>DnC?*_N;EHgQ)6nkGMzRshtcJq<%2Fu*NL~n5XC6-QM@V4+}UN~=g ze3cQq#1_Q~ic98tk%ReZtmppc=CGX`BL8sD3wj2Fs4Bu|ZdZAL@`+>-NS&*7Y@P5i9hz)BY{20&vGO)z!fj=;l`2pw`?emj zvgoA8cNCL8aRU~*Lj579)4+hLL&Lgz__y^`w^qJ-)c`+o^+0JyvXWgq@_yb2T+gNZ zpEo=^6Z9?FrXKp6Sq3v1lIH|z;8(G{iIvKTjW`IOv&goHE41a~40YkjD?j&T#KN50 zCM5;blH_CBB`ommvwVjeUxk&bb>i*!re|p&L&%bH0NMZTHI z|It%U_K#6fZcpic;Z1PV%0wzL7QPa1rj%KuD>O5stI^w;CacRd^1mV8{$SQ^nG7bs zek6gQ`Y_D6BkA$1-YY(54fTtvn>=vLcBrik19sDvw04< zX?EiK*9I^{<8N;iWHq^W3Q^vT^|yz=~nw8!Dq+Ii0>#iT$*X6(7jDxGrI$b67( zu<~NF^lVSzR|bS8K+Tq1+v1#rYQxc^FIaa}TIDuTt%{<`5pv(t5Xp)`zDNM?A32K2ZTgmY zgUjO{vFCPoh|KNMH8-Bk5}JXv6CMXdx5lg64r{Ku@ywCl1&m8027yS7c}b2hQuA5U z*r@rp;4MY71tV#cU@~@?0rR4^M*G;oXY0Pbt^&L6Q;ek|eY5dYNqJ79Vk>V-yp!-t zOO=-^?ABo0EwjD6Qr^cqA8oLqAB&41&O7;HIe}KFryq6BJ|>`LxE(Lry?Bo^>m8MB3hkTM;|LRXR2C{m4lD5B>+%Wkjuds=N+c-TXEnv;>(>LD z`ETe_i5@L$n$u+L&&TBV6?JeW?zdbuJ*Ic+^h$(>;hOhh@9YStpv>!a`fVZfr*0qG z+}H2eH5RbZ0=vl+3@$KUB8-XQVyC&5%-4Px*rTV0h$GuJhcrMA$UuZcTVfZ4K|BCk z`hgmf=a03o;LAgh*Z1()Nn^v05Ff(-&ZUI*!Cg>7KDhdHf(YMb?*D@o0Z!uGS8r;1 zOuo>gohkbfpYhU@V}JumljHM%BaKc^8v=VYs&ycdApT6H)BaOY|~*b{#3 z_o^$v+Z{s63O}?l2o%AA6C7_&WY}H%;@t_MM8iEx)5cRDpgQ>Gx3$PDV!&mczN(Zo z_~T@&Dl+77>#NaH{|o;2lBA#vY8=E~mHkP>f>u0(cs%Kl|MqcSqu2U4M7Jc6L^1&c z1_#{nmk-2Wa6loFjoVcKu`o1@_x;TN2yz9#5GX7}#k~XCq6~m0jJ;l41PGB3fQ^KO zew_&Mg*Mc=fLijKYpfui6M}yvNFysjnH8wPOb3(K>KE%F7QSigmRK9M3I6v#XzB=OLhnZ z_ow3Tk*$xfql9pjDaZvkSy~VvaGyc(8d-G@e{XzPqa|4M z6JX9;2ua~EZcd0M1pm7YY!1Jg56ZlQ0{>Pf-4ER03UI=gAfRp+VxW+A~R|fr8u$s2uj^>R`MZx)bg96k*JsL&%WfRd8SGH0@X@h7h2rQFI6DkKDh3 zuWsL8iO8W1OELjLbXYxXcNHk&0>wpc@JeWsF(%;BV;aBu2s~gm*lUg}Us?T;)<0*Q zS|@EB=o@`S3Gg?-%ury$zVSC2U}hNb>^+&F22lG?2zhfA_&Wcjyc^q4 zrw>{(0ymwDr5(BjY!N5>p(jR<-@NsTeC0uLOt!J%4hVv!QUs%CP4dUZM@HGR(KH)#= zqXLC1AnxoY)9z%QSB!D$6W}VHpf3{;hy;OIY&_Q>(iDmzkK7aMYXA-`N~;3yyv{B0 zl8h0OXG}NqI;Ft}{wIga{OZ$atm}nlxCC4=KG4^VLECxcki394Gvo%9n9vP4gFZnG zI6=6;4~O1;x-eomfE+6yNd0MfP*unb9BttZf{_XRmzvCJVBcqJW_r+<<{0kLOnz*eP_{3=jCEi?k}95*q? zK)rbxH15a3(|BM+9t5?v;r8r-zXw2oUUo;{Zv-fuy$eJUr*lO?=Ep#O;mUG==+*yD z)s4OfSlu@WtNXX(qCvV8_)eH1o>fX29q0{J`Cgw5v?cWs)JU=~$xw`-8TdY51n^W5 z8Gen`^ZMl{u~DFrT9M!j;bH7Jux%HZkW2Gg9n4^c|Ddd%r{jQ6qktNg_M6A7v91j2 z!;YuBE&?`pK%`w=q73#I_?ap(y#+S4W=P$WfEAd+`>bSD(J zWQY`0B2Jny1|E!{v#yXlj|nw4Q}iwxv%BNebA1BL%B=KLeD^d}SoXf`GCN#%s59cZB6RO^EXNnFU+ zBtFSK)c7tcRp9nW28J4C@Sacy%CN{Pfe4R)2Y2rH(Bd25Y62n-Ypq{s5Oh$a73zOM zPwAk(Zw2a^f2*|*^}ir+tJd!iQ*WWTiKyR#?qT%XAqjMkdT88qC89_1{&XFT4%$?8*?yeVx0_ z7gQPufr=!Zl@KD#1sUY?WcUjH0d0?k9Ju5y+p8?dBxquOe-tGQn)4RITICRLbif7g z>E7iVsjMXBK`$%!KzYX-sxVffeFy_Z&)nN+s4IZR3~d)y>ViDwKG}WSLlb${y6w+f z@6c*+l&iDj5ox1xlRWKq<XjH$bXrW#Ot2aFc7AHgX&H7JLVPEJ+ zA^!VjzVnL{i<%SVoPHfRGwgnUoRn$zRKv0gi928HS z4?NFDf3Z*7U5?l*L@&;Ho{nwzZ9?alq}}sIu^!}fBp31G z3m4yNqPA{|qs(i);UyX)(;>mVL0j`sxZLfn^4iApvJ>AY{J(mp!XJPI?t|J5$Mhda zMZBFAW+tRrCYA$VGNvQIc1`KM&g`>}dl;n1HzpXxH#h5*B;mZ=Y3IKRe)Sta+=6BU z4&2>3wnohq5jL*9Tg=ybd(wWaC6mz>1_{dt(0%XaSyB#{={;veF>PqM)|~hf{vx=> z&m5RT6+A1N;jw)6GFL(KvEA%WczcB}gR$|Jk*vs#tTuK8gYsTM%o^i2BW;`}B8PWA zTe?&GL{c#xxHL5{aHD5eNgc?@=0s>p^@6JOh3;o}tPdrMi0jf(4i=HL_25vd;?I&d>Va*Xe$QhgCxru;?K~u@wzg0sEreyJ}JF= zpHsB=dcrfd0`HUK-I*A+Iet59-d>c$-E6^T)CGdYM67u?{FTD>Gm6R{E;n2%FTJ9$c9t$r%j*Qw#z4;F7PHEep z#5twAMET+CJdk8zwQ*Rm_tAZzx;EHy+)&1m-IIel@D~I8&0%-L;}3v%AX8BGBA25y zg@xVmO;n9akTe-fK&U)=uEmuAbAEd8y@XM|W!h0yzlB_8O(LsXOKiq2;b)&(H#}xZ@L#q|B_jNv>@={^7GPp;TUI>NK;aQx>sjLy>VYxJw3TEO2pO z>ey&RPWGP);)mbE*tlrtPTr-_Xvc5zpe{7m^$;B4g^^iw4n?ml27NBnmSTpN@1Oo5 z5A>V9Gqq5Ab(QiFnSLy}4`t=9?|S5SmgH{s4+HJ_T>O_h!}=p1c6x0!k;J~w7khs< zrh#!ld$I6Imyaap3C6el=1Qb6@^bZr`p!#wYSoWXk>E5TALq&?h11!ijoRJe@_CE7 zS+$EuW>{uezo_$FXN>@>W8s-0aAep6^jN}f^ ztmhQHt6yF*jP-bhbDF};965prqVaM9^@xe{f{sHU=xLs{>l!@;o?QUR~EvN z$LZ>$cj!Y4lqMEEG+M1}-3~2QicKWSTCkfi^;rG!wIF_{Ebd@%p@WtiQ^a&Kk#as8 z<~L8WbbDg*O3-nb5ZYuzc|Z&tdM19s`D z*lU!&n7F<5QoMeK(GkVNhF?QRioeqcfb13Nltb0L=XvOP6KUK;+^4g-SoHEoYv&DX zH+rpKL+|!AX8!WMk@Z)L_MXeR5zQAaG?;qa*SGv+CrwtZpMnaNfZ-k|@-3-GmnO5W z2MYVa2RkJ(l-2^tamwA0rW$q(!A)g{SG>2}|LoVS)KJ!LA`_45G}Juk`9&L$ ztr{lE9Z_BSrtWURj3q=iMq@%7fqETwus*Eh@hq<)ni}(C8uUIkwQ2s^Xn5UQ@)(Mr z?3-bQD`=ZL95Sul1EOCfKkJo@Z|k=;U#FQMo{8+8O}2}42&UEzbcj8FapH~`UjAge zdZsb3-Xlt8p(RsT`{?pBHoG9-k?QNH*NVe7Z z!Q_(?e*-E_fD!Sk^K0BpF_F0iSq_rEN@E%HXkU9nR{ryQUr?Uj;Zk$rY|k{V>A(X& z5hIWFyDwtIKbuJ}q@5I8NT%C}(b0gN>a9Pp``Vr0Bt!7}h}Zz0aJY+n-YuZ?hZ^{r zXrW-aVBye816KYr3QgpG1P}2yNEdNPmsy*f&I&l+V{Nrw+{t?J>v`DeJ%ZPczHUNG zC8%AGU`iWY&Mix>^=}~tx^A>J)kx>N*c?*x`dZtp82jMPh^}v7mKzXz zCzaiGRYCA^wRfGNr()32J(?V+eI9?U50HZ76~}4ctrmWYAgY<$h_~BgL!uloagG!* zvkt5G86tVAw804`sjHmNe_R8Nr&!;1hrF zi*|Afr<(XiQR_L<`?G-G$JVcb+vJ?}mW93_&5{D-a7vLoJYK^7knWT%Yunrb?94&hI)$Y`R z#9B(2Q(Mtg=Be3ti$%`#v%xQ!=WE~6&Ic2Pm^-oo`D}K_&&l#rK;zf&A-}cZSorMW zswzA0kb8Ye?F_QqtI22On5wO~D1V9y)?0lCEpbeJ(;vv0eLKfsAQ(p1)idS5s>EoG zXZo0+V+FI_^Rj1RUULB{i9wXTWHO(udC4R)Tx9;S#HpsZR)nfH$HKd?*qW(*nEJuU z4g>bR78SqF4(W95?ZT~uZ@BeIEEGchD}ruC)nU;ZNB*tr?N$DZK52a zp<6c!cbIUi-~2;V2B@?$oDYm8-9nx#OZoBb?qDH@$@bY#$W6$ zw(4J=tarpC5H~X>!gd<5Q7PFLKUv_FMwukLB|M(UH7d6+mv&+OAyy}R-nu>KO3k%B z*q}o8(nJ6?TsG+^&-GAEWW*k+eH?qG?^(9zyqi|$rE>sR+6YW+jLM)rPZSFbMcZd;iF_}se^{}SUgXaZ@U4JVx^z*=BdP}Ro7oetPHKgB>%QP0syg`%D#kj( zO+>=NgP&(3wm;%96R~}zV`X}3OS7f}*EdgW*A6hrqN7C!gXh5=1Cl9hZr^0p{Rjj{ z;_S}G5?n8)yUncJXA(3vN^Z|#Ey%cPBw9PvD8YdO35!HSg85cZXugfgl;{1o&u3yW4gvkCv2)U$bAX5(~qxezFFg9 zMkdmk^nMLLn?w}1H99UzxOav3&pJM=*UkBSM{O`@3FHkJ|1)G?|MV&~>6^7*d|Br` zG#(qT3AmFviwGXHE$A;?ix=I-NP-{bDQ6Q4J>|bmGOmP^IulKi6!aAJR>*4N8$an# zuSC&i+h<3Fx6j)DY-Vu22ef5BqOig6TI_Q5k#F?Lh*;D!Jmu71&lr#k3dUjmoH-WV z9?M261-)N7n|R&VQdFCwgOQ1cLx*vVI}P-*&gK$wa_e{q=b)qyj>|fM``KEelSS2$ z?vhs(xFz48Iw$C+l2*26F;!hV@LoBn&D}`iI}b3Zv~Qwenq)a15&wc-XtMLY2-h%u zz4+zztJSL2*nJ)|9=~*6nA*C$v(B$DoT8&yRuM;Av>M-u|0Pgx}UpCK^xuZKgYFaOdI5|yB@WK;MU{IuxQEi^hYJOKT z)-nz$9j{;RNI{mt>psdwDK{w3Skz$fX03H;p*ZJR_$TfP235y`9sDdqu__Pn{5Y_ z@&S#nx|V5*aual3R(^_!Zm$lE$4M;LDZV<4Da!Gk3n(+-{@Hu+l?sm#@oVvt{rcDy z=M-7h;SF5{&F>SMPv;K~_|=Z$BBI^a+8urhi(q>7-@k4%LNE_G=aT2%U>H6SZme{; zKUwq;72Z){<}FZTJ@#S#BLOdA#O1j40oJld`Qw}u2V3`kxL@|KwTd_qPCz%h&I!Ot z@2+TR$IOAUde}9qd}CL zJz{48FP}6jZ&_WVaOyY3aBZ|k_N`y>JPT<|ztRm=)mPi^>F(GXeEI502s7N=zGYWh zuO()UvPvI(ywri&ckC}Z)Tr^oxpL=nL2yfzS3#N*c3&l>*EA8#+CMv3FG88(Jf{w#T zcSQ17IWjnh-l*HWP0n;B?POl%KKIyn_FY#dkJeD!t}74fsz1eDncOYEc&~WY#}q)w zOIm*$eJgfTzndoHtBm8$d?)4HCJC#+)O-|vdBHV8I^)Hsk z9>smAp{-8@sUYON@~fKSJ_^WdEI8vEhSKA7Q~{mg*N!nmlgjm9OgAs*dwB+RUh5!k z>E`k8C0dbG{NzD_bW{)MI`JA7@zm0=XF3de==WkP%M`Vv8=dW@+*<8vvxItMbs|aB z&}4WLBoNc0{=GAJK%4L0@U(l@irwV=p-ldi5dV07FnA{lBfs72c{F`7g@2I3bYAO< zBD_q8d**pDeX*76C47e+=Y)N;|8qr3Npp>UDAI3o$L?gcZqTb zi4CD0$#dFf8w!M(`)ZtPO~qTjXsVpCN99BlOenI`8p(xXd6j1j9TMMfO}){L19QX^ zpCii^1wq^|g1QEJdTzUa$iQUgIVBP5=`G>W>D7A$%i;O=kPjQfVPE(8Cb7!8e9E)2 znFI6r)BBAyK{q0Tof$^x+PrMxtd9P;#fB5alJk-@)#M02M!Z?*z-PxJ-N@2e-h;6A zx~>L+mzRs*8ml~E$j=8!o5D<5t{R<)#52JYMh|^XY7c2F&!f%sw>K=TypR8$P~-?? z^+tQxuBj=+F`wpeN^axxdCa{o@;JFT@chu`MZxdNruTCJi;~?FvsJbAy5qT1C3P~| z0;GkM+kr{x<*LeFv4b5|h&qIS;_Y&;IR;i3t=+Yoh?ud?8eG>^ZF2qt+4M+>AVUOq!~ZT!e?~R$tY4g#m+s?kdLK0jUHmdI^dy1wNhh6b zx9pem*mc0k>O+FvmA;46vZqTyd>Iw5N^)xi@spLh1+q_>R%lsGVFOK)u}ZT{u(;Am z_5;)g{I_|ddGlym(|2J$lMpg~QTu!q>k!*w-cv{%w${Vq6Q*Sbz3$7UdGGt|!;UzO;X?*{*i5#U> zO!J7s>rN;f@nsBemY(I{o4(eheinj(E@1%2kispvX#dV&%+lb%_oXfvIEhf{ziE1W z^cU8xakO1K59~)0|EfRmT$;nGVQc5?&zXPWp2N4a-46T+qYJw!$DPg&HmoXI4K8vx z(Jim+IF}PKY~MxuUgBI2@sr9uvM2ix@->#*sgXN*ErDv55k<3B#POSvPoflqz?BzxZQNsZEFnFdPC`wJ2}ZDZDv77MeVEn3;oj zyR`=|j9u+`RooQC_)5oEi;Npr*| zZtQ~Xd~yih2MA+hR{*}9;DXkw|1HMK zhx|-%;kh?(cxQqt)8s|GXidEE0?2iso3fAl$kF!vJ}l{?`IRxVg46<4+ciFp-}(JZ zR9|GC|w{jcS?VkM`xvnLEOo)3~n3TIAxvFAu@j~yrUUfJP% ze!{_Ia^i(uvJ_=vwo?MFo%A#Gd54nP6hn9Q>LWA-=Q-J>ALY{*@5!(Io+|)q9rDJ~gsP&~S zs9T5SsSx=#RoHheu-QEQLdhMA^9mp*sV_w!|m9JWC zG)PaEj`j08IGcCXH*=EsaIteP?9CrHh9`{We7r=}J2BOaK0eQzA2m+3&O2GgmKdR| zk#(?KDmimvx=rvV^+^(`QzG=%de66U2CksAbR~>rwUhiBd53%G>P95M$GE3o=TTy| zCSF*Q_Xe2FYl-$2*^u=?fM8wwamUejj;j&v;hhFvriozvpNLJRHl$Qtlt zRTu>nJ##2a-3evz-5bLQAiW<1DzW`cFF$Wpu5bnXc-Slxxq`wmk-37eYnm#O4Tonq zP&oD$v3&Kynct%FO~u~!RO7BLVzeOU$9s?X_B1be|c07uE)BUy3>o62JlpT>J5-rxHknL-bw%@a&0+YRmU z1L+veWQH|p>;J!fT$f0G&okE%?5F4%ac1>5KtoId;6 zyZ90RPQofZ2)Y1bx|E9Y#2f08X|;N3g!68+Vh4dqyO7!xvq2vOHbCHtCY!-4NH!<* zF9f#}8TzrG>)7QV?WylZsh@pc?x-$-3jYO_^nCPY5KXCZFx^cxr+)b#PzVnG?*Xu} zuG0B^72=>6z`cQbay_)I4sCl61hqlJc%=e8HudqHW^~ZK(?NO5`!+6Rv=1OqMn0(0 zjT)Z;_zRxyJvv-E7ef0TLNLB?aDf_oAi|DE_K`KTn-8gO-Hp!xBuCu~vY=w1Tjwj0 z1o0{m$U>c|8w;5LB}5fiUCM)Y^C4Q|&iD*pB%y$fDJT=d=zhdYBm#s3F->?`3tzlh zA+Bh4vHhTou4knHBE3FeBZUBmXi$qLYq}Gt(FP)5?nNm8utPeRY_;47*;X+D$v7E; z8i_QrKZ3?`LyheVT9E|aA_8>^$BEsv@WZW{y#pG1lf7?S14@@FoOhGpIj_HWI&`Bb zg?2T8B7CBPBvtXv$GpJNHY7xcz78v^OYsj3JQtbai0*Me4udZ1KmhoZ>E~eq>F}4C z47qXK>FPQ&Us{f7m?vdis{1RIhiSoCt^kd0hnNmVY^5R~62igVgV~-nH$sMQy#}I- zK|kEC;fq7g&i0PhH8ub40GSSOU$Cj<4>$)W}b;50V81qxE%)`2)g zeh7QpVr+x7t1d%!Ugq(@}2d$ zdBBDI_@EU+VRs{&jio1eA&wEDx55GHlmop*I70d+P?$1=utb`Lm8K~-H*oZPqM$U_ zS2Z>eJn4Fhj+5fFbm#vLQzWQ$P>@{$T&xM9XnR){aY1JofcU)jsNE(Z79d{+u~Dp~ z1Gb=048(E43LGMVsjLvbqL>;2{2FF#$n=#S^RT+@2l(QDH(P8XgBv(942+O_jb9*2 z_dbOEMH=;{1CtbhPos^W2nrN2;#cNC8k!O<5m2Qof&dzG@c2!e7+QF2(lH9FTAp!0 zfiLzzi4Zr{npOec642mn{@%t3k`{xoI?`*Q z9Ig|E%yE*h{8`=p-$n8OZ1qtzud_iBf~}??@a!wW8Ppdc2*{FE)g==&$PCol#kSad zi#9A$976nvA4LJVy#HauJ{v6x`R#WKIL=ffa3MWKNHV0D`3C&WyORb<;E{uIKWJlv zafx5Qfu9eoA2c`J^IOLo~Px!qrCVo&XBSs@(N)Mz;(Uv0>gg1 zi+})K9xxCFA79^v^v{0KYJE3)S@Yy6sK?(Mntz|T`~=aLR)Vg*PF@2^wEf4&FfZJj zSHav3h6MvskQ?0X-%j<07i8RPY5&jk2NAFDfe|rp^$wtUISw@7*Fdn;9f-O5AD=~_ zkcXS1E*Tj>EjXZdUZa2W@AVr0^{^X#r=`T7CJs&7(CjiVlR*qdxglr{9Th$B+<$y( z|C6}QKp+eZ(|g^)&{Osu1eP`7eOrKr(|>*(7wbKN87y+q$ifG90!W%3qtmgX4_xO0 z#Wz9lCe)80nhs;^aasd`fzTW>{g~IUu?`Xw9kE*7MF`kH8h;ct2w3$KqTP9MV9ogS zqYLO+g%AF=WKnQ|bQ+yNtSjq3oLmW5IRkiTPm2kjrza+*j{I~J6PmM^%QoSXdlwQJ2&B`Y^<|_x~s~aIe*$ANWgYnAO_;-f}t*_z_LpQ z+ENLevOKRq5884cAjg_dSkR|-QZDW3fBavFwADp)@5371TC2)kV(B&IXVn9uA{^p+ z%V;Yar0@EvmnixDe2`;wBSM<=1&h3HDSv9>z5XA*FXywdysbnoHzT&K=vZj#tZwT4 zDpq8`T-w-hpZlv!wkH|&VDqoWSY4T$@iA%*{*W49lK3<+5m%!~;3coxR!pV^;}0Wk zFST9<|&UA2kx9TY8! z(UbJHuu5kqspLD}GN>pLwlgJ)jFtO>pJ5*cf2%%IGv%#2rRJGwbs%;Fm(l z-0rv2Q{?jA)L*iV@xUDO`tudb-NDcWB)VDJfcIp1RArV3hNp}Xj`%ox+eJh$lqDM% z(e5zs+PKa@?Op06D5_Ka8{$`s!hn8Uihy^VK@k)38qC*zz z&CODKMzsTO_++!j^Crrx-g7?PSLas;E1#<3^*3|awHwX*z9<--s3=bA2bG2|zer72 zHMdl3B2gE6?whP;I?B1^mG2kiF16%0R*V%gL9sfrY|G>YI7ek;7IF72Oecr|7}k9= ze~Br}DB5R5>KDnCfM!|&wqk|VfA~w?Jj?SHshzfsE_dUEkS0f37R`nZ{aF*mptBMC z$|gP4M#do6@>&dHbY;!R!{7G}F0z-nAc}-7@>j}|IA^QkyD%}OTN$$v$a-mBxrXak zW&{e64^29T73Y4;{R~Exx`uC{jiM#@)JYA!dDugi{LZ(K|5^(e2#!TU8-wR%il+}> zUCGL|l^}AnaO*gAYr{q(3ZqIh^-{Lo;Ugms^>+!2b}vU|+G!93$`bd+@C8+5?}?FR z7(ZUEOXRydHx-W(&+Zm3a~dtuxpX+{xose!O+{v}PU!RS&tHYZCtooRWCMs-4419e zs7!X@J?a$LYA4d9*CKhei}K8_v)5vCQa0>}^wb$qECG)Ugsxv&bX7g>=f^onEku@I z%+|Uf6snaD!XjCg8yG`i$v|_(!nUT$_a)W$!|2L+WfdDm&CoGA4{z$CZjw0pXZN`V zJY@X$f1;al7_%y#II68o1kvKM1p(zFNn()V;JD2=q=`{=IM7U8OgD1vhtVLx9U?Ig z^URUJe(~Yc4rGQ^zeLfU@zbSsuhR61=fgV0T=`+aT-`jUg@|cS+=>(Q=Rt4N(1(_ZJak*cfWXL z&BL>lydBRl-RW)@&!S|{KGzKgUF~d-wX`Oz=EUS)h~~aVVpfIqTupWTLc!e6xd$h( zxI&TnU7Z-~5+k~4k$v_d;)hQA3FrG#65eSiC_7=qJzBXsItIVvAo6Lz8D`qHlk}8? zA4RcbOLuX&vXonI$1{q&mWuBdIj-U95FIRHp(A6DVfonS96=amf67!z#K$ZYDyg%@ zkGKCTPCUeodL0md6-D7N!xY;~S|0qE5gD4O-PSkH`ncAnS??_U1Y(K4!cjx9Ls z84(=p^+X780_e^Wyzylpo!volH@#VtQQND2#kIjUZc17WGxhB#ZU+%ghWHhY!vxoU z%cD;h!?N`{3C~g#JoLZdyQF6HgGGF7!E4v->G0Z3o(rNP=p1!l)q_xq^qx*HTS6z0 zLB|XA%f2!{Q`jG-u%RF0A|X|qhwL!Vn{2P%wl_x`Io~EVmks!ymhylr!O_~hkZnfq zeJy-LN$RAYS$5&?W{Ch^g2sosFP(DspX?A-Zz)A>2&bAJ?AifYgDYeK!e(`0C+cRE zh~%o<9U+Cnkw_7d@uG*`VTrf-I5#fowP7F`Hmh_z)wF z6+c54)}$SDI}{B2zNx*l9KaoJ=pyheQ^=!RvQ>fhi71L)~W<?$3{^?GMbS%PCrjBVhL8nUPd0m7)&k|l=av-m9sF<)YGn;8*5UXq%lU6 z&RRVTC1WWMKf3bF)v#q4)eTgjPE*Ei9@{PbM}Al8`+xJh3KIfV;Eqe7lRt=C%muQh z;2UvDg&U*OM4L=ALCyyDjbF4rq8(3`?fkmmfFt`L+p9VpN(&7#rnUg-qy@FG%lMxX zKid5o`b=Cfc~JnR<{Os-avT88X)hw_lH^9HO)4Ezdl;P9EtVh8vzzE8RB2LDCc@J> zsLhzIu<_t$X(x)^0aV7d5Zp4rR3eURVB+OkI%jvW~YE^H?XhTKtRdA@?M(J6lQ!D8iUWnFb_>p!u-~6{EPc8LRLo`TeTo>T_1EzmzVf8K zxWX>Ank~kjG%ykOTWwfZU1WDn21QSwzz%C2pM(F#e!+IP)~Mdw0sD<7?{OOBem*ws z6z@tg7B;W3_jQBQ>TK39iIi?%VfM8doNHnfSgW+_@W^Av43*NGObCMT7kZvENG@%~ zGP%u;zh*mDi-=h7znGO*HO!a2H!i*#S?!y0jgw()q7Gw=}S;I&K#pnw%dpw1?JM`7(&soJG^n3h*91ga*sam@WgKp&ez%Lo zO9|7Myyg`z=$hNDjS^kvD+34CIpv3!q#A#lXC zX)(Oti;;Lj6XcJ@#xhjwFT^IEQ~{|h_khETnng7$klERHlU91;aAYUGT;eZ`aa>*M z8wu?JsQy*5E(sl{cG$u-sGQKO4=8_L@2s*~-O0WnY%Qdk7h^t&E!Tvv?f2@|80|T8 zS6*E3U7Vx{eS_pZE4R%G%ssrXit9d7!JTg3>)*3B>X<$cBt9S7c)saTlj-c{y32Fi z*BHEnMOYr^hvw)M#i#jx&&0U{D{&8cOR>)WhJK*GChxWt=|%60lmX1kP; zRJjf&J9CmaZs9C*D($CET*E(rcs5&xR|PYv@8S1StivhMvlX+PuwntMD7A%Ja6!4_ zQM_ySGb_7&otWnM0O7{b9ErMW;rqiyL2PO|U$puI(=VPf9+Z`+-gy30bKC44ZmdNa z8jD@Hp3SAb;?aDtmxF7di=O}1NsR$O5{V1gbBdm}X}O`FV*Hq<`UucXYc*8!mukd(C!JDBp**@NnhuR3QEi)Ypb7l+_inL&t_p>Xww zB%P2c#=}zA6b;>hA?j1T8Y2gXRWmEMnL#E)o)&$)b4aTT~zmWCUi@74Ey_?WB8KKPnpkx?^sz$G%~Cu{JyfF zo=90-I{P)vdY<9BUa#WzXZdX!$j2(rb-7Z$KM5PrRl>Ze7re$oI_7!i-A!xJEq9V}=*s7@Jt;Jd zX&TE;J$f`v`$tV%{qw@*ev6DIG+|0faakV5?>Z;MBe}$SN{!KX2YK*yRT)$7>FVg_ zYporYYbOb)yVh-#3xZd-xt4(1HFI2rrAUz2R&@=@vT{;Z@K`YA&ND-}+=)}j9fX$! zWgU$d-#n`w4zZUrjX6qK+}*@%A%@nO0hLm952KuyGltTf1^abj^Q7ml1}@WUDHQW) zbS$1b|M2zRaZxSH+AukYfPey$GaDp| zL?wfSAt)#ySx`ZTAPgCXBnn8*NLC3VS&~CWvP1=kVMt2OnHkc+*R${0=bn3i-+ljE z)AXvYuBz^}=;v8gb2V3^%ugZfi|#W4RT+P*T2jqi3H3p(=8A&dQ65d7&5Ll0wGGQJ z<%aEut(58K%y~jrI-Zy6I}`|vH@+w=St|73lzhAI1CCJu1&Fh`Dvl66b=a{P2nL z1#$k27VvO!Gvm|D=VtLe+hl}GbZ0{&7v}?l7uH)BLSZ(rPdL7}bwy@rN|C2mJ89v6 zuwuCDAB1MS9zbd+KIrxR1SQwIGhk(_PT<9*uXo(H`|A6EFZm0- z6cQn1pqvihVMLvi_;;K_p!+<-jzWg`Fo7h&`4aODe1$^Sq{)aa=J>{7%b|(BUj#~v zF9aP*_>ToxQ|X4%c^G_!5F#Taik>ZZ@9t?Titq1#a#$Z@^}Q{TE#IBm^6v7Xs|#lY zVZ=71OJwZ`b&3Ebw(7>E&4I=L4peV}@zxgQZ^FR1^HE}MMx$DNZ zacu>d_(yFz1i__@1!4BI9uwc2H#q|ji#Kw`JBq{_rBCq`?|U)U{R+m%Ad4qcl;y@9 zn+1ZMo66#wzKWSiAA@Tb#r7#?O!oRPAdQYtjw`$w7>;Bzop(I+n>GQ=PiY5acUsM8 za{VwNue>z#gw*b%@;*Mfd5sNiw&BAS_3hlfsRu3jUu&6B%(K~1kH>EOt5R$azEbRh z{=yw7>$@Ub=_mHOMVb?ZEJ&@^Gji8g8m#ZUL)sPtm4+YDx3y&~NK}?QjYMD$FBVdRqF2CIJzoq$*aXLo{e|BBiU;3k zfdB%@QyodL`bI?GkLk53Xq5CGg%yW^|D zS0NWml=yEQ1+O^&olU;;N!7i-Qpqc(vAUDnQi8rbUx$PI;&S((J@FrUlfu%jR2ONFc^HV(~7gyhyTi&+b#{F(S;v?<)P zLwx0BlhFc+zvzQY57z(MNDAZV4IAx)eQWBBbi2F^KA~-ii-~);d76+;ES~9ZFcd1} z?9zt>d5ukZyhvaDW^M3j#OORwPjk@=P(ldN{~k8FLl&Ulz7EX+WF7XK^JzbF1sH=- zK7bcGTa8#G{Mh#555&W zk;XzC0?CZ3bo$)Ixn+Cl-1&hOC-G7zS+WaXcV51|f2wk{fIS~z&-5$bT~G&AT;I&xUPeqyi}bS4|CLDmQYln} zNcw`W=kKE;%F4&Q`&a)?0GDzp#E!7IdNSU5J>?~}dxd~Wr$~L=l-&?Te2R)ef(2KB zmO<#4wT?_anJ&hBV)AK6s3j3uy7zk-Zl#?faok$wEN)_!48_N-nq|{?)J>%~WPOS6 z+*R?gE7x#S^HFichjL^G^QXo1bh^f$qMbQxD&g`GFn5Z0;|9|L5=*}HX&0~~LlpI8JuTNN9kFsn%ebcqQM=&fvNXB=UCV(i% zpuYTU@&R9J{Zm*X1M0-10kg3>H<1ZG(OxHXr_a)GP`av4<+K9#SCUPI*y-r~iC`wS z3*^C2408##`T8wtDWth5YHls=(aR&KnpEA{d?q-S!LcYJ50}E!vSz4rS>1s4cs%zQ zSs`WJ-E7!vsb-yJV2FOVe|E_CwM=^J6``H3(_rTQnahBFw+~1cwMgU7m2JN<6|yMA zi}2U-Yr5d5d)##|i9%q1zd~`p?4qRaG@6NNIPf|SFz5xDl`#vPE#SB(8uv`1i?z@);d^YTtjFb^`bNDT@;T7%3K5X<>{2?56{lf z$KE8kjN>lY;9GgQ+}7!ubO)~8?&}efvGT$GLE#*c8fuaketSjX8&mDCp(e{x^{l_>0-gWFVta5EAd?;qgA$XO zmYG=g@RW30Deup~h_qYI2B&i&Os(a}EIhE?v_=(x&c;etJ z0-^2U(_F&h=D$h?fB3k#u6KY>N-umMYCZw;-Tm9=xk@{?rIdEg%sXnKHt;iaZP5jP z9|cNTaF*(NyKms>!CzKrL)5UOnepuKhyJu;!Ar>{9vcknm*|V?>XXn>$ta4L0OCUwq}qnjd7g_ zc`qe_YwC@GK5q9&U(T21apyB0w9}Oo&x8q&F43T5Q77o*qLA~8{ul(mU_;=LxttiW z5>tB8jcziSdt~h*Bdm=@0;|3Fc~<#b0E`+Mn>Qv9%Fpb|^@jkaqV`|KoS)^WuO-P9 zWhNOu=5;uA+!Ok`@kJHXap@M7CwAI|eScD1C8K5RfaNpp@Dt16Bk^(ZeXc@tW_kj- zwETKg(l4uC47Z}Y1Nr_~a>+@eU=emX`p+#-WKWL#I0}4Fujo$Isfg?)g#Ga_>bUes z#a+Ya8odZ<#i+C){|TEzgVT!(8e&K%Z;Dj!R@3jHAp4oBmg3Ceo!{(pc~(C* zRLEdOVD!RedCeirzYVA9N274aOH|0@+t z2!h4rL`DZ7#DI(e=Fg&<6N}F!g3r6tzgIR1?8S`F3_`KzaX>>15&@av=|}i_g1`vT zco6m0>zjDq1D=Z_+Eud!pjBJjr=XVJ|D(JdNb;ki2nIN`j2TbHC&;;rN7vsfvEzwR zIv`Y1pIG2IM-u-oFxV@}b<74v7;OLSglDf%fxD|mgg*i$2Jq^!m43$?xLRTa6!Ypb z{X2cn{NG8B;ARZV>$Es~{Vl}l$U^_Iv!qG~8X0wn4+KH0%%JVi#woDa{f_&+l$ojRF`-Yk>F+XkP%=Ev3Vc*jA2`D(9@Hh^4h zAwPhXVhZo!NkjFV_~_=|L?!=a*RbD0e~ z)?E&Jn+nH*TR!)N*^@BdqKnYyQ4IS;cs>@lvYo zYqkX@6FbW1A8XmKikhi@OVx^8=z_9ce;3I!S(VIG`ZAe`5&DQKF}=%XN!$q`Ccdy+%uGx!`kZwgsg|tLa;$2iQMX8nwDO3?N@106cC&x^9t@IVC<@mJ_ zQHFc%X!ipTum5hG7ADMQB@#Bf7atK!;S?Ht2`TU`U;S+`g^CI()g^kmDpvUq+R>9Z4^AMB9Vm~!FX4HO4s{zyYt}NuGW^qcYORUksgwrefsgQqZV-_R zQt6dPe+9{=6=OXHCYFqJ;g@&(Uj zg)oW;6>2aXi|!XXLOMqNHPd4kI*;1zQ=gv`~DO*+TLPGPe$4YlPfz@L11p}!)v z$v~Gfyz2b)3gJK*L7=*U#ys_cDo}t0C@=~F3MfDd^c5v|WC8r4rw)9;61&GkBulx1 zeT3KSt?3ZZtPqHl=GQ-_C!|YH0X8c2S+oRFbcvj=l?u5E*npuLdGkVR8{^m1cq(mx z@Y9$w7lD5oT^7|8iYN{QfnK@qOu#^;8eky)9kdCFMxHXz>wKU_4P0`D^p!rW6i^q^ z0s>)RdKsCOXCN%~?t<<&lim4Zrhx(4)^hS@-y_lZ zEe{kJ4!OrmBzk4!`gh=j?*X@NfY7@F?tK&b{v*&J8FWuMcEl^dP1GoCM(y_NK5)%5 zE-+Ez2Z*a+qTyhocPtklf{Bs=fl?|d{iGC*QR-k3DLg@WKBq$o0C~>c`bCT(|ItVv zGU@QJy-D3I~VskNF@mgt) zo8ZJ*;;c_!|Ll5CL5tyztL4Q>JrBSCV69egI=9zNPUF!c%XfN>##W*oBZ(zqRcN%IcTq9v|f;zqf(}27wR$L-Gy@R zG~bS!QfqSg+|o>CzP~yfyIS|trOtMdfPtA$Huq=iR!dr-grHSqvaU+2mMM?F#mOBS z@fE&)x}A+x>;&Ql(h8wgzPNV`7Ow=Yvu$h)eL4aS)uiXD6R0y(J{_nJ8delP zuliKM?#=(QjB~;{ye&OF3A$jpRkt9Vm?;a_-^$!XqNf=}h*W+ zXI5PKPd+00No?O(=DgFg_f@^UNV6buvtU^C2>r1A2!OAdy(pQ7r8c7K-GU=&s(wnF|!7-b2a+$$LqanR)`nFi=>0 zmOy=3h`!vAGT^P;?@Zsh%_|CH1z4y#KR#Q=4$0Z}X zc0mH+WDhl#AGTbJc^SmuRL7P1mzm0;1P(?1EogKXZo1|Ft%Cb2g#vVyxApXBc}6>v z9+E%CrT|en(H6d+d(s_s5shd`^p~5;B@aJ1^!4yh=a$f`>XkH{@Us%`mSnOkKRP^s zAQ)j|?&*w=n*B};P-jdvFxdiR;n?fb)WSY*1oPYDIC_DOB%qWIP+ zhX&)*~Wt;)#|F$P8rE; zv@?CB+)uCR;3+PxE7gbcPZVbDqT-V%Hn%d?-hdk$f7rb1@L zR9aRvY$|ht1FjeCZ85iuZa>_5xD2`5KhozF&M1PD|U!eEv7wwn!P=B4JHFt7!caC)rv%A?< zAL*_K#z{_2Op*3cVoVTfNWQ`w zCT|x$q7@;k7WCr!yll(sBa@9%ME&BUW7)OSCHrEp+R!digq&I{pL|}O024(fCwj*B zFxFV-{&w8cbZSDQJhy!M&axL zTDJK9$MUIoO~kfTDqd@|S>~C#wb^wBhW!HoHuKJqA| zzpyw|dYr%UED^A#em>32bP!F<#QH%zl$!OoPYX+)`vz;rY$iC{hFyOA+uj}?^*Bx0 zooMw-|6nyEw-!uB>*scZ?Jtt!!Q6|57-fw9HIGE9)jbEwVPchm8LYlTS#IXs4U&ET z8f5jp9xu7F7H506otCoT0zqQtc1aHt0$p1!u-q6>bEoxgyLnhRX)I%8=7$oU;IHLg zdZC^ZOZ2lGWb4On3T5Mxch5TPMOFpk?VdTsNL%Y^%|gVU%@hh?W?Bq$v-rK|Sv)BG zpsU;Z8Nz1jtxw`qbx1E~r-4mz5#_j{xFV8AiG7B#;lE(%Iu0NfPY%qSYIq*YjJg0VR zLzEKf&2a8+x5>i6V+0MlbsNQ1i{0v&380CrD%+gI3{Io#={DyqHvEU=H!^RSS<>I@ zHNDuoZT)R_2zTz!(Xd-T@eb05ox%~PBAB^$Za+mB@=SdX*nqe~d#~pGMD+ zN9?ACk+<`z&LNB&96OGDO>-`~dZ}^uNf{-I5kYSy6~a(sM~*+bXGmPb>Jt_011w%b zCTjDb<+g-WI_-oa--=#Sg2d%dCqK9p8`>Rli{3Pw)0<*{WLHsOr_g2Pa|7ez=nK*Q zE_)U~HkHv(c*%sM)5cFqNR{UbM#t!j{nl#GF4myeJkkV>eXYE!6q|lkX*G#~l>d(Y zrDu)lU;KY!n(swA3kejMVHDLl{W1LE2^pSE)HB+B$eHV9_3K@u;#tMBdhKs*dE{7w z=1<03rMp56qmyUWMcTyA$S=?(vZLaPrRB;eZR1a*n6~6TO^4S#F}E|eGv7mCvKA{B zJy!1^>L0uI&7>Yj%!{?uxkP#$hLT?Hw2>jIMr6$_!9pI-5J5xk)~D`J%6n`JaWFUO zA6?W$-8^tIu83N;b6PHBZ=7WYhl_HUsk#|Jr(73oH1GxMK>07_5A#z+n%xCdV~owi6gXRae3Yfakz2ad!XRFwAR;G4d$lkQkp)6ZDGl;5o9lE&`&`}^&YE&sVV#zeK7nk?v=Ic&! z)*gi7(%D;qdWW$VI^bSF>%sC# z*0#w3{QNKaR`*`Tv8-x^<3uUvr@@r*la81?6T#q===Rd zzo=r__3?pen_62pc>=BjeWSc|Vu=y`5pK8WSmp82I+62!EcbFWt%TCZij;KjiG#;{ zgGqOBb>Y^+FzYg}l&i%kCu?=4|G=sL0PaZ1Nv}UsH(Q`Zb>^?cFIGnNe(hiS?=sUd z+Y&lqYTTl?{@&ADpGfOTN*p_GXmO9&moPYf{)lOqZTsqQPNt!xCet{l z^JN=`Ef?2BCHkI~)Z|~6Ot$9kZ~vJl`8dlRe{%cg3XJyF+Ml;LM*bYdnZd2((Ph*` z18<##=GpYTMGZbAmz}_4%8JQgQ2AnLxfyP7#ea{T^95T^=hWQvJo_B`yib(3<=Q}T zeTv_!^E+1iGhW}%g-M|+q&z4&jy3PNb)QOs^T1s|uP!^Bx7ui_v1sf!T8?oQh)q-I z8G*|fSC)gi=M-rqm6Y?%x@UcZDF3~$2~~lN*0Tt9Z#4yKe`dYGmL6nOI{NheQ+0of z1B zxj4< z)X^`t!$c)}C(`WFEUJ@psuNC)w|XWu28x0j3oSldmD;-Lbc#JADAgQpqMT2T54)dx zYUI7v)&*Z?J`L)K2!FMJ@okrdpHM{%7NJm6_brA=OtfQ3OX)JPRT5U~&SJ~PuASA9 z*e~ANP#DIfOmp~-S`g)m%O^>hfGKY1ZcY61maj8HuPbiFt1`YNU*Pw(w7al>i(=p2 z?1=dYb*sIshkGuwQg(jgD3Eqs@FEr78W-167j;#Hrt9^U6XUm}Z2{c`?{c=~FXQ-b za@wPR3htuFck%x}bfj0aKi)LEi)SC-mbGxtm@?R8m{kk7Q7jGjYC%&y-&(G;q9FRqpYP`we^X#<=IJ02!!0IsLSM zQl%+7NWEp-xzu$#DODDFB8>(+!`d8ykSc*lVs%wsry--OVKC*bxkL36zT&i%j-?MX zagf}p88K{Ici`f0EXrTGN4Q63Z{_Vnv6;epnUl@i0^W4)^7Y7>;O59=c2`QB!FOVZ z0>-Z68_O0@=tfHEb_E|>4x~%%R$*~tO{*^~`XBZG8B=i2l?~nzFp{zEnN(|v z4U8oOM7jf5{~T$_(g*o_R?ykAW5b-Z90QV7S?j$$Y-(aZkpU4GT%{;+1Su7ck{eGQ z>dx5Lr16yFH_boQOFa}*Kx zZ}?Po_VH==UQ()??%AV&tsCRe1}M8j!* z8kX%V1d;Xo&JfIPt`yB&_G`S2OMSWzK4A~hToD)A zi1gUJenH{B^wRZ({#(tr&=;F@o}P?DZ-m)tnMO<@yigL~Pn4To7?Ht&|rg z=~9x6$Pdb}uRDUda@_)5*Kk>1eA?%37DusMfmW$IIf*tTAGM*HXtP!w;qljph2WS5 zFWj-=v{HZVZ5OOKHo(2W$J*M>DaK)8qM&%{S&*c_bLc20vbO9xdI_Vu7_jDi1mARi z1ck}eArESD1m_F#?^P}-s;zyyINu$HLtmH%Cp-}l%1j(m@@=9f)M|41ey)zi$r?_@ zhjZRhJjhBuyZC85o@0ByMC`lOZrS>gnbRjcz?Rg|(E5S)7IP1)S+z+ne|Oa94?cT*+{b zf%Z%yiTWO6h7Miu^~8+EvR8`k7D;Obb2*vWlQz8#=S#U8;vPaaaAAr(mtlO(i=d()K z?xE?rAmuSp~i`gohS-pmpCmGYq)G+Mr z)}wKo=~~W6R)#FeZvBketo@?zA!X=ZohC>EFt(|b@TxoZ|4M=EEL%u>sq z#ew=m1xR+y`qR+ER~3HqLi`n{t{2OO%b|MizxZMeRggQp&YfrGc9U|y0{az%4xi8u zM=A#TT3U}lx*2x#Ry$ZKBxx8q&6gl^_6A~HOKF};4$MViQ~S^l)CksL$9c!b`dt?) z6MX{80KIRyO{Z(k#jT&D5 zIHjwn*BfdpJ-*J%CoQ^cZ*wBx=Hyqq&Ja=cSnsm@yRPJxF0`GY7}r2Sm^`1fgMvFf zx7Nh?fmK;{MZXJlzww5dbco+!?peR7)^133E;3$E0NZ?sGvzp43%4=mUFxSF@2)uy z;LD?gC3;bsWjSspE2S3hOIGwdxQiXsaraex6ylDi z&3r3Hf9&W*`!m!3I@gDGtMNb9>prnlY&jy)WX7flZH;*ke?;5ZSy6_fcsd*0b$(n{ z7SQZ?+xOo4^ULP9edU*s%%LQV*XKV%Mb8t&ZV$S?BB;_>x&wa5V^N8&*GgeEsRj`R z*xNSuTLlCvJuausxY}7JAttTb*H}?47~_PBiTdtmpZ9x*^@*ehpiVA+nHX%#DW*0T zQ?u`rSz$W}o9Sz!*L!j^oS3aj`KE1gYN<-F8TSn*I23}>auK3I%0Fp;A^N*4d)d}C zZ>^!M>r?Ii1J$fH$28KCs{=o$NbZm~Ml8RbZ;!35lnv=siuJ#p^;~t;vH>&_c#z~wFfZ4o60L=0K^)3=%j;gjKoDo$ezcmHDB}#Tv7oc=2tLBWdKu+#Y1DL-SJILTVsGYfmfha={!IZ6%0x}jpd z6_Nl9jkhp;)PJ^)CX&>QzX4-7|7E7|nSw1F4E#e%1 zvG4z^?@jWkw4;%cp5{PE2Oh?CTB-NlbtqT#hwD!W)DjjAi9AYD^e?e}O2k_WboB?O z0qKrFdOV(Z?!3?x!hg3eMMMSwvcrHH7?ZKoT^RU^9GG1G)Au5Zh&B;$-?Mh%q8{$N zOfkT&fa;*p5D-OIL6`eSHD=&a2iOVn6KM^s^O2&|vhr@WRTPMA8h~`K+Y*j@YTDw- z19fbQ*;fBF+Uz!<%}MSu+!LPrrSQ*LKs!s!JsJR9r-DUMd-eGyK58<60Ug8KfuFBX z4oI7{XGE2r0cG_JfF|zw?tg(UOh6Mv@AnFz>;f1^Kuclf{T%>{=L6FgAhhvdITz?c z_UZ#Ku=2lK`>%|MH3Lr_e&e?XHk8J%932~fVEqFy{x7PuXfOvPU*%?Kt$_R{V0%=` z0V!PxP<7DzJ;`5YrbAIZy}rkvTf4KYpr+K>GE5=800Ww9cyIv8B47{^7JYD64X|HHZA7$k z+?|Lv7oZ-fl-7_=u@MJ^o!fn8jr?~bijIP9GxmS&4gY(?c)0?$k+1kxZKA8C>&uyO z3Hy5wgJR@wLroQ%WS9u)J_)L{f38n0pa6rH;v-P=2;HC@a=T*2?P-j11_L)E#3rUY1ravKaZzsbipMl7~qD= zc-94enH+>&s&zydKpS4SqZeyD<*hN5KR#Qj40vtOoE_y!+K%l;~8T z@@(@#WvW9tJ1`Cz=)yu_&<~`i0y;zO9oQy5lH-AARbI}g^}PZM7SB9^NA_6*-SG^d zlK11?KsX%4!6CnZKi!I<$_)Yh(}PYM0Vq7_x!~Tj=vr`Rn+DKDfJjIdbl+z{skLY1 zhI)ZHq419;{V<(yKnMvAzQlNy4|K=`B$3)k3HIgqhGL3j5S=M2js{@LLSRV!*XrRw z^j~1-68`Zc3rG$iA@eb5XCcss0L_tL8>yx7-o$ShKpv97(VPc_2A&h;`896#A&18A zV4w*Nvxn6qk~&>riIRj-gyz2+eW4^C)?m+j$~Okq#Vtc%+2bp#!9Xbx`2E_Sz%$V? zr;YHE$-d+WAuyUSnEJ!e#CJfC6Cf9`_zb|da1YP;qE8->QB?__k_TgFlbek*gec;- zTjN`vOmwLKY}_G*8e)H10qwo;t_fHDm7-x=0iFh@& zG(h?FypZu)&HrdTxq?b4yM&AlEiQ>@o53$n#cvI{6dMu16#w1mscE(kW;BQ=F9}># zhyaeo?^j}K8Tf?(_5~BE_amxE!90c^}rF+YVTt~?{%Psd0hOEAb8S6 zwS|$cry6?VgVL4}c&OSo#TcwRdC+}O^`R0l(lg)`X)5IO?O&vtw%71+V>@05B&vfh z)b9(W!DR{1#hkQJ=32<+701CP@PH;NV^E1s1%^QFXr2xXXyrwE!==C?4>l`&IN| zKdq3w;c6O%hyhnh)C!}r_A6G77TXm15c#AOJMYJnB_w378uXAWcAHDH@cVj0TLW zLM+5m5vXN%QX6$X?{e;zx-ZXug=)H|ix4`M6U;MJ-Cm%_tUBZ#dpWf%=P+F`J73-A z9hx|*zxe$M`S5n43U$Awp2!=ixJuZpOOs;W&&qLSx)rf!jBfm+Oe7B_`v zfd^!|SBv$kK4!)5|X%b>TH-kXGG5?8U z;X9R8-rk>m3~MPn{@ylO67)_bmV4l_;pGU@In$o1!2-MJ*er>@vaaxnkwpKM6AJm* zgGbexlYq?>8h${1sV}tCvv^xMKKO4M%t2kW&V!1Q)%hD}tLBq~?cLybIVR4jMy0)> zaShfOSNaGip4+9i(QR2(X%C#v0zMnk#8U;vx_vhluJ2Xttf*K`+6P!TqqE5kGd$Ui ziNlPI_O(uBywbYP<)m^?KDIsLphf;|9ztV`Jp6l|Nss;8y5evq=UXk>TwNkvn7PmA zA1!I}1igE?^ZTn8`JL0sa+j&OtYvFBXP>dr9^QU3{em>A!`+RcTRkk#XLbFfAWyVEx=d^f4X3;)pcTjBG+$Ul-X0F@1}P&a2Lg7;8xDyU-Nk2baQZUa`-4!cT01mRPfc z%G%Eg663?_e9@M3`@_j+Tl2a8yrJ1oe2FTxA~auMnH!lckooQeaNUyy6H7w_-ut;M zrw^H5+|~7ZHv6KnFZ3ZX8|#zHK8Y09*IpSuX5b(T73Wa7dO(ruVLhUg%KLj_ zv_9LuiCIcZk1k3|^n_cjoLe1MOG{2Iq2nI>etS&_V7JaT=^1(tF=> zeP;WfIL&q9zLHD?9YibjkzD4pK5a39`_ml+z5Hg@G|NpX}q zRD3ZsFT4#I71pBIwP(rw!D zD4zE#E1W8`Fb`MOyFFpP4zsaS)r*MficFOkSL!~%O_LFlY{J(=8${#6Qv#LQgX;WSLd@##cQ z>teNZu%OI)Fm{F2htRd=24m_oLA+Jlzk8w;X0 zoF#`dArmzppkHivhfTR^;|XGRp5JeLrgJ+HB`D(`w8@GM%BbVJ!xGlGD^AfF{$>c$ zH=eM3hxa6hRPRSSGcqFA5S&6sfZ8S#UHswm!;K#*VPM?-wrr;g(VIqoT@>B6fP5RlQ%eD=BsQ zz{%O=t=HDnK(yd^%l^?}_f`ESlQWm;JLQ8yNi zs=sqfxh`;!S9-F!PM%EunFPp8{Kr$t%U)_XuBgiePz1C&aw`rimIW1K?k~gh{ zQwJ99hNsMSTi{}Ghm9$RgI7!6r%WkbFD)Z997$lAl$6;B89Vx|$~BWpzR&|dRp)e> zg`0oj^k44HO6QXH=0k#wnVA(-$AP0JE9<b%Ec#>O#Bt^-tK+gZ4VYJk`0Y zWiI7v7pSByeW)0dt5+&AJpS#*wfiUEsUS0)03?exYE&2NmdBa3miEZX>jU0e{j=@3 zx;PvE?HV}puqwO2wu`vO?M~jHmWs4;Rb-2edcp20MeYUM6W4z5io7Jr2{}EB$bu2ejwqBB3V0)<8Y36_W zz~@uGxlaIc6{ ztXq)#cB0rXLK+n`_(MI)UU~U+*PqM~KHn9zw0wF%$?=VaNOw}p`iS=YFtT&id~{qE zbLJ7bpw)1O1xM!m2|o{a5&V;5R{1|SUj1_xVB8?-&GU=`e2NSY_=N(WxLR|zqB30F zIaFme<}#yPZ!SxJ$E+KbG~&j4>un8FqmcUPNuUNl9L4)Jp8pw7T>b&1H+sYrBA(X3zZei+(i=yj%Se49+~Oyf`g`s%&DB#IhC zuBCtg2Dr=bSsvHqdb9(Mt*(Tr)JN!?MsJMzI>XoEmL`rbLE|h+)W`W-fF>utr$6&y+sA8HpIT2NSQ&zp@AJ|nWUUj^YDQs4(dw${_u*4ue z9v5V?44KdHgrYl@%~u|mUXMALkra@8w)$}Wh5c4feS*^TYO)`+SoWPoYgkKh=;FmH z?WvHa4TW)_(3JPK(B?d+PT_&5%~K-($I1tyNA=6IPu`(#R)b~j^ORO>=(z~^KS41B zIcsT5dgHK&Xb##9OF@x24saK zCro+fJw~rdbLL4Ij@}DMYLv(;4XVKqV_WAMrV$4^vxg{i$gRdvdgt+J$i^KQXB7X? zG$69Hv=@RG=OBZb(X?;pgtf^d{&)Iw%nT373jLCd7yEybmh4XvsC5g_5=Pmuc)PUZ zsjfO{ojL_$*0c`b>b>E1>fJ>=!N z_GACTp%<_MnM1B0+y$Om8Lvp`x3d7ha}3q<-Of=`e?q(fvRJ2lW4Y2>t$$LrUK0@N zg>hruuLS=;%HBK>>bBn>M}$;DSt9$AvZQWn46^S$=|8HlMj(ulMWC?lWnGrEPF7PP~t9 zGHCsBhyQSBa@i`yR!^F9mhshaQj%dVMlh$qo2m*~;J)#OS6r|3$2W5Oqt{QokGL&D zZ;99ZQe&Z$c4>DHKZpdVGt8t^_QUY4Kc8H)RMre$r`7Cw)Vg`4qo?i8hiWHBv0U+LZ^<@8qe4NP_i@}>9csAG0%SvTr#lH7 zsF6hIA#XOeNK{v8)M#BU%ooxcpa^2v=}p2DY)*7vVY%0;n<`hE7WE8kK~!D%C!r8n-^5 zRXq499Znyp4P+!J4wUzatzp<4O$;?~POVLHF4;Pf~A zpHoJLbe0G5vTwG}b!wpxkQq5%@2@%7O*_Lqli>OD8~KOs`x4qkkME5rw#K4ydwH_|)Rh1q-zC@aM*1=&KHL1y;m$Ngg?LsYc{?^l z>ua@6kKXFHLzZmhp<4G5V=ywe-;=pw^C1E-q_-^KzAC(D*^B$bC}GjbDmvw{q{%6f zpU~uy#&mlxyjfH{!w7UinzNruR4{ae!CGBy5d$NQK2DzGNs|%d<(CM_CXJP3A;Yr+ zMrY`nC`YeQ7~8B5_%;3Fo3;^htVGY@0q+{$dg0YqtZw=bZXY`j?ey}vVommr+c`RI zALnEe&WPApXvcZs(T$QUSXD0tTzlc3XVoyW)||--AtOP3bhKDZelj+s<4w6GmhXd= zOQzNkKxpZZY#)=o=2lA7;E};!_h!3i(lqJ4z2$)8+A6+?+5$$WZE-_<4aNblu^bc(7DO)nV26cdB^* zQj8o?4;9`v2H)CzvCMCa?G>l*9@|Ml&Ej09?{0qo0;w5%8+tt%G%)b`a{=?6*Q+vQ zHpO>&l~OVHMas^@a*Z}v92Y+pEv=Z|ZMHin5unrUjejtcTqZt`z3Sc6XJ5GIq_>AS zE;KDYA6_N56@JSc6w7V6=N;C(EbHFT7x9bHewQ(MHZ1gjX;~S7MRh~T>oCPOFZ_Wjl^z)0dA=vIDCjz5``WkbP z?CzZipCAtsyXbQgVjz-epr2Ch?c72RgFh`ui3GA3y;AK9WU2PbZR9p&-}&l~eiTe6-j2~GJxj-)=N0}6E_tHgK?N@jfY6Yj#^*0aV%d+_CD z;!G*`o3x>OuiX8o^aE9Jtjvz z)+SG0$HIREYu>_b7e~}P?e3@A!5$x{t!ySVUq(klV)Wye8%$0-8}{@ZzS`WZ&85;8 z%%+g35jrao{y0JX((VIYprXocJE=13K&Lvdc*9M%MswbxXG{X;V~2fXHuTb$fec~S z$$d_DH6dG39Y~k)4NGR%L_3H19-G6UA0$p z^pUSs9a&|%LI?H(E^bNfP4LO}M7rxbs}2)H&;jnLn=y!>k$m5%=1lNi5-8Caw1A_i zi=C`i-LIuhd?YG1s)pgaPgig?`Jqr|0s0BpFjv`(t0f<{aNZ{!eO;zVTCLo?o#2YF zVe_2+a;31W*=}USYcQz~fk>4k@mLyq?R2>t^=rkfyD$*Q-iy zM7jYXf$~ya^c!KL)&{Er^iKd11Xc{G(|nR^W+fTiYW}$m9fK+iGP&o>&ez#SS z1PI{&)>JlysyH=p$>Y=40?_VxP_eb&jRer&9WVe%Zwu-no`76NSAggjnR#8%HaRHb zdPxxuI3oZVUA-QKkx}}Gu~o}V{w;$)D=6`@Ir`rZs3k?HSwVnk+HcHW01Bq(7#LR$ zRjRD}>n;Ga193pM&d)>P{v2RnsQg)*!XS+QA85K56zmU@GRcFvdRQ8JhI4?GIQ2R( z-aMmh2LLi0C|Jcnm#Rvt$o_ZhTA6v3%40qV&Vu9;xEbdG4s1>dTF|(UVFiB2Kz@^E zr4%a35m-Zr$X4-(UlCtD2nwf0fLy_LwNa=^5TNM}sfKv~u9Sel`!9h$$qc^Whug?V ztb=ay#xSDXcY($8O5w5vzYeJK4x|OZR5}T10YDNJQt{i67Qh|w=zC)ESyOTv|7)Pt zarsLwWHb`zfH_OiX#euxjGt7^o4CL(Y}Xg3^Phr%PPnQI2hFH3DElvC?PXnlfVLR{ zAqJ47$5{vr0|e^{aYRA#1do7^Mas&HMge}fQ3xgvBh&1-;?D&-CxzN|aijT9Mn=8T z@E$EUXo>oRaXL3p1xNzTw?G|VK0S*AAJ2iB_5B*#1YN9YY(Wuyss{s1p!Gk&z(0Bt z4S;z$Dp15ID4-8eXlQH?1b|Edxrz!95cmXu*i}yIFi`p`pgMu533jzt&NjDX}!C7NQFORi<5<{kzNFc4Cx_yc&9 z6O51r&$fVy&SnkW10Qq11{BmXA%N|Ap10JZ<*JvpAOOQ9u7lFA&YF(U`$O!Waf@MR z>%WXXKM{z#|J&*S3z5-Z(im`NmcQ13(Z~i(^0x8YfX+C93X@`ry`b5KAaLE3+0@_g zej2oxJYI&TB$VJ0J!6mSR(@82%`PVc0Plt>0pO{kxe^%Qo8Y$km~s+?ecr62naXh! zcvOmSVk!c=*)SMPh5C=GBJh+iD0%$;*&EQfI%&0WsXj?AM8d#8m@2F1I=EyRfJh8F z=C`3agcu|**Q%UEiJ9ItYk^0_C70a5_*{V2om`}|BKVvPd~ie!Yyuw~E?rN%M^+G| z5(EAa1nbR?|Eg37Xf+B{NfW^f8ZrWGq*@egAPSH%rj7=c6wOBf52LOzLkOIFrEH)H zLU4{FCrKT|b|73@4e;c7-s+ieeobVhwt~Vw7IaAYOrB*(GYy!czl_aKs4&X2Il!6; zS*4C@>nZF*K+)=%oS}Dr8OhQ0#b`*rrH(R3h?9f>wQ$O~8pv7tcjExO6xS~&4rY6l z0$S~)>K)L`r-IXh40x3caT(s<7kqJlorA;yYy!KxFsW2PgaHFMZ~hVD5J8d`)w5Yc z!ObS5V#@|(h?plsnMywr!8nC8xlI|7ia`hYzl?!x@B*-=^&x@hU*wEGBl$)hlJh%18tJvF*Z zGHwQTG(@ryy}{&48|hEc18L1zH>1E8z^!YbrDemf(057?=&SWvfqjVDwmPX!hPbvd z4oQL#fg;2MmC)ID4GiS!^O0gu`5Cb3F8GQ01Vabyd5Bl=Itc?l7D7a^D{JEr#St1y zj^RpW(iO^1)IIPl-RIbwzzq*DVZGf3(_m@vLV7D4nrP^dO^{n9LrhO}*MZLDg6bV^ zm`D{;uq1HuMfE7COk-$NBYbl-qhCv1ssJi#{J#s)(Sc2GDN~#d6Vu+4$ z=>ZvnT|y9?BZd%va=WK03)C$Kil1qD22n+}K_&4vFdr2lPx1*FC zwMi6fVZf1T&OZtNG@=XCgh`CSL0#&21Mnez19oP#6Oh4yF&i+rCX~s1AWHO zx_xtfITVbuIGC!b<_s)&vKqLA_<6ly1CkQ?f8YC>!K>LE|HaajIpvGbzbn9_5;@=2 z?!HR^;x0rmO6Io7r0I6MeKq#>vG&HP#omO5xN{ha>G)87hMwahf+@ajIK`R3Bi9q` zy%M+t?kSvIz^JWAz{^+E>u=@_oe?OHKhahk>AX~GXq+r^*JWPYF?BaD<{ZXPU`hSc z&HgJlovwA{?mHytAPY{)N$o7?ELl3XT18>ASHpAwn<^$F))rZ}x*7dr+8HH$twQw8IsJ&B zIT#IsE}B%xAz{Z1ToHLtI=5xt%yq3b33ooM*|S4}MONFDIkE{m^pfS>+*@^<+;5t6 z*$;U}GtpWCsW{O565*~6Y8%J4F1|LKB}|(h-v)DVJ6)q9N0IBvW4Uckq-Tpbwrg}Q za09*RG=H>2H)30aU9fmkmc^5MlHJO3EnpfI;By(HlG^^1q)xyv;&eO0ROPFIPJcY+ zaln>$@b1b`Fe%TosMua%8JBg+UCdxawCDA2+OvniVe_&Iv9y3uuDN?D6=n00^Rdg` z#$T0?41_|34?fhhfTW%2U&d~{dhGS%#K6SHK3ExwJ}_lHTp3B*vc$(PjTBGqoe9;` zeR+%Db2*pmz6DaGf)NkbD{H*vdy=VXXOEs=6d*6!b5hv=CB%)Xhtg*jzof&>y# zSataD-WZERv27b^BNX!U#O!9yTfJ2U*JFy^r>vGBPjcNNs&&CK{P9I2gli@2?Q+)6 z=c}!&xIygG7IiYdE2FVOXUF*lA;Qbf{(WK3MLlt!nFU($Vrz%t!xvccxsgAy5poQx z7KM}gBDgta$D)4zbi}6+bdXS71^=~b&)6^E1~6d1F2Z*sOwx>-x9go^@_`x)z&VQF zJ~S#oOOaSI=2>W5WS>*qAk8uPS|nL(N>*_D%n@IvPUR#YAJ$oPB>#1_9_qH>BeAIyTR%ex`WQ)_s(CZ+rR`k@$nY3rRR|!mp8Z@ zcCzysIR^us3*JzX+OVln$rLo;eJWuDC8>sk1X8ePetqIm(q8JArx)T)C|!MYF? zhWfZCi;Yt)vitxvDG;VG0`fn{KwE5iG|Dc^{YN7J7cW((~u0MG% zjBt9HY~WdUU5UQEkFmp}y_NUM9p@e{dK;s5ehveRlUulIk;S8wJ@1X^A(4KjEOCNN zRUKg80<9=!i}V{!hfdT_Pv0{H1cS_Quo*8Q8q3dPwYF>>)&>*fugb}-S7%0d*Q-}k z1c(8+E{t7Q_TJ11<#w`d^Bc3JLbQIltjj_i#&hPc-fsW zST7b;OM8360o-J7aLF_vYxncQDhg?@4(xUYL6 z;iitPb>|yeoDS+m=1h6Da9(OU-e@}h%JR#Es27$%#N7|=lA%HQ!M_UHk^Wz!WeT3S-_U|nxpx&i${nf}|sykQx zl?wRBRIg+$1_2WHk!x-zThu4RO^07--|MnJe#>Um*zsI@jqaXq=1od5-f5wPQOSm) zFzVJ_G^Vfj=&;AYJ#m+(hO(JdEO)C%)4;CIB0A*RuyNWzJkIiB^3HqY9EW6Z@#-X8 zvXA*^k@Mk`lK7mlfVT7+EnOlRcbE)_7*&ze0-;;Shc?rGDJ~;wu}*iF7!b#KmYSmy z)mnXs;>47g-E%;)W8@q)oiSpW4IcWY%G=%WysaRtlAleaT5REF&Q21uS!N#Q`%-5? z#gq%`vgAJUxZEqZA*aXrhe4)Gmahk@tTxF_8 zRb7^-PrRXew7&P+&a{NHX>jf8eLnma;{_qDa$8k=Tj$NsaJ{~J^)CKFsnUS4z!H~; z{*tCZU*!@&5ByIqTQqh)?u*z32{P!ghAjJ22qD4(piab&7|&dI^P4L}S+=(*T&EKh zmS?MG2zyfJwWV_L^i#@lm{0^cT7;r0j`otWs$n^S!%Z2*&@1vvB-&UdH0Y7z^lod) z=!E;b_({bc26V`%yXq>_Va>ev{O+{%vX2uP!jULPj*y)=!{T)#K6*r!h?VqnBW6&% zONRH9sJGn+gKxE}x7l(NJ&Qph``?uX*NCLH`O%9sM7&=vxh+d(fn%)^&ZHi$CK*zps#gqF~D{5aWk9)18#0*iszP z`g-BW$JFJoyMUP5RZZOUBUldk!n%d*UPe~0eut=A@;e?8F4we2p+)Hh>ne3H$Quj307{e4zN z%a+D3ZT0I5!w{m4TFs1Yxazj$Sc~;VL4C~aMa!@5fl%2y0%Qut#dBr}%&uuAM=QpTiH4q*k%xkNk!wH8xJ4si z>b(Y>FSJ%?$M1S5r=?Ey_a_Tg)8B+xKw++y9STknJHuKe|tO}%0(*}*pEv|=#Q67~o-iRvPab+~l$ zWd-M3#psf@P)6GBBvHZfUxDr#u40~+n54$4>g513fin~)q*EPfM}sBblIF(8by6nr z=v}YF=?=H$z zkip#H2OgjLIZbDwk<#qtecYu(b>d|y%XI@@xDO_!1bs%}Qs1-CB^Ca%u_s>bZ1FO>Y)e^t2&UoN@Mvrjx@IA{;K`&_3&~YoC;#Z) zB!A)55Ok)^>Pzz;E-!5R>tk20HzA|m&z|^Q?$LqofGAJPgdOo5pLZ2oxJdqf(YTU@ zbMzSpYGk)}qfazuIEY!wtveqdfaKRpIkw>LOq}N~lB;;oGF2C0Zrna)1Ud4~%T{LicKCd;>8sZwNn@e?s zaAiDa(c*J^yB;y4HJkvACsMk_KSk~PQx6&8`hw*QW5>6&7hxA%^|}&|@vu+ho6DLm zH9W_=4aR1!CqkAM3&+Da1TIAEMauZiy+B>0V2;LNvWnXn;tp8>l80P^w&WpRH&KrB z#RVzul>|oWV=m?8JX5sO#FuI@n;No)sg`k=4eRHNrwW#B<0AJQIlqyEf(|J!sp^?p z?d`@JkrnNW_mg=6wyox`xqS4rZlR%QkFuO$2$`?t9T({tWGXx7U-PvM+lKv?E}o-P zwu3NQC#2+H_fz0V9#N!}XO{PV%D7Klz#gRRxHYMC{?blkzXV^ED|*j!u!H<+hi5IK zUZWntqC(&bLtukyc8)v$+zrfHkDYC!iiZ!6Kb${%d2sQA_d4Og@w?c590~PaV@>1f zIfnw9^i`9|G43jr@W)?Wg=-s;XFR#5vD1Gxz!6zfDd2I8rP=W>#HYxr-Mr|_bNOD~ z5ft`O%`C{z)5TR9sK@m{EvD|{Bm3V!q;Zs^$yMU@yTL%iK*Owj?^ylR^}(u*?AODq z5#Jtsem)Lx(anIwVP)&3CjN)3zUS`xw)3;L(lp5qU3tr&crbd9=(65`@k+llUiSgM zTJMd67nyJ6%)9vxz1sX0E#*$i)R8Z0{QdCl11IuEw7o0tR*~p&l(FR#K>_JI?eIZy zUigVoQUsDee7*1_Y;bXXd~$Fd@Nq*rx|QJ<&9x(%7O{u*eNAK$@S7H=vaYVItQ4QW z+|x|An^mZE;h*4I`N8ns-I|}bP~PyZdotrq*%6DTU3wJq`pRtbTpG~Q;IWDec!Ymp zD|r44L1w@HprhX1HqPuSfhS}S(NE8&wd`knKt8Izm~L529b<&4DodV)!C|vwM-v(S zFFo(>YUsWU$q_f%mUMO83fTL#fAYD*_tzt&bi+yAbfiq3{|Y|J{#-p7&#D&}9S&*l zWWuVeexUB>p(aC|m}B_ij&NGTa`kBL^u3kdgSxTg3h|}I!v2Z*3p?>zH4p3DTecZy zXM#rd)=!6J_8V^(A0`Xeo>FTu$iC2QvL?it9Y!%vB%B02F=GN}qd!3~&y9D*nqqGB z{q*=SD&&3Jt)~_7eT%ieXI#^UjmywU@ip&;NBf>u7`41SxrW_UilHSfIB+61{IUs` zJz34F7L@t;Ae_2%v{)kk=P2X2q>-HlkhQYgjB_n=Dgjz8Z-{@%0?_U7!^pQq4Wqd! z9oUQ2o4mWG>Q2^+vL2c>`giAI#$<)Cix-c!$TEey==^AA1f?~w`(imKk%sX{VebpN zwg!!#gx3VNI{gy!$a>J!?hXbHKt((4zDY5)0_JIM^M__fK>F^RS?CX~G}(IK!Hn%cl>T$NV$0jT`En+b)!!e$UWJBNHA!ObJfE!2ph6AIzKr1DtoFD{+s11WS>QpAPkR^u(2dG z-P#U*>V&8{dhlVvWQ(TAdxLA;3`pP5c4Iz07wWS@`uSX*zWCYrol~fXXTAJ1=WJ~3 z^z*MiH{c}!qTh25)Zh{;L z>vSEvIYi05!-AAr7PI1M7t=W@&gn+e6p{L66%TSf)mOm~m{NyEbEeCF?K-5%8dXP3Q>%K!VebJH5c#eqE(!6V=6wKJ{bWL6PY(B4 zp|kO#+z@k><1vlbW0E}FQ`2%b5_-!JNl8CV7X{Si;1heZp^g-B?_!{;i1LXaM1C-D zSTC-_`Vr*_|6pupH231ihQ$fV$SfhwN-nQVi|z>#+phdJdR(MB4m#O#HJzBOAl z?bZb~NNF^Fv@%rV_MUilPXDX8)`8X%-i4s$PRiFTB4ub3@Kc+g8O5O1>$1Go zJIY*pnCn=)ja<*oZNLH!1Z!^QomBrC%mR?Ti^;pW#I32O>zTPj*)747Z>2M)2CmM> zKgFRkV)c6B98rQ1}p_ zon*}le7Ah;kCSxRX=nNh+)U){;rTo?TI#N7$&-A#g@fT-(PzhceSPRfvG!&QE0I-C zw{E)eq^ddgd)lsJi>SMM^he2C$S$1fvVHLz=sk#?`d3t5I{uOrHwe>;d6;2eT+Q=X z_ul9Cle?m8`-ulQ_FoH|J%;7dAQ2H8vo@{e+u{hrlYv~+c{rOd|HL5kFJwm(zwcsf zKfN>fc?Tj+Gb7x8gz80~Gu(*j#0Xz0HTk@AD_-i=)MJM7Q*z(vs2xczHueR<@5(5+}q9m&8U4FmPdEB4qyw>5&<}E7~Rx=22>rL=N zII@y(@Fs{YCpA^FXR_CHinQi>3k~wA&?Kjqr>;6^c_{Bxy$^Uw&N{Kp^M}893mc{U zDp@Me{gntZZaejQYHSB4VmanxrK%lkgOxW4Obll2dRrDLg#a%_y~YSh<-RD-+!~B? zd`;zCc`sYelvN9-A)I&yi!W^@5gdnJvH zM!yEXP(V^(8&&|ztC#5P2{ILFjb*GRbtn&>^)EIYtFIEx>zltU8T8|e5gum4#1)UN z1iY5BjGp`r*N`I?z5vcVJl2yPlLKVUaOYz(Nqe=kufIIjz_vC%IvIJe>zr>bvH`$ z5_wr9GTPF%;B(^lay%9UGM98XxV^BgX%=ROiGf6AT!BOGb;~)t9d@WINZ9^5d|(`W zHIF)Ki5Ztl+p-Y_ytx$thzuHee7bqmlVw(Za#=7~`?JnZ4H$+??tJikEMMpR zRdjG`gDyJk;@xSYdu$>y$AaC~RPoejiGLAiMr7y&_bRdFXMggMl8N6Id8qP%8eRw3 zixR8DWt#IwJ!+6L(`B25OUXOl&I_+jFjf_0R0FKi-XyPXExwU#T`gqWTVcDC6f&pEm#sAv+VGG>)W+cGIu0FPlljZ*6sVo(fyy| zPvPJK=VR4b$#BZs7CV^SHG9xmO?mbOc#kBm`7HRJUAMO{8e1=#aslfSO_aOKP4-!e zZtW6t!4(ii8EFZAsS0tLs>qIKDgas`jr{CjgZ=gS+4rMeZ{+iSPW5LLo`~ z_zVmb-wz^O5hBCm0FO3@Fyk`PDkzRpgoOEqVTi~#WpL*n1|GHhx&&p7?ET*h-o0%Rqwl@ma*au8Z-6#A|YithiXwON-Gzm^s7{cXHG04DSy z{NRR&DMZcroEx;G@~(R01_?8SqHPha9Q?~z>p&uP ztVX7x1|mC4Av`9TGVUUX{mlr-VS%EH&->3_F6@1+NDgZ41R&wps3-`zf%NNp-zbs; z_~##1pD(1&yA8m-afFqNFwH0jaOv9FM+UV14YanI=5X0B{~$WBlZcYik`-9Lw+Fz3 zqPYjCsXH|*zv8dEfIB1M%)GyPs%%Qw9%C-1p2LjO44i z;U{N98VLx%v+OXdVfdSoQQMsQ=A%;FC8a5O(u)vs`uV;2{=+)@%8< z5Ml_xyGIJo>VH!vG$90sBnSnC@t+4n@>Bu=rB0v(7>^dw10e|Q@`XVP0XF;pccz2Q zCI~BXww+f4^Yw31UqJaVz6nmL*j)Kv5H#S90)=LU@;}lzaF#+aB^f({3iS3F)Y~r) zOu-=1LqHYL+?H`Cha^b_dxyQnP{c1w<-(pl$~U$iB%c6$Z8s#8@B> z&jq!ObC%Wwe5Z$}f+74LQ(I_D8b|x#q*y_pX4_1}>HT}56>Y!d{G3MOU&eJB_YIKE z;&%cE&H)%GjI+$911uV8K>b*iykrM83HI7|pV0R7`>cxQe82%$T2;6LX}Fvpq;2p` z1ZCQ|QX*nAGX6**uU9F94G;vHu9SmhC$@>rjS;y4RhtJZuAHSe!F<=9%M;=PS_5E2 z{$*tNoc~`Ecz}gW#&Q*$Q2)&K;Y1s`c>!|4e8Aa?M&btOYL>F;KE40njsBTrs~_u~ zkzfX{0KBq@!ps=b%K#wx_lN|*ks;LwhffvfQIUO-(>uAFfC4bm|CWhsalYW>s*@|G z_@z5^{M^^^3wVSS)PTTjL%<=xy!%z<;!EOY+A#ahO8IQq&ZCxZbol zHJGns(BPg;?iKLyCYZV;+IlZYVF4ml=+~n_wYPXcpZTACO#mlmAs9yMwwsFto{Z!o zp!R^curjU(>Vr66@}rf|xeKX1AnuQ(f%;xi6atP&>Z1@a8y7(s{^}3PjMb3lcnG29 zHj2HavGc7EEJy{=|EuQ$!=V6t z2<~6VdJHA;sJ{rE7!=9$!1Ka?=hlRPAtG`R@~Z%%2gs00`GFcCf}D|VIdqIcqBJWU z1H@lRK_-zJFsxJ7nQ5TNEYv7073E&^ea$18W~P*P`Z`dpo_L|z)k@v+9bh9 zNr5L_IHRGpbrU>k6x@2?1oq$>gu`zr6jHrw_5t3l^TpQ&|I4@$)%;*HJHTbQb9I`t z6p|uhKA#jWK%xSU$bTB&3K%kn#sDklbKty7UQ-+&V~YCWl2&y zUJNyIBc(Q!Vh!qa<7~l1aP!}da%l5)!bUA%b2PU@Gb&&%RZPJG$rnUrmDGj&W!$-D zkfxO34QgKRP~`*U5O;`;=fpk??Gi|w;+S#yHRml+|6aV_f8FT+&+b1F8Tk)E6tGE1 zO((#H|8FwJxH6MEf2{^xs=7iAoi`c(=5T5*Br7N_flepI3@ByHbY~^>eJBXJiijF% z1mn>F?G5EzGBpz2cg-YVGU)vZ1wr{-h_!J+#RD9OhLA)DNo3UV6O*))8eYS_Nrn=vY!Aim?%+^`)IJf z5+Qn#m;F4b#@qrtNp~jhE?DY^6qP2W3w2e};KZ!vVfM!Nh-8WOSwgl|Z?EL}RTWWuR;RG-(e@m79ljf&D3{MX=kr z4y@NuP+XKGEgp>dKfXL?-C{rrs9V6JGvsCl4E{yXJAqNF^ECXD`;@ovaYh2%#Liy> zFxBqix|Etw?|T$~kO00kIzyUzfMc zk6mU)`}1W}n!~9u&#zk=v|EMEmYr1j$dIsoI=wQYw++e2Dr`LTT?HhIt*^_wM9~$& zFXuJ6QBH*cE-_!;U{Y^R$4j#LfFJskmgNN7& zPB{=~&t1SH=F9ywE4F0(WHVfPPaC$C{u7uqyxqq8o~Y+ zq(dw+f4%E)L6E@C{i5sbLN(CrSN2%?NnRkNP|QB37JGx7k_&Y~O>vjK_!Toxe~hroFH?{g#~ASCfB$KSUGeByR7+1P`#@EhM*F z6qibP(Am{*<&ve0hdW-_g*i`=qF9_>?l3kjCWaJ@^-mS2jyPe3@v@37ZY$kkzO4L^zZKgRHtbC zP}M>t8Pz8zGVib4y)tPjX`X5;l#1amOg5LDzaD$v-G1`bBze#vols@rIU1lulzSf5 zsH%OW+{NQmsS!5I^=@|f>hcO@^H8?WcL$A=uc4H_^KGpH0=T)i%eH0*benfMb`d=I zC6gM3R|*UQGh^bEs<&TP@h*Icrfc94D&qM=(8;@v^|AxDsyscANOtMhJ%Okcx0&z* zk8ZkjS)fvEiNz=%e`|j$Y)D!!#^i;1EL+T@Mst+=np?$ddq`(Yy+(+5#^Uxack6if zec9h+c$L6nYIzm=((;apI<98$#*huwoFKG3fGBu<>j)xbbc zKWZ@Z?e&rThw1i01^YrAuRoljFxg;j|E^lVoPLy*D$r06dWy}N-C~EkCb8+53-zH* z_)|w0qc$h5p2`EcUx?XIRM!nPcDerHfH&&E$6@X*w@uh4npLlNTBY<^F!IKx4-~2H z{aRvp@&zb()^993nlnz?y2*KBFv|$P;k4e-Wb7J)IK8IM7RZB~yR2KD|6arU$HfMX zUZ0}TwMO%tfW&jISieYTRjkV>wU+C~XRa@AtyqX9z_nb&&h6usVS{C$f1ps)xC}cE zpVoiI6xm|+M7;*dM6&UQ zTVxCW5W8lpw==k&F1LIpX!AuhbLV~9-@Fjz&PUvMj&$tqnC!cisc<)Vsn;;Yjg~zr zJU8(@UG}9w3bo+KZ1i5vTwhU?&o!WUQ@l!=|0$XZyf9`Vp6y9DMoyA!-xaUnJLWj2 z&!5OmeEZS;Ev0g0-~8-wF|myuL;K!)>l-!xG8Sf0Bc`J>-Q@Z=z;Ro<)>$HyEYr{S z!F--X!seHnQFKCs;QQqfiikHVuRw;dTv`9lc##0UgCX1%3z&`en8)ZDaMh7RZCx?; zvW^fCcaF0~dEp|fXJo4au1w_%*S-1(37pf)2%TS6aMA4zv0{gQA3H6+3a%!*)EB}r z;{bkjs5O5~WQ+KTqeK<;3)N+It!=vCUbJN+WP61*ey}7w6?d-tK3CDt72hsW8(aBt z!oKC*z^nvW9q*@n7riBn<_dqGFDaX-WTogu^xgU0A}lh7>?Zl$JfsXo&d&Olb`GIf zgxu09tRZi72o}`89>;%FwPqPZRzQ89Y6iVtQOIx1N;f#1>H3^8k<+pSg%iQFE$!*e z?H{s=pKz`$ed$@sI1CUaYvm6Z6F>Xy#4F(aQYrJwsSCj33E|JJg8R7Z9n^$&X?AH5DV z^7?@}EIqTrHfK6wxp2L1Ul#`mC9iCX-qO0f2o-GM3fdis-eIyHds(a^-u;9bl{AZk zVggZv16$;5Z}y)=JAL+YEWYABL^j%)%ly|}0F^3FP|thwI+rdP#@+n7c~-Np`nBDp zKkQH*xaxL$TOP#8mN50DuZ{U1&zxAht>Z%r#g$?Bt@c~z9D;qsS%lMX~IADfv zOEIh*-fH5+vlxfR)j*Dj`os20we>M!`?y-$W9JXxBX$2ilGWR@$rxo^9?{V9(5F4k zl4_NIO>gJjiP7P^#s!vNa=M#0au7W2s|RsK)s;!4@Ihl1mYM00|);pj(>k&oQ*S_!jtcDwChq5TXN}(cs84zdO6gbUS zp|hRece}$fWyRieSK&NOU2;Upa=d;2q0c+ry3^k=Xx04i%EyRF`xbS1jb+pi3nHdP z`-A~PHt$#z5AHkbGm;YD4x5*!vfnPJm%jXX{;)byE`EXhTru* zR-4%KT?XM!(YJ_*h>qaG7x5U}d>WUS{%V@(QN+pPP=$Eh1+H{HfkV5Y?wp<<1&Jk5aE(`d)F zX=xaMu{XZu+DBw~_7hB9R`UWRt>G+dr{*=?#cvL=FUQQ3r|KP zivB)O5|mKi^U(M65`I+?zT-6Mu?4a+bQK>OQRtBsxi)dQ8mpdg>`Q%45`{fK=x%oc z-wpDSo)MDACX08PLKpw*+ZkrF$P4!w6H@ z-5+-&Cw4$o``IpetBVH|E#_WU-?HkNj@`WHQcKWd$(wG3 zukK;gX~oo|!|4Jugde@^-V|FfEqr0}IFt)Z={%9(tP!!;%(~_ML=7_P1~r3Uy^dco z(UZ|W>ez21v=QPP9;z%j=#(PDRaNukoAJqy z?+Tb4%qV$3l&VB`sAAQNH4m1Em{!0F6InMs>^KY1DRSpje8H}~ zQN-5{@iq^m?Wh~0?cL*z*!{ZAqRpmVUv+iqbhOW6*GwJv8N$1%#hUf>m*iqK^R9Yi z1@z804Cxrw(exu%Dsa3fp z4rY+{+rjS_OZrrtkSCHMj7jplt z2_`_J6r5oALJkj_XvwPZxkZ}ng<5CaQej(KNALhP=Y0KT%}eo~rlABM^U}ai-;$*9 z#Szm>Mk&FE7uwD@9oAQQg->L|N+Bv1IC{$DoaA@paV>0hW+q5AgqppYgR980mYLn` zYkd%jM~y!B<&qc8-s`ASN9CG-onF zAb!$lwocGFw>oo^S%ONkmTO0@X&kIu+=U zh{LbGvLTT}K{fg7vU_0+r%ctDL8QiD-q^#I+n+GGC9xy4Ca=x842@_9R0JN_G9|Rq zMq(z~toEE%YSheo$UjeP)Di{_OedqIZ#}WU*O(Y-^Pnd@{zx&+cX4N0yN`1z>hvgq z?O16%3U;;frh`gBhO)|%D}BbWK>b5m?@fy1kDH% zKWorRZ+81zfF?wl zLp4Yb=1$?=`sFDL&sn~+(Pd;wV#N`&y1F6^>mA|8ASuEu(}G?Bo)&_wtP#3cL?v3p zj6XRdXZ(N_Mc8hSob=Mb=YoiF5?+g0U1R3#=!l#J^{!2}FkelL)b%BT`w3hCKk9mz zWY@TG?H8HcdR@|riRCfS{u<-)r?P@5(&qD+zMRdNVQ>3Isgk2KZL~xz%+}}!ChoMJ zwV;e47FfEZ3S(0kG)T4 znlLUtB&Ln_WL~17{}paq!wZ%#k=SUn%7Bu*&aX&`nq3KACr#J85j7#yL1kn4sp&-Z zT0tSfSV;yIWW__-1<6CUwmyr^j&@@^@yDe^YndI*x7ype&kpS(Dn+{1L2TriO!%z5 z%JdE-@p4r3`48e5|63X<^%-~Mk;l4sAHilivMbZmcQ)HsYX-6QEhNv`4(JVyFyV1V zvLeAuoMX2=&<}h z6(`3bGOFha0dl43F*UPU`t}zD)kZ*;J&!t8e;H0h9d6jmmjn{lDJlJGgqK`?XW$4Q zCLD33+wtJ@uTgvORW&A5P5_P|CeeJD_nhn6n*%FL>4$dFSk|a2h5fwsKjqk`9jD?>Nb@v3vZOec=@tU-^m3#${A0WC3$)6Mv)9#M`sTtSx=Qo_Oo2W~ zZF=bW`}{AI@R>cjYu^swfUGZpOLiq*7#vK zE(yy^9OAUR>t#E?AlNih->03R{PUGBK64v#^i6=dW zyK+yo%f8yKMy3g4dFb(?yz$kyQ^qBDE+;P{-`?-!r_)C`I)EY4leR926oXv1D6IGXc>XE@o5l^jL_B zzUB7vx$v=XNrE7aYGS;|JvNTzOVN>A=!U3RZvKFaErI$d0g^?pPh0*^o}=5u3efMsuPVPYycv_?ycOLz&wdu_j_Ft2u8y-)|)P^;2T8TX8n%BoM4w2pjWi z%43F-*`J3lHHpU8d7vA40VvS$-^;KY^JY|tLs!ZE>`N5ja`-=jsJeg%b_OxH3%o!o zuL5rJKXR1~)x>bcK|<(gaDCmY zr0=8jgMyU4G3y-Wb8{7RPy8Gd`7-tlVADX68(bNmEnWu=1p+|gYMe>(5 z$Fabtv`AecT7eDmcNazTzZ=;v7p6{Wk3gs>q{s|^qnAMA?+vD6+|A47?q9}=k*8qL zlc9)VbmVd~=W`PO<)Wge(3}=?Ip!8+bi&`YnG3x=(YH2%LtWhPded-Sh>pb3SUk%b8zY@7rhge%c#E?Y-Zc|}5@#}=1q1h` z02pb|XZ)9!4B5y z&`kr=Vhw>fcYbVbKtc=6z|KTj1xB$KEEPMLqErT`2Mp+KT0p$<=^V|4U4K^ed6) zA_o6I@M0s%qyMk2?~H2dTi%8kG=Nf65Co-zGyxG%N)kmn2m%7qi%1EGNRg5tK@kCw zUZh2&OILacpcES&LN9{SA<`j~ygRP{@7}xCd+)cb$vJz^o|!%SoHH}e*p52$F%K~V zt}oCS7&BuVC|kh)xfkN!#Ow0d!$~U|6j_RJO2t|ZSd#Y{O^aL5Z4dzb58}gM{R=>J z1Q2zqxX6>H3%xbP3Whbi)DJe9V;Ut`ychM?IW1X3J;x)!!dy4!s+ zz+94mLA-dDn;8y>CxuPa?6UTOC%8&!F%XTMX9JQS2MpA*f29C6FawQ6K;I46&71Rf zQ)+e--er6Qay<)pU|DknU>t8L!ScUY?x2~4i#5nXItA5G13si0c-JMt6Ney1UP?Iu zIAAfaLA!xRl&G~1!_3pfzYq&xoQ~Hf9AHRoVDBiA7z#eSqym^}m>Ko#khwd80D{jI ztiCIhYIqQ8HFCReO34MHti0k2$_fZ%0%~;0q@gR1Qc}V47UL z4H#gfxClvk3M7O{a?>^)q4#+?4w$htnCXAJf~fD%Eav}%tLO&d{kJwi#Q#6G1%XGe zqJYkbMUWkM{&LEB8+>X0Bw*k!82!EESIV6<6b0V0Iba9tU;yXT5|a%dD06wuo1Au2fWdq@*r;kq@a9n z)_dXr^-TMpC?fwri(t{sBg8Z>N{^ukN|uP@l&1^xHA)pdpQ^_K)q%OI@D5GuQ(MuY16+4oar$^-n#KQaZu75}H|F$lbV9}zSLXyY8q8RedzITiPxQ@yZUc)z_U-%@RrEwq$!tS$p*$P6A$xb&ii`dF?(}j#U_ZHUNV8_{RPL;whe4Z!|{bOd4e+U zPn;k;%@qkASSRI9=X_*sAW9d+d<%Xroa2-m+D>-a9nm)_YA?RK=RgVev`?ZX&EM}RD0|&KVyfg1X5u&L1CgaTj~6> zWHSoYeaV|^H$;cw)LdGVa}QpLpK$E)(r|s~_1kF>?*9gad3i%0Hz!}Abw1Ni1bB|p znvJI*`HFl(QOO*7)qk+RmJRl7w+XX^Rh3ES=H%yY!Y}iuMsu>g9?Xo6WR=S-M+{Z> z<0)te-C6gIrFSS*QSPzl{fEs3r)FmN6UE(7q**m#vZj9mVI1C3=@0srWlwF$qxCzYBir=xu}}};w5o;Vol3Js z;(O}>XlT|twMbDY&L_a4I1&--cxLNW#|!wUB(?jWeCT|a3%CMK1uT~}F7XlTrBtg_ z%KWw6z3p_C4)>(WZY1TZ%;!Gm+&Jw@VihN}AO4BN^SkSO^%nI2*}pP3HnHsIyUjnT zJbl%$+u(5+gPGJls6L?*elxhGMm%EWz^J@>&~PqS{moS0^bIZ>?eK23dH)*@(_1o! zy&?SYhR9@eB_a8-=U>x!`H zmm%BuY1${T$#p436#=K@0eJbUa@VYfYo}EwJ|_=+JbK5q;JFo~k`eYq9W;{Tva{k) zr?>u!54N@6nMsA=Nb`l+R-%8_D$iaR|Rov81O zfI}4s!w1p48nf5(s65W`>q)>`N#lrTMG;G}<7B*;GJ<^uj%nwgh_Y>kZ>tHB{F&+E z8|i(g+GQ{^8uV6K55`Z+W-FF^%s#%B&bW_9>u(ENjGolY`6ZR zIv3u6@fIcO$N1EN4>b476(q{Ygl#s{xFVAx_sL?$Mss-1CN5a!yr6arGu+hDi_UX!X$?yV+KJOHGW^H z3EP$a^E2F}>;qx6+u_2^NqQA}21_{=+!LZb9d6bxCaCr24;z=d_da6Ug^{&9dbCkr z-5Qn^bP2ikkula?^%a|gO#WBTzOR02+!ItsyLRJUB;RZC#Hka;XSzOB`;Y5mM2*}? zE+kR(`d~}veXcgbJ2?*1HUTNY2WKX63r<=dKXTW_vOjyj)YdQ6=OAx$q5nM3ntjIr zvphgQUzmHbsqylXFamW>RKZ|j?a%zODD-1)wqB3wn=9_`h%)VC^gkyA^EXT`H;Z=~ zRCmYwNF%CDChnPyMB3gDYS?npTGIDuYn%0nBvi>}f4puw)R*&oH)|8)6?;~U&L9qd zLStC%rEm&dp6>29fOkL4pz$IO2e18`;iHGbX`Z*WTguhs+^F(SsnZQR)~W=)iZM{Q zInmlq!VBnT?(UTx?oM3NvvfX)MFEt3rW6f2LR3T@;g*lZw#5dt_WsP=0^b}Ik^Sr7 z@bww}b-ArE@R7U@1dJ~_EppbVrGId}H%E;2Zt(C(kp5+=`9-;fu#aCrkIE2TQ6vMF zqT~kB2$*H)>fI!Lc^6cc#8s|x^Tu*F*XVD1rTjlSB763Ar==oMZYtlCUe^q=rB^=H zLG9fbwRfvgm)fW?H%VkSqOGxP#4B z;lYyYYOa^F(Q8MU@a%Be3g9G+R&qRs_S_6f0ZtMz8CMYx9%{#sFdmFw5wdPpeOwz~ zPzMtB8)HiHRWsDQY^Kp%pBWJrHxB^N`G_y*cu~QHa;Sf?n}PIYpXRH|sR{ahV=a3D z6gvvw{(Mq77$VIJ8v=-KCZDYP(yg4d;U~A^MU7^=OqywVG+c`N2_y07LM3CfLjAcP zECrV*gsp_flag`|d@WojNKdBrCk9#fJ6Oanrfbk$q7lh;03|>v#j;Zm5Go4%*bsPn zv2DK$q7{?)=Z-()G8%p_x+~57(vt&bhv!GBHyQXW{PpsfmVM6M(f6B~Gc^C_TYyQ1 zTCIuA$8rEP%u99p@?vcpE8DV|^(HFl)i`IbQ1rfHvZ4O(q~Dt!V^3&t7CQ^cU1?>E zPd9pm@Rbi-M&rA-85|A#e{Y@@kL|CO@#;8NDD`N!jV~zjZ*K%AOA?&*w5XTxMO0ke zIKd0R%7?_k?~WOudbUD8pKlG5=EXY>he!v*Z19m;Nn7_CpRWMuR;#E+@~_Mi)sx(< zi^1e6tI@uG`nP!O0%XCe!}Kc>U~Rya+tp{8m%~|gqcv-(@9UEaic18*U1q z$<7m1K>V0$HJlZT?9OcoW5H^7l;kUrd+x8VtXOd*jji40ynU}<*i=2-QH;3af_}Mi zTU(oGMlP<$w;tutu~K2_(IiXQD=x0Gl6Ud1Nu1`hyWJW%yd{dOv&_+#vHn7fXT8vp zTL!4EwrKF%a#godN9$Ws`YG{Ppu__$yiq>}`OyowoA+jZn2mY7wymILSdGeO^avvm z!dQ$=v#s}T{@&-zO(zI_6;)A?aMk>^nA$Ygip`5o6g^@k?4xxxDtIWoJ zp{*$0UKyXl8V6%|k#D8&c8=bitUoGC#1ucPp^8fdXT^}fUP%JN&70jTZhHynyInGl z^Td%kA2nekt#OZh#y4=O*OeArgnV<62I&prWC3yX;4~;8FPMES!-S3O-yMz7S-dT5 zv2a^UX}Xs^A(g{#5^tVZ-*?u3k8z{d0yV)AO!sEmTp4}UqLgI*61m&7d2jTcxK(9t zXRDF{DQ@*(pot~6|KOq*Cfq2@L9@!sXpDBDR!yg8ab0Q-{gDO*yGH+oQd{kD#jK@j z+&;R)yEK&L!N|zc*nRY{{Em(L@CVE(uIU>8%BQ67LN1lo7V!61uS4KGD^~qKhBYU3 znl_dzANGBMmxqkme>q35bqBo3g?V;icH`tiwLNle&QM!p2GF4N*asOM>) z?tUzJ`c`IJtx|(U@>&Y#8=db@xbBsXG>m}!GS}5dsb9bEakON1IWp?DibQ-!pbr*V z`I$6g)aW>Lx7m*ZigDpdgOTy{< zR(Yyx!}#2|FTgR;RgJ~f&P^EeZtyMo6wB+@H@*h2$!6f zZilN|322`U9?QmenC~Fmzx$GJmG!h!vGgCRXrDRu_LL{C^|n2W-!hZKyIAIu8$4vu zK73sLEm z2iHJ@g!pG&xnVz->D~?(4Zt`5JQIqo=zP?I0ekIoNNv)|!|sK3g#q-cvce7zVgT>4 zKkV5o()^XCu>3*jb=>?}kwyp3{p(wUd`NR`f`ZCU=h5fkr;hva6*MCyPWhAfo2O$X zXhPk%d1Vv#%YAlYb`6Jw>Xo$--fGPmzibHd?>!XFBfL{+_EP)vLu`9u=517B>l0x44p< ztH-k)p^P$sYq!gBtr##_728fkZ5 znW*RcxP|fDZoP>PaeuYa`vEjft1ZNad!tSD3LR%sxeN+jIKv6u$env3u9x3O^EIfsw9Oo7H95_>Bf+!tG{1s+!n`^L3=3%Y1 z`+{K)M7A}Im80dE&?A_i1e9g1wuZ$2XwXt>fgLw!p*Aa@ctBvDDv*FH>JwTP5+=Ml z-4*tDr*mS);gu8AOH6%xiu?KgtC;KsgQ5nGk$&`IL-sPEx5%{gF%_=zr}m_NopUqfme_6gSEqVUp{ zmtc{(zOeX;d`uzty(LOLdC8F^M4(t!E&57m-LGkl9$uk$hlVre=|3CecFu~r6lO=Z z?8N(rTvvi8L=DihCPIT~XHswFT~{a(@&R@N$EwvASL)v6<jl|3hA%N3amA6 zh1#RkUtN|mLp9(G%dXTJE;TA5gtm)pRWfesqq0&GkiR%*qDIA8MVG~nenEp6%2MU` z@ptOG<6D-U47?NC%kzCbvl-XQh%mC&cK})x6&TUIqfZxAwLcVVMUv4HM7`O;fOicu zlvZWUby1^(*z&nsc)h~Ms4QFvl?uHS5q^c^o<;-~jtF{gxM+TeFU{tE&^HsByTYA#jDRHgO# zfm&v`_O*t{b2D(;>vpakrfw*V>Er4Ln8nX^+eQ7mR-Yei(0o_^XpP>KINGd(IiGbi z>l3>E<29aEo>Xr6;%otGaFs+2MKo^qR|o}wwg!7G$0U4K+S#=Td{f~GWXT1Ca$ERr z>Kf>-$tJw@MtZHrwbmR(3<42jmcwGYgpf`-x;ZI1z{TTZnyf zV~y0`WmN`RhA8cg|j~sv~VYWQ;IqCrcJ}>x!^7Fy9rOKua643 zJuIFl@AvObkf9sJN%hvu==&3PI~(x3Od8e2X&RPL1=NCB*}KR;O!aSL#dO~a8iyJv zzFBx#z#`5?bR@R_iVhu&T+xl6v+I(YBWZvNb3C!j7i&6c0F8Zq)n*c;{*fI9JIn0k ziY2KUg%lnwW3KsO&&)B@pK$vesw^~-AkF3W8KuV`x~PTcy^+_$R!f)nQz4|aO)u|f z-8?Sl_0cZbabxCMWWXBx$|%>-`p)i;9%qk^vRu}-mx0NZ3(0)ty}s}L(W+X5iEzH^ zgw>Ur^qIp6=}FT`AKlENtxDy^RcF>h`x&rc3rRhH;40=Hh1TJ`sO|y zCy)aUx~g^|ZBTbkoJ^%OUFvMLaj9h3_7}lbwYtH%iKHD;jkh`Xa^gsD-klA{g`>?# zW4ia%a!Ri^GR0@Zyf^n)ao^9IQ2!|*AI*$jEu8)?Lu~U!DlV`uB|=gV(jcpR;j6ovy11^kN7z?%jcKAd3J1UEHns&IN{XVyWnI;mmPfl zQB?~cL*(LVqB33HuJ%m(mZu_Fn`lGQ-kF3IJ`rE_)#m6h(&2Fh zFV@S!J%k)I&QXw!%70lyNcFEbUvl_RT2ZQbs2NOf!m@Dq8unLD z+KH|a*Vfi|9KE*FA2&wUv@49x%%dGv_xqdV>87tkYG^c{2ZSPS*X*SE*GqpEAq4nY zr!_PW{TdLT3s3bZ)W6&&pr`p(-P-j~KYmx#nC@HkA*~BWw`y5&&ctlCAftn9Ox zsUY-|s+sAXvG7XI$*p>b%>~z`ydJp`!jaN;_+gjX0*0=z_mk*M7kzrC z@kNiisHSA{$l=ZJSC*epW4A6$$1uhkiADuE9o<_AI&m+l>3mjqrN-M6)Yt)KmKAIh zHw1!;x;Z*K0GaLL;VylA>HIS)tN|2C5dI;^gX9hgZ2FercoG7=&Fsiudb3q@j zw$@u4!0Mwlv9u<~spTGaDyrjV8nph6CkMhh$Kq;pn)`mr*ZC>akNrY3wW%YUCmC(Hb&C2P7 z>a{?OKrQbx8I10P2YMNb1CB38wlZ@|vM`w#oN5P)_z%(-4wJs<3um~I+#i7|PdZ=Ob^;DKs_K3tz)d%XIA$cPYas-&a)F4WO32*$4dH$w--{h$g?9 zqSBZc5@SBp?@nTp7YgS`x%!nyA4Vs5{Uoou=<8QI5 z2O>{1nP_|x!YPXV87To?(oIdkh7bkXqxs?rfi5!73zsh5sW=URh5=pi z79JNszI`6pB$DB*{V$TZxb=Y)5N(oI6$+w5cgq^nP;~Id+y9^!=RjB;*3xo#4B3bB zmToI$$WcR}ooADN2$Tx4G0-yQ0Sg;Q#a0D65L<|GRBJI7?w zU_5jL^W^6)l^4k=dUd&*d_-eJmZ?;Rj046VYC7>jN#Lr(!661(Pav%ct%X1;>HZi!W!uShG6UO%O4$ybv=m^fKs$JsH_rc2Aa(1}fb!x~c4R2}X z4k4C`8gdS(gMLk?!D?y;aj;q-TihJoG(t}B4FdnM14BN3OPPuL(d)n_`SxKkDC|BH zlL0v1fl^k8G2KIv$BO(}s* zeja~PsIbSu-^Wxw+yd+ZHK%;uXpBGxBHgL6cT^T)yh8kNT_zO-A z83405YufNMvOEfIasVg7@Mm^v2n*PQVc~X8z=eZxa7wAn+>aufa_-R(1O?MzOnEne zq3HoIexx0 zs!oJAov$Hn-+n6v+_6csSYp!`us?n+jw)c=pfBhP+ZOF=pfGK41N1WwHN*?BQoCg5 z^$*F$0aDqQ20UnSmh7#!R$vS_*OgbuCt#+!m=Pvy08PTNf=Q)Ou!;yEd{ugfVW=uN zU-*p1(`m1Qv*5hA`Gh@31d;>X5g~t1fEpqP{DSfyU!AM!MYn4` literal 0 HcmV?d00001 diff --git a/common-1.21.1/src/main/resources/assets/antarchy/textures/entity/queen/black_hole.png.mcmeta b/common-1.21.1/src/main/resources/assets/antarchy/textures/entity/queen/black_hole.png.mcmeta new file mode 100644 index 00000000..78df88c4 --- /dev/null +++ b/common-1.21.1/src/main/resources/assets/antarchy/textures/entity/queen/black_hole.png.mcmeta @@ -0,0 +1,5 @@ +{ + "animation": { + "frametime": 1 + } +} \ No newline at end of file diff --git a/common-1.21.1/src/main/resources/assets/antarchy/textures/item/critter_cage_item_overlay.png b/common-1.21.1/src/main/resources/assets/antarchy/textures/item/critter_cage_item_overlay.png new file mode 100644 index 0000000000000000000000000000000000000000..aabed591b5e4a05bc7b82cfda38ef018488c7802 GIT binary patch literal 267 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJV{wqX6T`Z5GB1G~mUKs7M+SzC z{oH>NS%G|^0G|-ogoK2KhK7j~Cx(TERaI31h4pxMu>dKyk|4j}{~*AydH<6AKoQOY zkH}&M2EJt=%-DWsWj;_)qQo_#Bsf2xP`XRapn@;z zz|>!_LM{ZX%RE{AjOX6Qaxb~JT~YS+k){k;UM!ujj3a=?GB9|$`njxgN@xNA3-DG| literal 0 HcmV?d00001 diff --git a/common-1.21.1/src/main/resources/assets/antarchy/textures/mob_effect/commanded.png b/common-1.21.1/src/main/resources/assets/antarchy/textures/mob_effect/commanded.png new file mode 100644 index 0000000000000000000000000000000000000000..b73a5b56db39d7609a8b1bfd554a817bf682749d GIT binary patch literal 312 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|K6$!0hFJ6_ zCrEs15S)HwnaIKaCu;UvzAs$Wn%$6jYl_m3|Nrl^|NsBL_QU`8*MI!qzpirO2}S91 zEW%&rR>~bcP;dWAGR<(RQH0Iy{=}KzUF>&o&n(#a;{D<47O|&~T;xdKtZ@6pfddBu zRvlz1kGuNJori~K5)Y3Sk4BNfRt+`-AgBR literal 0 HcmV?d00001 diff --git a/common-1.21.1/src/main/resources/assets/antarchy/textures/mob_effect/dilated.png b/common-1.21.1/src/main/resources/assets/antarchy/textures/mob_effect/dilated.png new file mode 100644 index 0000000000000000000000000000000000000000..d8527f963cf7cb30d1c109b6000f3d34c56f0700 GIT binary patch literal 227 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbL!dVo)eE0F&Eef!_9@&Erb{JQ3K z<_yEjjiPs^32#af^vt+n6}+ZYgGoY6Z2CTyJfKp>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.QUEEN.get()); AntarchyObjects.setManticore(() -> AntarchyFabricEntities.MANTICORE.get()); AntarchyObjects.setRoyalBolt(() -> AntarchyFabricEntities.ROYAL_BOLT.get()); + AntarchyObjects.setRoyalElementalProjectile(() -> AntarchyFabricEntities.ROYAL_ELEMENTAL_PROJECTILE.get()); + AntarchyObjects.setRoyalIceSpike(() -> AntarchyFabricEntities.ROYAL_ICE_SPIKE.get()); AntarchyObjects.setPrinceEgg(() -> AntarchyFabricBlocks.PRINCE_EGG.get()); AntarchyObjects.setPrincessEgg(() -> AntarchyFabricBlocks.PRINCESS_EGG.get()); AntarchyObjects.setRoyalEggBlockEntity(() -> AntarchyFabricBlocks.ROYAL_EGG_BLOCK_ENTITY.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 2a31b6b4..d019edc2 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 @@ -146,7 +146,7 @@ public final class AntarchyFabricClientBootstrap { EntityRendererRegistry.register(AntarchyFabricEntities.VORTEX_CHARGE_PROJECTILE.get(), ThrownItemRenderer::new); EntityRendererRegistry.register(AntarchyFabricEntities.WIND_VORTEX.get(), WindVortexRenderer::new); EntityRendererRegistry.register(AntarchyFabricEntities.TIME_DILATION_FIELD.get(), TimeDilationFieldRenderer::new); - EntityRendererRegistry.register(AntarchyFabricEntities.ROYAL_BLACK_HOLE.get(), net.minecraft.client.renderer.entity.NoopRenderer::new); + EntityRendererRegistry.register(AntarchyFabricEntities.ROYAL_BLACK_HOLE.get(), com.craisinlord.antarchy.content.client.renderer.RoyalBlackHoleRenderer::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); @@ -172,6 +172,8 @@ public final class AntarchyFabricClientBootstrap { EntityRendererRegistry.register(AntarchyFabricEntities.KING.get(), com.craisinlord.antarchy.content.client.renderer.RoyalBossRenderer::new); EntityRendererRegistry.register(AntarchyFabricEntities.QUEEN.get(), com.craisinlord.antarchy.content.client.renderer.RoyalBossRenderer::new); EntityRendererRegistry.register(AntarchyFabricEntities.ROYAL_BOLT.get(), com.craisinlord.antarchy.content.client.renderer.RoyalBoltRenderer::new); + EntityRendererRegistry.register(AntarchyFabricEntities.ROYAL_ELEMENTAL_PROJECTILE.get(), com.craisinlord.antarchy.content.client.renderer.RoyalElementalProjectileRenderer::new); + EntityRendererRegistry.register(AntarchyFabricEntities.ROYAL_ICE_SPIKE.get(), com.craisinlord.antarchy.content.client.renderer.RoyalIceSpikeRenderer::new); EntityRendererRegistry.register(AntarchyFabricEntities.JERRY.get(), JerryRenderer::new); EntityRendererRegistry.register(AntarchyFabricEntities.JUMPY_BUG.get(), JumpyBugRenderer::new); EntityRendererRegistry.register(AntarchyFabricEntities.SPIT_BUG.get(), SpitBugRenderer::new); 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 be1ddd37..43ef66f1 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 @@ -512,6 +512,7 @@ public final class AntarchyFabricEntities { () -> EntityType.Builder.of(ScorpionEntity::new, MobCategory.MONSTER) .sized(1.5F, 1.0F) .clientTrackingRange(8) + .fireImmune() .build("scorpion")); @@ -526,6 +527,7 @@ public final class AntarchyFabricEntities { () -> EntityType.Builder.of(EmperorScorpionEntity::new, MobCategory.MONSTER) .sized(6.0F, 3.0F) .clientTrackingRange(12) + .fireImmune() .build("emperor_scorpion")); @@ -613,6 +615,7 @@ public final class AntarchyFabricEntities { () -> EntityType.Builder.of(com.craisinlord.antarchy.content.entity.ManticoreEntity::new, MobCategory.MONSTER) .sized(1.95F, 2.0F) .clientTrackingRange(10) + .fireImmune() .build("manticore")); @@ -678,6 +681,18 @@ public final class AntarchyFabricEntities { .clientTrackingRange(8) .updateInterval(1) .fireImmune().build("royal_bolt")); + public static final DeferredHolder, EntityType> ROYAL_ELEMENTAL_PROJECTILE = ENTITY_TYPES.register("royal_elemental_projectile", + () -> EntityType.Builder.of(com.craisinlord.antarchy.content.entity.royal.RoyalElementalProjectileEntity::new, MobCategory.MISC) + .sized(1.0F, 1.0F) + .clientTrackingRange(16) + .updateInterval(1) + .fireImmune().build("royal_elemental_projectile")); + public static final DeferredHolder, EntityType> ROYAL_ICE_SPIKE = ENTITY_TYPES.register("royal_ice_spike", + () -> EntityType.Builder.of(com.craisinlord.antarchy.content.entity.royal.RoyalIceSpikeEntity::new, MobCategory.MISC) + .sized(2.0F, 4.0F) + .clientTrackingRange(16) + .updateInterval(1) + .fireImmune().build("royal_ice_spike")); public static final DeferredHolder, EntityType> OURANWOOD_DEER = ENTITY_TYPES.register("ouranwood_deer", diff --git a/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/registry/AntarchyFabricMisc.java b/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/registry/AntarchyFabricMisc.java index 4667b59b..626843a5 100644 --- a/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/registry/AntarchyFabricMisc.java +++ b/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/registry/AntarchyFabricMisc.java @@ -23,11 +23,14 @@ import com.craisinlord.antarchy.content.worldgen.thoraxis.TyphoniteSpikeConfigur import com.craisinlord.antarchy.content.worldgen.thoraxis.TyphoniteSpikeFeature; import com.craisinlord.antarchy.content.worldgen.thoraxis.TyphoniteSwirlFeature; import com.craisinlord.antarchy.content.worldgen.thoraxis.PotentNyxiteFeature; +import com.craisinlord.antarchy.content.worldgen.thoraxis.ThoraxisIchorLakeFeature; import com.craisinlord.antarchy.fabric.content.fluid.AntiwaterFluid; import com.craisinlord.antarchy.content.effect.DreadMobEffect; import com.craisinlord.antarchy.content.effect.GoopedMobEffect; import com.craisinlord.antarchy.content.effect.GrowthMobEffect; import com.craisinlord.antarchy.content.effect.InvertedMobEffect; +import com.craisinlord.antarchy.content.effect.CommandedMobEffect; +import com.craisinlord.antarchy.content.effect.DilatedMobEffect; import com.craisinlord.antarchy.content.effect.ParalyzedMobEffect; import com.craisinlord.antarchy.content.effect.ShrinkMobEffect; import com.craisinlord.antarchy.content.effect.StinkyMobEffect; @@ -391,6 +394,8 @@ public final class AntarchyFabricMisc { public static final DeferredHolder INVERTED = MOB_EFFECTS.register("inverted", InvertedMobEffect::new); + public static final DeferredHolder COMMANDED = MOB_EFFECTS.register("commanded", CommandedMobEffect::new); + public static final DeferredHolder DILATED = MOB_EFFECTS.register("dilated", DilatedMobEffect::new); public static final DeferredHolder STINKY = MOB_EFFECTS.register("stinky", StinkyMobEffect::new); @@ -425,6 +430,14 @@ public final class AntarchyFabricMisc { public static final DeferredHolder LONG_INVERSION = POTIONS.register("long_inversion", () -> new Potion("inversion", new MobEffectInstance(mobEffectHolder(INVERTED), 2400))); + public static final DeferredHolder COMMAND = POTIONS.register("command", + () -> new Potion(new MobEffectInstance(mobEffectHolder(COMMANDED), 600))); + public static final DeferredHolder LONG_COMMAND = POTIONS.register("long_command", + () -> new Potion("command", new MobEffectInstance(mobEffectHolder(COMMANDED), 1200))); + public static final DeferredHolder TIME_DILATION = POTIONS.register("time_dilation", + () -> new Potion(new MobEffectInstance(mobEffectHolder(DILATED), 300))); + public static final DeferredHolder LONG_TIME_DILATION = POTIONS.register("long_time_dilation", + () -> new Potion("time_dilation", new MobEffectInstance(mobEffectHolder(DILATED), 600))); public static final DeferredHolder STINKY_POTION = POTIONS.register("stinky", @@ -833,6 +846,9 @@ public final class AntarchyFabricMisc { public static final DeferredHolder, DimensionalTearFeature> DIMENSIONAL_TEAR = FEATURES.register("dimensional_tear", () -> new DimensionalTearFeature(NoneFeatureConfiguration.CODEC)); + public static final DeferredHolder, ThoraxisIchorLakeFeature> THORAXIS_ICHOR_LAKE = FEATURES.register("thoraxis_ichor_lake", + () -> new ThoraxisIchorLakeFeature(NoneFeatureConfiguration.CODEC)); + public static final DeferredHolder, CoralSpikeFeature> ELYTHIA_CORAL_SPIKE = FEATURES.register("elythia_coral_spike", () -> new CoralSpikeFeature(NoneFeatureConfiguration.CODEC)); 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 14f681c5..f8012706 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 @@ -152,6 +152,8 @@ public final class AntarchyConfigModuleNeoforge { AntarchySettings.setQueenBeamDamage(AntarchyMobsConfig.queenBeamDamage()); AntarchySettings.setQueenBeamRange(AntarchyMobsConfig.queenBeamRange()); AntarchySettings.setQueenBeamDurationTicks(AntarchyMobsConfig.queenBeamDurationTicks()); + AntarchySettings.setQueenBeamWindupTicks(AntarchyMobsConfig.queenBeamWindupTicks()); + AntarchySettings.setQueenBeamTravelTicks(AntarchyMobsConfig.queenBeamTravelTicks()); AntarchySettings.setQueenBeamCooldownTicks(AntarchyMobsConfig.queenBeamCooldownTicks()); AntarchySettings.setQueenBeamTracking(AntarchyMobsConfig.queenBeamTracking()); AntarchySettings.setQueenBeamTerrainRadius(AntarchyMobsConfig.queenBeamTerrainRadius()); @@ -159,11 +161,30 @@ public final class AntarchyConfigModuleNeoforge { AntarchySettings.setKingBeamDamage(AntarchyMobsConfig.kingBeamDamage()); AntarchySettings.setKingBeamRange(AntarchyMobsConfig.kingBeamRange()); AntarchySettings.setKingBeamDurationTicks(AntarchyMobsConfig.kingBeamDurationTicks()); + AntarchySettings.setKingBeamWindupTicks(AntarchyMobsConfig.kingBeamWindupTicks()); + AntarchySettings.setKingBeamTravelTicks(AntarchyMobsConfig.kingBeamTravelTicks()); AntarchySettings.setKingBeamCooldownTicks(AntarchyMobsConfig.kingBeamCooldownTicks()); AntarchySettings.setKingBeamTracking(AntarchyMobsConfig.kingBeamTracking()); AntarchySettings.setKingBeamTerrainRadius(AntarchyMobsConfig.kingBeamTerrainRadius()); AntarchySettings.setKingBeamTerrainCap(AntarchyMobsConfig.kingBeamTerrainCap()); AntarchySettings.setRoyalDecreeCooldownTicks(AntarchyMobsConfig.royalDecreeCooldownTicks()); + AntarchySettings.setQueenBlackHoleRadius(AntarchyMobsConfig.queenBlackHoleRadius()); + AntarchySettings.setQueenBlackHoleActiveTicks(AntarchyMobsConfig.queenBlackHoleActiveTicks()); + AntarchySettings.setQueenBlackHolePullStrength(AntarchyMobsConfig.queenBlackHolePullStrength()); + AntarchySettings.setQueenFinalTimeFieldRadius(AntarchyMobsConfig.queenFinalTimeFieldRadius()); + AntarchySettings.setQueenFinalTimeFieldRate(AntarchyMobsConfig.queenFinalTimeFieldRate()); + AntarchySettings.setQueenFinalTimeFieldDurationTicks(AntarchyMobsConfig.queenFinalTimeFieldDurationTicks()); + AntarchySettings.setQueenFinalTimeFieldCooldownTicks(AntarchyMobsConfig.queenFinalTimeFieldCooldownTicks()); + AntarchySettings.setKingFireballDamage(AntarchyMobsConfig.kingFireballDamage()); + AntarchySettings.setKingFireballRadius(AntarchyMobsConfig.kingFireballRadius()); + AntarchySettings.setKingFireballCooldownTicks(AntarchyMobsConfig.kingFireballCooldownTicks()); + AntarchySettings.setKingIceballDamage(AntarchyMobsConfig.kingIceballDamage()); + AntarchySettings.setKingIceballRadius(AntarchyMobsConfig.kingIceballRadius()); + AntarchySettings.setKingIceballCooldownTicks(AntarchyMobsConfig.kingIceballCooldownTicks()); + AntarchySettings.setKingIceSpikeDamage(AntarchyMobsConfig.kingIceSpikeDamage()); + AntarchySettings.setKingIceSpikeCooldownTicks(AntarchyMobsConfig.kingIceSpikeCooldownTicks()); + AntarchySettings.setKingElementalTerrainCap(AntarchyMobsConfig.kingElementalTerrainCap()); + AntarchySettings.setRoyalBossSoundVolume(AntarchyMobsConfig.royalBossSoundVolume()); AntarchySettings.setRoyalEggHatchChance(AntarchyMobsConfig.royalEggHatchChance()); AntarchySettings.setRoyalBoltDamage(AntarchyMobsConfig.royalBoltDamage()); 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 1bc19d80..26b4c255 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,10 +125,17 @@ 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_TRACKING, QUEEN_BEAM_TERRAIN_RADIUS; - private static final ModConfigSpec.IntValue QUEEN_BEAM_DURATION_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; private static final ModConfigSpec.DoubleValue KING_BEAM_DAMAGE, KING_BEAM_RANGE, KING_BEAM_TRACKING, KING_BEAM_TERRAIN_RADIUS; - private static final ModConfigSpec.IntValue KING_BEAM_DURATION_TICKS, KING_BEAM_COOLDOWN_TICKS, KING_BEAM_TERRAIN_CAP; + private static final ModConfigSpec.IntValue KING_BEAM_DURATION_TICKS, KING_BEAM_WINDUP_TICKS, KING_BEAM_TRAVEL_TICKS, KING_BEAM_COOLDOWN_TICKS, KING_BEAM_TERRAIN_CAP; private static final ModConfigSpec.IntValue ROYAL_DECREE_COOLDOWN_TICKS; + private static final ModConfigSpec.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, + 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; + private static final ModConfigSpec.IntValue KING_FIREBALL_COOLDOWN_TICKS, KING_ICEBALL_COOLDOWN_TICKS, KING_ICE_SPIKE_COOLDOWN_TICKS, KING_ELEMENTAL_TERRAIN_CAP; // Royal mounts @@ -579,6 +586,8 @@ public final class AntarchyMobsConfig { QUEEN_BEAM_DAMAGE = b.defineInRange("queenBeamDamage", 45.0D, 0.0D, 1024.0D); QUEEN_BEAM_RANGE = b.defineInRange("queenBeamRange", 100.0D, 1.0D, 256.0D); QUEEN_BEAM_DURATION_TICKS = b.defineInRange("queenBeamDurationTicks", 30, 1, 1200); + QUEEN_BEAM_WINDUP_TICKS = b.comment("Charge time before the Queen beam actually fires.").defineInRange("queenBeamWindupTicks", 20, 0, 1200); + QUEEN_BEAM_TRAVEL_TICKS = b.comment("Time for the Queen beam to extend from its head to its committed range.").defineInRange("queenBeamTravelTicks", 12, 1, 1200); QUEEN_BEAM_COOLDOWN_TICKS = b.defineInRange("queenBeamCooldownTicks", 90, 1, 2400); 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); @@ -586,11 +595,30 @@ public final class AntarchyMobsConfig { 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_DURATION_TICKS = b.defineInRange("kingBeamDurationTicks", 30, 1, 1200); + KING_BEAM_WINDUP_TICKS = b.comment("Charge time before the King beam actually fires.").defineInRange("kingBeamWindupTicks", 20, 0, 1200); + KING_BEAM_TRAVEL_TICKS = b.comment("Time for the King beam to extend from its head to its committed range.").defineInRange("kingBeamTravelTicks", 12, 1, 1200); KING_BEAM_COOLDOWN_TICKS = b.defineInRange("kingBeamCooldownTicks", 90, 1, 2400); KING_BEAM_TRACKING = b.defineInRange("kingBeamTracking", 0.1D, 0.0D, 1.0D); KING_BEAM_TERRAIN_RADIUS = b.defineInRange("kingBeamTerrainRadius", 5.0D, 0.0D, 32.0D); KING_BEAM_TERRAIN_CAP = b.defineInRange("kingBeamTerrainCap", 192, 0, 4096); ROYAL_DECREE_COOLDOWN_TICKS = b.comment("Cooldown in ticks between the end of one King decree and the next.").defineInRange("royalDecreeCooldownTicks", 1200, 1, 72000); + 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_PULL_STRENGTH = b.defineInRange("queenBlackHolePullStrength", 0.16D, 0.0D, 2.0D); + QUEEN_FINAL_TIME_FIELD_RADIUS = b.defineInRange("queenFinalTimeFieldRadius", 20.0D, 1.0D, 64.0D); + QUEEN_FINAL_TIME_FIELD_RATE = b.defineInRange("queenFinalTimeFieldRate", 0.05D, 0.05D, 1.0D); + QUEEN_FINAL_TIME_FIELD_DURATION_TICKS = b.defineInRange("queenFinalTimeFieldDurationTicks", 45, 1, 1200); + QUEEN_FINAL_TIME_FIELD_COOLDOWN_TICKS = b.defineInRange("queenFinalTimeFieldCooldownTicks", 80, 1, 2400); + KING_FIREBALL_DAMAGE = b.defineInRange("kingFireballDamage", 32.0D, 0.0D, 1024.0D); + KING_FIREBALL_RADIUS = b.defineInRange("kingFireballRadius", 4.5D, 0.5D, 32.0D); + KING_FIREBALL_COOLDOWN_TICKS = b.defineInRange("kingFireballCooldownTicks", 150, 1, 2400); + KING_ICEBALL_DAMAGE = b.defineInRange("kingIceballDamage", 18.0D, 0.0D, 1024.0D); + KING_ICEBALL_RADIUS = b.defineInRange("kingIceballRadius", 5.0D, 0.5D, 32.0D); + KING_ICEBALL_COOLDOWN_TICKS = b.defineInRange("kingIceballCooldownTicks", 190, 1, 2400); + KING_ICE_SPIKE_DAMAGE = b.defineInRange("kingIceSpikeDamage", 24.0D, 0.0D, 1024.0D); + KING_ICE_SPIKE_COOLDOWN_TICKS = b.defineInRange("kingIceSpikeCooldownTicks", 240, 1, 2400); + KING_ELEMENTAL_TERRAIN_CAP = b.defineInRange("kingElementalTerrainCap", 256, 0, 4096); + ROYAL_BOSS_SOUND_VOLUME = b.comment("Volume multiplier for large King and Queen presence, movement, and attack sounds.").defineInRange("royalBossSoundVolume", 4.5D, 0.5D, 16.0D); b.pop(); b.push("royalMounts"); @@ -726,8 +754,8 @@ public final class AntarchyMobsConfig { b.push("dimensionalTear"); DIMENSIONAL_TEAR_LIFETIME_TICKS = b.comment("Ticks before naturally generated linked Dimensional Tears collapse.").defineInRange("lifetimeTicks", 24000, 1200, 240000); DIMENSIONAL_TEAR_INVERTED_DURATION_TICKS = b.comment("Ticks of Inverted applied to living entities exiting a Dimensional Tear.").defineInRange("invertedDurationTicks", 2400, 0, 240000); - DIMENSIONAL_TEAR_EMERGENCE_MIN_INTERVAL_TICKS = b.comment("Minimum ticks between Nightmare/Lucid emergence events per tear.").defineInRange("emergenceMinIntervalTicks", 2400, 20, 240000); - DIMENSIONAL_TEAR_EMERGENCE_MAX_INTERVAL_TICKS = b.comment("Maximum ticks between Nightmare/Lucid emergence events per tear.").defineInRange("emergenceMaxIntervalTicks", 7200, 20, 240000); + DIMENSIONAL_TEAR_EMERGENCE_MIN_INTERVAL_TICKS = b.comment("Minimum ticks between Nightmare/Lucid emergence events per tear.").defineInRange("emergenceMinIntervalTicks", 400, 20, 240000); + DIMENSIONAL_TEAR_EMERGENCE_MAX_INTERVAL_TICKS = b.comment("Maximum ticks between Nightmare/Lucid emergence events per tear.").defineInRange("emergenceMaxIntervalTicks", 1200, 20, 240000); DIMENSIONAL_TEAR_LUCID_EVENT_CHANCE = b.comment("Chance that an emergence event spawns a Lucid instead of a Nightmare.").defineInRange("lucidEventChance", 0.65D, 0.0D, 1.0D); b.pop(); @@ -787,7 +815,7 @@ public final class AntarchyMobsConfig { VORTEX_MOVEMENT_SPEED = b.comment("Base movement speed.").defineInRange("movementSpeed", 0.22D, 0.0D, 10.0D); VORTEX_FLYING_SPEED = b.comment("Base flying speed.").defineInRange("flyingSpeed", 0.35D, 0.0D, 10.0D); VORTEX_MAX_ACTIVE_VORTEXES = b.comment("Maximum damaging wind vortexes one Vortex mob can maintain at once.").defineInRange("maxActiveVortexes", 3, 0, 32); - WIND_VORTEX_DURATION_TICKS = b.comment("Lifetime in ticks for wind vortexes spawned by Vortex charges or Vortex mobs.").defineInRange("windVortexDurationTicks", 140, 1, 1200); + WIND_VORTEX_DURATION_TICKS = b.comment("Lifetime in ticks for wind vortexes spawned by Vortex charges or Vortex mobs.").defineInRange("windVortexDurationTicks", 240, 1, 1200); WIND_VORTEX_PULL_STRENGTH = b.comment("How strongly wind vortexes pull entities toward their spiral path.").defineInRange("windVortexPullStrength", 0.32D, 0.0D, 4.0D); WIND_VORTEX_LAUNCH_STRENGTH = b.comment("Launch velocity multiplier applied when entities exit the top of a wind vortex.").defineInRange("windVortexLaunchStrength", 1.0D, 0.0D, 4.0D); VORTEX_LENS_MIN_RADIUS = b.comment("Top radius at signal strength 1 for Vortex Lens wind vortexes.").defineInRange("vortexLensMinRadius", 1.5D, 0.1D, 64.0D); @@ -995,6 +1023,8 @@ public final class AntarchyMobsConfig { static double queenBeamDamage() { return QUEEN_BEAM_DAMAGE.get(); } static double queenBeamRange() { return QUEEN_BEAM_RANGE.get(); } static int queenBeamDurationTicks() { return QUEEN_BEAM_DURATION_TICKS.get(); } + static int queenBeamWindupTicks() { return QUEEN_BEAM_WINDUP_TICKS.get(); } + static int queenBeamTravelTicks() { return QUEEN_BEAM_TRAVEL_TICKS.get(); } static int queenBeamCooldownTicks() { return QUEEN_BEAM_COOLDOWN_TICKS.get(); } static double queenBeamTracking() { return QUEEN_BEAM_TRACKING.get(); } static double queenBeamTerrainRadius() { return QUEEN_BEAM_TERRAIN_RADIUS.get(); } @@ -1002,11 +1032,30 @@ public final class AntarchyMobsConfig { static double kingBeamDamage() { return KING_BEAM_DAMAGE.get(); } static double kingBeamRange() { return KING_BEAM_RANGE.get(); } static int kingBeamDurationTicks() { return KING_BEAM_DURATION_TICKS.get(); } + static int kingBeamWindupTicks() { return KING_BEAM_WINDUP_TICKS.get(); } + static int kingBeamTravelTicks() { return KING_BEAM_TRAVEL_TICKS.get(); } static int kingBeamCooldownTicks() { return KING_BEAM_COOLDOWN_TICKS.get(); } static double kingBeamTracking() { return KING_BEAM_TRACKING.get(); } static double kingBeamTerrainRadius() { return KING_BEAM_TERRAIN_RADIUS.get(); } static int kingBeamTerrainCap() { return KING_BEAM_TERRAIN_CAP.get(); } static int royalDecreeCooldownTicks() { return ROYAL_DECREE_COOLDOWN_TICKS.get(); } + 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 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(); } + static int queenFinalTimeFieldCooldownTicks() { return QUEEN_FINAL_TIME_FIELD_COOLDOWN_TICKS.get(); } + static double kingFireballDamage() { return KING_FIREBALL_DAMAGE.get(); } + static double kingFireballRadius() { return KING_FIREBALL_RADIUS.get(); } + static int kingFireballCooldownTicks() { return KING_FIREBALL_COOLDOWN_TICKS.get(); } + static double kingIceballDamage() { return KING_ICEBALL_DAMAGE.get(); } + static double kingIceballRadius() { return KING_ICEBALL_RADIUS.get(); } + static int kingIceballCooldownTicks() { return KING_ICEBALL_COOLDOWN_TICKS.get(); } + static double kingIceSpikeDamage() { return KING_ICE_SPIKE_DAMAGE.get(); } + static int kingIceSpikeCooldownTicks() { return KING_ICE_SPIKE_COOLDOWN_TICKS.get(); } + static int kingElementalTerrainCap() { return KING_ELEMENTAL_TERRAIN_CAP.get(); } + static double royalBossSoundVolume() { return ROYAL_BOSS_SOUND_VOLUME.get(); } static int royalEggHatchChance() { return ROYAL_EGG_HATCH_CHANCE.get(); } static double royalBoltDamage() { return ROYAL_BOLT_DAMAGE.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 d9c1df83..36cb3404 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 @@ -221,6 +221,7 @@ public class AntarchyNeoforge { AntarchyNeoforgeSounds.KING_ROAR, AntarchyNeoforgeSounds.KING_WING_FLAP, AntarchyNeoforgeSounds.KING_FLY_LOOP, + AntarchyNeoforgeSounds.QUEEN_FLY_LOOP, AntarchyNeoforgeSounds.PRINCE_IDLE, AntarchyNeoforgeSounds.PRINCE_BITE, AntarchyNeoforgeSounds.PRINCE_SHOOT, @@ -436,6 +437,8 @@ public class AntarchyNeoforge { AntarchyObjects.setQueen(() -> AntarchyNeoforgeEntites.QUEEN.get()); AntarchyObjects.setManticore(() -> AntarchyNeoforgeEntites.MANTICORE.get()); AntarchyObjects.setRoyalBolt(() -> AntarchyNeoforgeEntites.ROYAL_BOLT.get()); + AntarchyObjects.setRoyalElementalProjectile(() -> AntarchyNeoforgeEntites.ROYAL_ELEMENTAL_PROJECTILE.get()); + AntarchyObjects.setRoyalIceSpike(() -> AntarchyNeoforgeEntites.ROYAL_ICE_SPIKE.get()); AntarchyObjects.setPrinceEgg(() -> AntarchyNeoforgeBlocks.PRINCE_EGG.get()); AntarchyObjects.setPrincessEgg(() -> AntarchyNeoforgeBlocks.PRINCESS_EGG.get()); AntarchyObjects.setRoyalEggBlockEntity(() -> AntarchyNeoforgeBlocks.ROYAL_EGG_BLOCK_ENTITY.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 727afd62..c7c19106 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 @@ -150,7 +150,7 @@ public final class AntarchyNeoForgeClient { event.registerEntityRenderer(AntarchyNeoforgeEntites.VORTEX_CHARGE_PROJECTILE.get(), ThrownItemRenderer::new); event.registerEntityRenderer(AntarchyNeoforgeEntites.WIND_VORTEX.get(), WindVortexRenderer::new); event.registerEntityRenderer(AntarchyNeoforgeEntites.TIME_DILATION_FIELD.get(), TimeDilationFieldRenderer::new); - event.registerEntityRenderer(AntarchyNeoforgeEntites.ROYAL_BLACK_HOLE.get(), net.minecraft.client.renderer.entity.NoopRenderer::new); + event.registerEntityRenderer(AntarchyNeoforgeEntites.ROYAL_BLACK_HOLE.get(), com.craisinlord.antarchy.content.client.renderer.RoyalBlackHoleRenderer::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); @@ -176,6 +176,8 @@ public final class AntarchyNeoForgeClient { event.registerEntityRenderer(AntarchyNeoforgeEntites.KING.get(), com.craisinlord.antarchy.content.client.renderer.RoyalBossRenderer::new); event.registerEntityRenderer(AntarchyNeoforgeEntites.QUEEN.get(), com.craisinlord.antarchy.content.client.renderer.RoyalBossRenderer::new); event.registerEntityRenderer(AntarchyNeoforgeEntites.ROYAL_BOLT.get(), com.craisinlord.antarchy.content.client.renderer.RoyalBoltRenderer::new); + event.registerEntityRenderer(AntarchyNeoforgeEntites.ROYAL_ELEMENTAL_PROJECTILE.get(), com.craisinlord.antarchy.content.client.renderer.RoyalElementalProjectileRenderer::new); + event.registerEntityRenderer(AntarchyNeoforgeEntites.ROYAL_ICE_SPIKE.get(), com.craisinlord.antarchy.content.client.renderer.RoyalIceSpikeRenderer::new); event.registerEntityRenderer(AntarchyNeoforgeEntites.JERRY.get(), context -> withParalyzedGeoLayer(new com.craisinlord.antarchy.content.client.renderer.JerryRenderer(context))); event.registerEntityRenderer(AntarchyNeoforgeEntites.JUMPY_BUG.get(), context -> withParalyzedGeoLayer(new com.craisinlord.antarchy.content.client.renderer.JumpyBugRenderer(context))); event.registerEntityRenderer(AntarchyNeoforgeEntites.SPIT_BUG.get(), context -> withParalyzedGeoLayer(new com.craisinlord.antarchy.content.client.renderer.SpitBugRenderer(context))); diff --git a/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/registry/AntarchyNeoForgeEvents.java b/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/registry/AntarchyNeoForgeEvents.java index 4bc728f2..cf1d4f20 100644 --- a/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/registry/AntarchyNeoForgeEvents.java +++ b/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/registry/AntarchyNeoForgeEvents.java @@ -83,10 +83,24 @@ import net.neoforged.neoforge.fluids.FluidInteractionRegistry; import net.neoforged.neoforge.network.PacketDistributor; public final class AntarchyNeoForgeEvents { + private static final long SERVER_HANG_WARN_NANOS = 5_000_000_000L; + private static final long SERVER_IDLE_BUSY_WARN_NANOS = 30_000_000_000L; + private static final long SERVER_HANG_REPEAT_NANOS = 30_000_000_000L; + private static final double OVERHEAD_INVERSION_WARN_MS = 10.0D; + private static final long OVERHEAD_INVERSION_WARN_INTERVAL_TICKS = 100L; + private static volatile boolean serverHangWatchdogStarted; + private static volatile long serverTickStartNanos; + private static volatile long serverTickEndNanos; + private static volatile long serverHangLastDumpNanos; + private static volatile Thread serverTickThread; + private static long lastOverheadInversionWarnTick = Long.MIN_VALUE / 2L; + private AntarchyNeoForgeEvents() {} public static void register(IEventBus modEventBus) { modEventBus.addListener(AntarchyNeoForgeEvents::onCommonSetup); + NeoForge.EVENT_BUS.addListener(AntarchyNeoForgeEvents::beginServerTickWatchdog); + NeoForge.EVENT_BUS.addListener(AntarchyNeoForgeEvents::endServerTickWatchdog); NeoForge.EVENT_BUS.addListener(AntarchyNeoForgeEvents::onMissileSquidDeath); NeoForge.EVENT_BUS.addListener(AntarchyNeoForgeEvents::onPermanentPortalSacrifice); NeoForge.EVENT_BUS.addListener(AntarchyNeoForgeEvents::onLivingBreathe); @@ -151,6 +165,79 @@ public final class AntarchyNeoForgeEvents { NeoForge.EVENT_BUS.addListener(AntarchyNeoForgeEvents::onWandererTrades); } + static void beginServerTickWatchdog(ServerTickEvent.Pre event) { + ensureServerHangWatchdogStarted(); + serverTickThread = Thread.currentThread(); + serverTickStartNanos = System.nanoTime(); + } + + static void endServerTickWatchdog(ServerTickEvent.Post event) { + serverTickEndNanos = System.nanoTime(); + serverTickStartNanos = 0L; + } + + private static synchronized void ensureServerHangWatchdogStarted() { + if (serverHangWatchdogStarted) { + return; + } + serverHangWatchdogStarted = true; + Thread watchdog = new Thread(() -> { + while (true) { + try { + Thread.sleep(2_000L); + } catch (InterruptedException ignored) { + Thread.currentThread().interrupt(); + return; + } + + long tickStart = serverTickStartNanos; + long tickEnd = serverTickEndNanos; + Thread tickThread = serverTickThread; + long now = System.nanoTime(); + if (tickThread == null || now - serverHangLastDumpNanos < SERVER_HANG_REPEAT_NANOS) { + continue; + } + + if (tickStart > 0L && now - tickStart >= SERVER_HANG_WARN_NANOS) { + antarchy$dumpServerThreadWatchdog( + "server thread appears hung", + (now - tickStart) / 1_000_000.0D, + tickThread + ); + serverHangLastDumpNanos = now; + continue; + } + + Thread.State state = tickThread.getState(); + boolean idleBusy = tickStart <= 0L + && tickEnd > 0L + && now - tickEnd >= SERVER_IDLE_BUSY_WARN_NANOS + && state != Thread.State.WAITING + && state != Thread.State.TIMED_WAITING; + if (idleBusy) { + antarchy$dumpServerThreadWatchdog( + "server thread busy outside active tick", + (now - tickEnd) / 1_000_000.0D, + tickThread + ); + serverHangLastDumpNanos = now; + } + } + }, "Antarchy Server Hang Watchdog"); + watchdog.setDaemon(true); + watchdog.start(); + } + + private static void antarchy$dumpServerThreadWatchdog(String message, double elapsedMs, Thread tickThread) { + Antarchy.LOGGER.error( + "[antarchy-watchdog] {} elapsedMs={} thread={} state={}", + message, elapsedMs, tickThread.getName(), tickThread.getState() + ); + for (StackTraceElement element : tickThread.getStackTrace()) { + Antarchy.LOGGER.error("[antarchy-watchdog] at {}", element); + } + } + static void onVillagerTrades(VillagerTradesEvent event) { if (event.getType() == VillagerProfession.FARMER) { event.getTrades().get(1).add(emeraldForItems(AntarchyObjects.CORN.get(), 20, 16, 2)); @@ -430,6 +517,9 @@ public final class AntarchyNeoForgeEvents { } private static final int ANTIMETAL_INVERTED_REFRESH_TICKS = 20; + private static final int ANTIMETAL_INVERTED_REFRESH_THRESHOLD_TICKS = 12; + private static final int ANTIMETAL_PLAYER_SCAN_INTERVAL_TICKS = 2; + private static final int ANTIMETAL_MOB_SCAN_INTERVAL_TICKS = 10; static void tickOverheadInversion(EntityTickEvent.Post event) { if (!(event.getEntity() instanceof LivingEntity livingEntity)) { @@ -441,17 +531,56 @@ public final class AntarchyNeoForgeEvents { if (livingEntity instanceof Player player && player.isSpectator()) { return; } - if (!isDirectlyBelowAntimetal(livingEntity)) { + + MobEffectInstance existingInverted = livingEntity.getEffect(AntarchyNeoforgeMisc.INVERTED); + long gameTime = livingEntity.level().getGameTime(); + if (!shouldScanOverheadAntimetal(livingEntity, existingInverted, gameTime)) { return; } - MobEffectInstance existingInverted = livingEntity.getEffect(AntarchyNeoforgeMisc.INVERTED); - if (existingInverted == null || existingInverted.getDuration() <= 5) { + long startNanos = System.nanoTime(); + boolean directlyBelowAntimetal = isDirectlyBelowAntimetal(livingEntity); + double elapsedMs = (System.nanoTime() - startNanos) / 1_000_000.0D; + if (elapsedMs >= OVERHEAD_INVERSION_WARN_MS + && gameTime - lastOverheadInversionWarnTick >= OVERHEAD_INVERSION_WARN_INTERVAL_TICKS) { + lastOverheadInversionWarnTick = gameTime; + Antarchy.LOGGER.warn( + "[antarchy-gravity] slow overhead antimetal scan targetType={} uuid={} pos=({}, {}, {}) dim={} elapsedMs={} inverted={} forced={} gameTime={}", + livingEntity.getType(), livingEntity.getUUID(), livingEntity.getX(), livingEntity.getY(), livingEntity.getZ(), + livingEntity.level().dimension().location(), elapsedMs, + com.craisinlord.antarchy.content.gravity.AntarchyGravityApi.isGravityInverted(livingEntity), + com.craisinlord.antarchy.content.gravity.AntarchyGravityApi.isGravityForced(livingEntity), + gameTime + ); + } + if (!directlyBelowAntimetal) { + return; + } + + if (existingInverted == null || existingInverted.getDuration() <= ANTIMETAL_INVERTED_REFRESH_THRESHOLD_TICKS) { + if (livingEntity instanceof Player || livingEntity.level().dimension().location().toString().equals("antarchy:thoraxis")) { + Antarchy.LOGGER.info( + "[antarchy-gravity] overhead antimetal applying inverted effect targetType={} uuid={} pos=({}, {}, {}) dim={} existingDuration={} gameTime={}", + livingEntity.getType(), livingEntity.getUUID(), livingEntity.getX(), livingEntity.getY(), livingEntity.getZ(), + livingEntity.level().dimension().location(), + existingInverted != null ? existingInverted.getDuration() : -1, + gameTime + ); + } livingEntity.addEffect(new MobEffectInstance(AntarchyNeoforgeMisc.INVERTED, ANTIMETAL_INVERTED_REFRESH_TICKS, 0, false, false, false)); spawnAntimetalInversionParticles(livingEntity); } } + private static boolean shouldScanOverheadAntimetal(LivingEntity entity, MobEffectInstance existingInverted, long gameTime) { + if (existingInverted != null && existingInverted.getDuration() <= ANTIMETAL_INVERTED_REFRESH_THRESHOLD_TICKS) { + return true; + } + int interval = entity instanceof Player ? ANTIMETAL_PLAYER_SCAN_INTERVAL_TICKS : ANTIMETAL_MOB_SCAN_INTERVAL_TICKS; + long phase = (long) entity.getUUID().hashCode() + gameTime; + return Math.floorMod(phase, interval) == 0L; + } + private static boolean isInOrBelowAntimetalScaffolding(LivingEntity entity) { AABB bounds = entity.getBoundingBox().deflate(1.0E-3D); int minX = net.minecraft.util.Mth.floor(bounds.minX); @@ -484,18 +613,16 @@ public final class AntarchyNeoForgeEvents { int minX = net.minecraft.util.Mth.floor(bounds.minX); int maxX = net.minecraft.util.Mth.floor(bounds.maxX); - int minY = net.minecraft.util.Mth.floor(bounds.minY); int maxY = net.minecraft.util.Mth.floor(bounds.maxY); int minZ = net.minecraft.util.Mth.floor(bounds.minZ); int maxZ = net.minecraft.util.Mth.floor(bounds.maxZ); + net.minecraft.core.BlockPos.MutableBlockPos crystalPos = new net.minecraft.core.BlockPos.MutableBlockPos(); for (int x = minX; x <= maxX; x++) { - for (int y = minY; y <= maxY; y++) { - for (int z = minZ; z <= maxZ; z++) { - net.minecraft.core.BlockPos crystalPos = new net.minecraft.core.BlockPos(x, y + 1, z); - if (entity.level().getBlockState(crystalPos).is(com.craisinlord.antarchy.content.AntarchyTags.Blocks.ANTIMETAL_INVERSION_BLOCKS)) { - return true; - } + for (int z = minZ; z <= maxZ; z++) { + crystalPos.set(x, maxY + 1, z); + if (entity.level().getBlockState(crystalPos).is(com.craisinlord.antarchy.content.AntarchyTags.Blocks.ANTIMETAL_INVERSION_BLOCKS)) { + return true; } } } @@ -1146,6 +1273,10 @@ public final class AntarchyNeoForgeEvents { event.getBuilder().addMix(Potions.AWKWARD, AntarchyNeoforgeItems.TITANIUM_NUGGET.get(), AntarchyNeoforgeMisc.GROWING); event.getBuilder().addMix(AntarchyNeoforgeMisc.GROWING, Items.GLOWSTONE_DUST, AntarchyNeoforgeMisc.STRONG_GROWING); event.getBuilder().addMix(AntarchyNeoforgeMisc.STRONG_GROWING, Items.GLOWSTONE_DUST, AntarchyNeoforgeMisc.EXTREME_GROWING); + event.getBuilder().addMix(Potions.AWKWARD, AntarchyNeoforgeItems.KING_SCALE.get(), AntarchyNeoforgeMisc.COMMAND); + event.getBuilder().addMix(AntarchyNeoforgeMisc.COMMAND, Items.REDSTONE, AntarchyNeoforgeMisc.LONG_COMMAND); + event.getBuilder().addMix(Potions.AWKWARD, AntarchyNeoforgeItems.QUEEN_SCALE.get(), AntarchyNeoforgeMisc.TIME_DILATION); + event.getBuilder().addMix(AntarchyNeoforgeMisc.TIME_DILATION, Items.REDSTONE, AntarchyNeoforgeMisc.LONG_TIME_DILATION); } 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 51ceef5f..39ecf4b2 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 @@ -425,6 +425,7 @@ public final class AntarchyNeoforgeEntites { () -> EntityType.Builder.of(ScorpionEntity::new, MobCategory.MONSTER) .sized(1.5F, 1.0F) .clientTrackingRange(8) + .fireImmune() .build("scorpion")); public static final DeferredHolder, EntityType> BASILISK = ENTITY_TYPES.register("basilisk", () -> EntityType.Builder.of(BasiliskEntity::new, MobCategory.MONSTER) @@ -435,6 +436,7 @@ public final class AntarchyNeoforgeEntites { () -> EntityType.Builder.of(EmperorScorpionEntity::new, MobCategory.MONSTER) .sized(6.0F, 3.0F) .clientTrackingRange(12) + .fireImmune() .build("emperor_scorpion")); public static final DeferredHolder, EntityType> LUCID_PEARL_PROJECTILE = ENTITY_TYPES.register("lucid_pearl_projectile", () -> EntityType.Builder.of(LucidEyeProjectileEntity::new, MobCategory.MISC) @@ -497,6 +499,7 @@ public final class AntarchyNeoforgeEntites { () -> EntityType.Builder.of(ManticoreEntity::new, MobCategory.MONSTER) .sized(1.95F, 2.0F) .clientTrackingRange(10) + .fireImmune() .build("manticore")); public static final DeferredHolder, EntityType> JERRY = ENTITY_TYPES.register("jerry", () -> EntityType.Builder.of(JerryEntity::new, MobCategory.MONSTER) @@ -541,6 +544,12 @@ public final class AntarchyNeoforgeEntites { public static final DeferredHolder, EntityType> ROYAL_BOLT = ENTITY_TYPES.register("royal_bolt", () -> EntityType.Builder.of(com.craisinlord.antarchy.content.entity.royal.RoyalBoltEntity::new, MobCategory.MISC) .sized(0.4F, 0.4F).clientTrackingRange(8).updateInterval(1).fireImmune().build("royal_bolt")); + public static final DeferredHolder, EntityType> ROYAL_ELEMENTAL_PROJECTILE = ENTITY_TYPES.register("royal_elemental_projectile", + () -> EntityType.Builder.of(com.craisinlord.antarchy.content.entity.royal.RoyalElementalProjectileEntity::new, MobCategory.MISC) + .sized(1.0F, 1.0F).clientTrackingRange(16).updateInterval(1).fireImmune().build("royal_elemental_projectile")); + public static final DeferredHolder, EntityType> ROYAL_ICE_SPIKE = ENTITY_TYPES.register("royal_ice_spike", + () -> EntityType.Builder.of(com.craisinlord.antarchy.content.entity.royal.RoyalIceSpikeEntity::new, MobCategory.MISC) + .sized(2.0F, 4.0F).clientTrackingRange(16).updateInterval(1).fireImmune().build("royal_ice_spike")); public static final DeferredHolder, EntityType> OURANWOOD_DEER = ENTITY_TYPES.register("ouranwood_deer", () -> EntityType.Builder.of(OuranwoodDeerEntity::new, MobCategory.CREATURE) .sized(1.125F, 1.75F) diff --git a/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/registry/AntarchyNeoforgeMisc.java b/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/registry/AntarchyNeoforgeMisc.java index 86302673..cd05a4ec 100644 --- a/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/registry/AntarchyNeoforgeMisc.java +++ b/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/registry/AntarchyNeoforgeMisc.java @@ -7,6 +7,8 @@ import com.craisinlord.antarchy.content.effect.DreadMobEffect; import com.craisinlord.antarchy.content.effect.GoopedMobEffect; import com.craisinlord.antarchy.content.effect.GrowthMobEffect; import com.craisinlord.antarchy.content.effect.InvertedMobEffect; +import com.craisinlord.antarchy.content.effect.CommandedMobEffect; +import com.craisinlord.antarchy.content.effect.DilatedMobEffect; import com.craisinlord.antarchy.content.effect.ParalyzedMobEffect; import com.craisinlord.antarchy.content.effect.ShrinkMobEffect; import com.craisinlord.antarchy.content.effect.StinkyMobEffect; @@ -127,6 +129,8 @@ public final class AntarchyNeoforgeMisc { public static final DeferredHolder DREAD = MOB_EFFECTS.register("dread", DreadMobEffect::new); public static final DeferredHolder PARALYZED = MOB_EFFECTS.register("paralyzed", ParalyzedMobEffect::new); public static final DeferredHolder INVERTED = MOB_EFFECTS.register("inverted", InvertedMobEffect::new); + public static final DeferredHolder COMMANDED = MOB_EFFECTS.register("commanded", CommandedMobEffect::new); + public static final DeferredHolder DILATED = MOB_EFFECTS.register("dilated", DilatedMobEffect::new); public static final DeferredHolder STINKY = MOB_EFFECTS.register("stinky", StinkyMobEffect::new); public static final DeferredHolder GOOPED = MOB_EFFECTS.register("gooped", GoopedMobEffect::new); public static final DeferredHolder BLOODGLASS_WARD = MOB_EFFECTS.register("bloodglass_ward", com.craisinlord.antarchy.content.effect.BloodglassWardEffect::new); @@ -140,6 +144,14 @@ public final class AntarchyNeoforgeMisc { () -> new Potion(new MobEffectInstance(INVERTED, 600))); public static final DeferredHolder LONG_INVERSION = POTIONS.register("long_inversion", () -> new Potion("inversion", new MobEffectInstance(INVERTED, 2400))); + public static final DeferredHolder COMMAND = POTIONS.register("command", + () -> new Potion(new MobEffectInstance(COMMANDED, 600))); + public static final DeferredHolder LONG_COMMAND = POTIONS.register("long_command", + () -> new Potion("command", new MobEffectInstance(COMMANDED, 1200))); + public static final DeferredHolder TIME_DILATION = POTIONS.register("time_dilation", + () -> new Potion(new MobEffectInstance(DILATED, 300))); + public static final DeferredHolder LONG_TIME_DILATION = POTIONS.register("long_time_dilation", + () -> new Potion("time_dilation", new MobEffectInstance(DILATED, 600))); public static final DeferredHolder STINKY_POTION = POTIONS.register("stinky", () -> new Potion(new MobEffectInstance(STINKY, 1200))); public static final DeferredHolder LONG_STINKY = POTIONS.register("long_stinky", @@ -375,6 +387,9 @@ public final class AntarchyNeoforgeMisc { public static final DeferredHolder, DimensionalTearFeature> DIMENSIONAL_TEAR = FEATURES.register("dimensional_tear", () -> new DimensionalTearFeature(NoneFeatureConfiguration.CODEC)); + public static final DeferredHolder, ThoraxisIchorLakeFeature> THORAXIS_ICHOR_LAKE = FEATURES.register("thoraxis_ichor_lake", + () -> new ThoraxisIchorLakeFeature(NoneFeatureConfiguration.CODEC)); + // Biome sources public static final DeferredHolder, MapCodec> ELYTHIA_BIOME_SOURCE = BIOME_SOURCES.register("elythia_biome_source", () -> ElythiaBiomeSource.CODEC);