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 d8a8f572..794ac937 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 @@ -107,6 +107,9 @@ public final class AntarchySettings { private static int ultimateCrossbowEnchantability = 20; private static int ultimateMaceEnchantability = 20; private static boolean ultimateToolsThreeByThreeEnabled = true; + private static boolean ultimateToolsVeinMiningEnabled = true; + private static int ultimateToolsVeinMiningMaxBlocks = 64; + private static int ultimateToolsTreeFellingMaxLogs = 128; private static boolean ultimateArmorComesEnchanted = true; private static int ultimateHelmetArmorValue = 6; private static int ultimateChestplateArmorValue = 11; @@ -299,6 +302,7 @@ public final class AntarchySettings { private static double royalBossKnockbackResistance = 1.0D; private static double royalBossStepHeight = 3.0D; private static double royalBossMaxSingleHitDamage = 250.0D; + private static double royalBossHitOverflowRate = 0.25D; private static double royalBossBiteReach = 12.0D; private static double royalBossBiteDamageMultiplier = 1.6D; private static int royalBossBiteCooldownTicks = 45; @@ -974,6 +978,18 @@ public final class AntarchySettings { return ultimateToolsThreeByThreeEnabled; } + public static boolean ultimateToolsVeinMiningEnabled() { + return ultimateToolsVeinMiningEnabled; + } + + public static int ultimateToolsVeinMiningMaxBlocks() { + return ultimateToolsVeinMiningMaxBlocks; + } + + public static int ultimateToolsTreeFellingMaxLogs() { + return ultimateToolsTreeFellingMaxLogs; + } + public static boolean ultimateArmorComesEnchanted() { return ultimateArmorComesEnchanted; } @@ -1624,6 +1640,18 @@ public final class AntarchySettings { ultimateToolsThreeByThreeEnabled = value; } + public static void setUltimateToolsVeinMiningEnabled(boolean value) { + ultimateToolsVeinMiningEnabled = value; + } + + public static void setUltimateToolsVeinMiningMaxBlocks(int value) { + ultimateToolsVeinMiningMaxBlocks = value; + } + + public static void setUltimateToolsTreeFellingMaxLogs(int value) { + ultimateToolsTreeFellingMaxLogs = value; + } + public static void setUltimateArmorComesEnchanted(boolean value) { ultimateArmorComesEnchanted = value; } @@ -2229,6 +2257,9 @@ public final class AntarchySettings { public static double royalBossMaxSingleHitDamage() { return royalBossMaxSingleHitDamage; } public static void setRoyalBossMaxSingleHitDamage(double v) { royalBossMaxSingleHitDamage = v; } + public static double royalBossHitOverflowRate() { return royalBossHitOverflowRate; } + public static void setRoyalBossHitOverflowRate(double v) { royalBossHitOverflowRate = v; } + public static double royalBossBiteReach() { return royalBossBiteReach; } public static void setRoyalBossBiteReach(double v) { royalBossBiteReach = v; } diff --git a/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/AntarchyTags.java b/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/AntarchyTags.java index 4a54dc63..581c1828 100644 --- a/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/AntarchyTags.java +++ b/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/AntarchyTags.java @@ -11,6 +11,7 @@ import net.minecraft.world.entity.EntityType; import net.minecraft.world.item.Item; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.dimension.DimensionType; import net.minecraft.world.level.material.Fluid; public final class AntarchyTags { @@ -157,4 +158,11 @@ public final class AntarchyTags { private Biomes() { } } + + public static final class DimensionTypes { + public static final TagKey NO_MANTIS_CLIMB = TagKey.create(Registries.DIMENSION_TYPE, id("no_mantis_climb")); + + private DimensionTypes() { + } + } } diff --git a/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/client/model/RoyalMountModel.java b/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/client/model/RoyalMountModel.java index e1ece6fc..c9366b81 100644 --- a/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/client/model/RoyalMountModel.java +++ b/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/client/model/RoyalMountModel.java @@ -11,6 +11,12 @@ import software.bernie.geckolib.model.GeoModel; public class RoyalMountModel extends GeoModel { private static final String[] HEADS = {"head_left", "head", "head_right"}; + private static final String[] RIDDEN_NECK_CHAIN = { + "neck", "neck2", "neck3", "head", + "neck_left", "neck_left2", "neck_left3", "head_left", + "neck_right", "neck_right2", "neck_right3", "head_right" + }; + private static final float RIDDEN_NECK_RAISE = 0.35F; @Override public ResourceLocation getModelResource(RoyalMountEntity animatable) { @@ -31,6 +37,11 @@ public class RoyalMountModel extends GeoModel { public void setCustomAnimations(RoyalMountEntity animatable, long instanceId, AnimationState animationState) { super.setCustomAnimations(animatable, instanceId, animationState); + if (animatable.isVehicle()) { + for (String name : RIDDEN_NECK_CHAIN) { + this.getBone(name).ifPresent(bone -> bone.setRotX(bone.getRotX() * RIDDEN_NECK_RAISE)); + } + } for (String head : HEADS) { this.getBone(head).ifPresent(bone -> bone.setTrackingMatrices(true)); } diff --git a/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/effect/BloodglassWardEffect.java b/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/effect/BloodglassWardEffect.java index e56947b7..9c049b83 100644 --- a/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/effect/BloodglassWardEffect.java +++ b/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/effect/BloodglassWardEffect.java @@ -21,6 +21,7 @@ public final class BloodglassWardEffect extends MobEffect { public void addAttributeModifiers(net.minecraft.world.entity.LivingEntity livingEntity, AttributeMap attributeMap, int amplifier) { AttributeInstance instance = attributeMap.getInstance(AntarchyObjects.BLOODGLASS_MAX_HEARTS.get()); if (instance == null) return; + instance.removeModifier(MODIFIER_ID); instance.addTransientModifier( new AttributeModifier(MODIFIER_ID, "bloodglass_ward_hearts", amplifier + 1, AttributeModifier.Operation.ADDITION) ); diff --git a/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/entity/brutalfly/BrutalflyEntity.java b/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/entity/brutalfly/BrutalflyEntity.java index 503cf1d3..a4d65466 100644 --- a/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/entity/brutalfly/BrutalflyEntity.java +++ b/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/entity/brutalfly/BrutalflyEntity.java @@ -40,6 +40,8 @@ import net.minecraft.world.entity.ai.goal.target.NearestAttackableTargetGoal; import net.minecraft.world.entity.ai.navigation.FlyingPathNavigation; import net.minecraft.world.entity.ai.navigation.PathNavigation; import net.minecraft.world.entity.monster.Monster; +import net.minecraft.world.effect.MobEffectInstance; +import net.minecraft.world.effect.MobEffects; import net.minecraft.world.entity.player.Player; import net.minecraft.world.level.ServerLevelAccessor; import net.minecraft.world.level.Level; @@ -85,7 +87,7 @@ public class BrutalflyEntity extends Monster implements GeoEntity { private static final int SPIT_WINDUP_TICKS = 16; private static final int SWIPE_TICKS = 18; private static final float SWIPE_KNOCKBACK = 2.8F; - private static final int COCOON_HIT_THRESHOLD = 3; + private static final int COCOON_HIT_THRESHOLD = 5; private static final int COCOON_HIT_COOLDOWN_TICKS = 60; private static final int COCOON_HIT_ANIM_TICKS = 30; private static final int COCOON_HATCH_ANIM_TICKS = 40; @@ -337,6 +339,12 @@ public class BrutalflyEntity extends Monster implements GeoEntity { if (nearbyPlayer != null && !nearbyPlayer.isCreative() && !nearbyPlayer.isSpectator()) { this.setTarget(nearbyPlayer); } + this.spitCooldown = 0; + } + + public float getDamageScale() { + double base = AntarchySettings.brutalflySwipeDamage(); + return base <= 0.0D ? 1.0F : (float) (this.getAttributeValue(Attributes.ATTACK_DAMAGE) / base); } public boolean isCocooned() { @@ -514,6 +522,11 @@ public class BrutalflyEntity extends Monster implements GeoEntity { this.playSound(AntarchySoundEvents.BRUTALFLY_SPIT.get(), 1.0F, 0.9F + this.random.nextFloat() * 0.2F); switch (this.getFightPhase()) { case PHASE_THREE -> { + this.launchOrb(serverLevel, origin, target, BrutalflyOrbEntity.OrbVariant.POISON, -14.0F); + this.launchOrb(serverLevel, origin, target, BrutalflyOrbEntity.OrbVariant.STICKY, 0.0F); + this.launchOrb(serverLevel, origin, target, BrutalflyOrbEntity.OrbVariant.POISON, 14.0F); + } + case PHASE_TWO -> { this.launchOrb(serverLevel, origin, target, BrutalflyOrbEntity.OrbVariant.POISON, -8.0F); this.launchOrb(serverLevel, origin, target, BrutalflyOrbEntity.OrbVariant.STICKY, 8.0F); } @@ -586,7 +599,8 @@ public class BrutalflyEntity extends Monster implements GeoEntity { } private void performSwipeHit() { - float swipeDamage = (float) AntarchySettings.brutalflySwipeDamage(); + float swipeDamage = (float) this.getAttributeValue(Attributes.ATTACK_DAMAGE); + boolean finalPhase = this.getFightPhase() == FightPhase.PHASE_THREE; AABB attackBox = this.getBoundingBox().inflate(4.8D, 2.4D, 4.8D).expandTowards(0.0D, -2.5D, 0.0D); for (LivingEntity target : this.level().getEntitiesOfClass( LivingEntity.class, @@ -598,6 +612,9 @@ public class BrutalflyEntity extends Monster implements GeoEntity { } target.hurt(this.damageSources().mobAttack(this), swipeDamage); + if (finalPhase) { + target.addEffect(new MobEffectInstance(MobEffects.MOVEMENT_SLOWDOWN, 60, 1, false, true)); + } double dx = target.getX() - this.getX(); double dz = target.getZ() - this.getZ(); diff --git a/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/entity/brutalfly/BrutalflyOrbEntity.java b/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/entity/brutalfly/BrutalflyOrbEntity.java index 13e133c5..5ec942ec 100644 --- a/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/entity/brutalfly/BrutalflyOrbEntity.java +++ b/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/entity/brutalfly/BrutalflyOrbEntity.java @@ -162,6 +162,9 @@ public class BrutalflyOrbEntity extends ThrowableItemProjectile { if (!this.level().isClientSide && result.getEntity() instanceof LivingEntity livingTarget) { float directHitDamage = (float) AntarchySettings.brutalflySpitDamage(); Entity owner = this.getOwner(); + if (owner instanceof BrutalflyEntity brutalfly) { + directHitDamage *= brutalfly.getDamageScale(); + } if (owner instanceof LivingEntity livingOwner) { livingTarget.hurt(this.damageSources().mobProjectile(this, livingOwner), directHitDamage); } else { diff --git a/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/PrinceEntity.java b/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/PrinceEntity.java index cb671845..78813207 100644 --- a/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/PrinceEntity.java +++ b/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/PrinceEntity.java @@ -3,13 +3,31 @@ 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 com.craisinlord.antarchy.content.effect.RoyalEffectEligibility; +import com.craisinlord.antarchy.content.effect.RoyalEffectHooks; import com.craisinlord.antarchy.content.entity.royal.beam.RoyalBeamElement; +import java.util.Comparator; +import java.util.List; +import net.minecraft.core.particles.DustParticleOptions; +import net.minecraft.core.particles.ParticleOptions; +import net.minecraft.core.particles.ParticleTypes; +import net.minecraft.server.level.ServerLevel; import net.minecraft.sounds.SoundEvent; +import net.minecraft.util.Mth; +import net.minecraft.world.effect.MobEffectInstance; import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.Mob; import net.minecraft.world.entity.ai.attributes.AttributeSupplier; +import net.minecraft.world.entity.monster.Enemy; import net.minecraft.world.level.Level; +import net.minecraft.world.phys.Vec3; +import org.jetbrains.annotations.Nullable; +import org.joml.Vector3f; public class PrinceEntity extends RoyalMountEntity { + private static final DustParticleOptions COMMAND_DUST = new DustParticleOptions(new Vector3f(1.0F, 0.75F, 0.15F), 1.6F); + public PrinceEntity(EntityType entityType, Level level) { super(entityType, level); } @@ -40,6 +58,117 @@ public class PrinceEntity extends RoyalMountEntity { }; } + @Override + protected ParticleOptions beamWindupParticle() { + return switch (this.getBeamElement()) { + case FIRE -> ParticleTypes.FLAME; + case ICE -> ParticleTypes.SNOWFLAKE; + default -> ParticleTypes.ELECTRIC_SPARK; + }; + } + + @Override + protected float beamBaseDamage() { + return 12.0F; + } + + @Override + public boolean prefersAerialCombat() { + return false; + } + + @Override + public double preferredCombatRange() { + return 0.0D; + } + + @Override + protected float biteWeight() { + return 3.0F; + } + + @Override + protected float spitWeight() { + return 1.0F; + } + + @Override + protected float beamWeight() { + return 1.2F; + } + + @Override + protected float signatureWeight() { + return 4.0F; + } + + @Override + protected int signatureCooldownTicks() { + return 360; + } + + private double commandRadius() { + return 14.0D + 2.0D * this.getGrowthScale(); + } + + private int commandLimit() { + return 3 + Mth.floor(this.getGrowthScale()); + } + + private List commandCandidates(Vec3 center) { + double radius = this.commandRadius(); + return this.level().getEntitiesOfClass(Mob.class, this.getBoundingBox().inflate(radius), mob -> + mob != this && mob.isAlive() && mob instanceof Enemy && !mob.isAlliedTo(this) + && !(mob instanceof RoyalBossEntity) && RoyalEffectEligibility.canApplyCommanded(mob) + && RoyalEffectHooks.commandedHolder() != null + && !mob.hasEffect(RoyalEffectHooks.commandedHolder()) + && mob.distanceToSqr(center) <= radius * radius); + } + + @Override + protected boolean isSignatureUseful(LivingEntity target) { + return RoyalEffectHooks.commandedHolder() != null && this.commandCandidates(this.position()).size() >= 2; + } + + @Override + protected void signatureTelegraph(ServerLevel level, int tick, @Nullable LivingEntity target, @Nullable Vec3 aimPoint) { + if (tick % 3 != 0) { + return; + } + double radius = (this.getBbWidth() * 0.5D + 1.0D) + tick * 0.25D * this.getGrowthScale(); + for (int i = 0; i < 16; i++) { + double angle = i * Mth.TWO_PI / 16.0D + tick * 0.1D; + level.sendParticles(COMMAND_DUST, this.getX() + Math.cos(angle) * radius, this.getY() + 0.3D, + this.getZ() + Math.sin(angle) * radius, 1, 0.0D, 0.05D, 0.0D, 0.0D); + } + if (tick == 3) { + level.sendParticles(ParticleTypes.ENCHANTED_HIT, this.getX(), this.getY() + this.getBbHeight(), this.getZ(), + 20, this.getBbWidth() * 0.3D, 0.3D, this.getBbWidth() * 0.3D, 0.1D); + } + } + + @Override + protected boolean performSignature(ServerLevel level, @Nullable LivingEntity target, @Nullable Vec3 aimPoint, boolean ridden) { + if (RoyalEffectHooks.commandedHolder() == null) { + return false; + } + Vec3 center = this.position(); + List candidates = this.commandCandidates(center); + candidates.sort(Comparator.comparingDouble(mob -> mob.distanceToSqr(center))); + int applied = 0; + for (Mob mob : candidates) { + if (applied >= this.commandLimit()) { + break; + } + if (mob.addEffect(new MobEffectInstance(RoyalEffectHooks.commandedHolder(), 300 + 20 * Mth.floor(this.getGrowthScale())), this)) { + applied++; + level.sendParticles(COMMAND_DUST, mob.getX(), mob.getY() + mob.getBbHeight() + 0.3D, mob.getZ(), 8, 0.25D, 0.2D, 0.25D, 0.0D); + } + } + level.sendParticles(ParticleTypes.SONIC_BOOM, this.getX(), this.getY() + this.getBbHeight() * 0.6D, this.getZ(), 1, 0.0D, 0.0D, 0.0D, 0.0D); + return applied > 0; + } + @Override protected String geoName() { return "prince"; diff --git a/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/PrincessEntity.java b/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/PrincessEntity.java index af6e829d..ef9f3088 100644 --- a/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/PrincessEntity.java +++ b/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/PrincessEntity.java @@ -4,10 +4,18 @@ import com.craisinlord.antarchy.config.AntarchySettings; import com.craisinlord.antarchy.content.AntarchyObjects; import com.craisinlord.antarchy.content.AntarchySoundEvents; import com.craisinlord.antarchy.content.entity.royal.beam.RoyalBeamElement; +import com.craisinlord.anthology.time.TimeDilationApi; +import net.minecraft.core.particles.ParticleOptions; +import net.minecraft.core.particles.ParticleTypes; +import net.minecraft.server.level.ServerLevel; import net.minecraft.sounds.SoundEvent; +import net.minecraft.util.Mth; import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.ai.attributes.AttributeSupplier; import net.minecraft.world.level.Level; +import net.minecraft.world.phys.Vec3; +import org.jetbrains.annotations.Nullable; public class PrincessEntity extends RoyalMountEntity { public PrincessEntity(EntityType entityType, Level level) { @@ -24,11 +32,120 @@ public class PrincessEntity extends RoyalMountEntity { @Override protected RoyalBeamElement boltElement(int lateralIndex) { - return switch (lateralIndex) { - case -1 -> RoyalBeamElement.FIRE; - case 1 -> RoyalBeamElement.ICE; - default -> RoyalBeamElement.LIGHTNING; - }; + return RoyalBeamElement.GENERIC; + } + + @Override + protected Vec3 boltOrigin(int lateralIndex) { + float scale = this.getGrowthScale(); + Vec3 forward = Vec3.directionFromRotation(0.0F, this.getYRot()); + Vec3 side = new Vec3(-forward.z, 0.0D, forward.x); + return this.position() + .add(0.0D, (lateralIndex == 0 ? 0.4D : 0.33D) * scale, 0.0D) + .add(forward.scale((lateralIndex == 0 ? 0.32D : 0.23D) * scale + this.getBbWidth() * 0.15D)) + .add(side.scale(lateralIndex * 0.24D * scale)); + } + + @Override + protected int nextBeamHead(int previous) { + return 1; + } + + @Override + protected ParticleOptions beamWindupParticle() { + return ParticleTypes.REVERSE_PORTAL; + } + + @Override + protected float beamBaseDamage() { + return 10.0F; + } + + @Override + public boolean prefersAerialCombat() { + return this.canFly(); + } + + @Override + public double preferredCombatRange() { + return 8.0D + 1.5D * this.getGrowthScale(); + } + + @Override + protected float biteWeight() { + return 1.0F; + } + + @Override + protected float spitWeight() { + return 2.5F; + } + + @Override + protected float beamWeight() { + return 1.6F; + } + + @Override + protected float signatureWeight() { + return 2.5F; + } + + @Override + protected int signatureCooldownTicks() { + return 300; + } + + private double fieldRadius() { + return 5.0D + 0.8D * this.getGrowthScale(); + } + + private boolean ownerInside(Vec3 center) { + LivingEntity owner = this.getOwner(); + if (owner == null || owner.level() != this.level() || this.hasPassenger(owner)) { + return false; + } + double clearance = this.fieldRadius() + 1.5D; + return owner.position().distanceToSqr(center) < clearance * clearance; + } + + @Override + protected boolean isSignatureUseful(LivingEntity target) { + return this.combatGap(target) < 10.0D + 1.5D * this.getGrowthScale() && !this.ownerInside(target.position()); + } + + private Vec3 fieldCenter(@Nullable LivingEntity target, @Nullable Vec3 aimPoint) { + if (target != null && target.isAlive()) { + return target.position(); + } + return aimPoint != null ? aimPoint : this.position().add(Vec3.directionFromRotation(0.0F, this.getYRot()).scale(this.getBbWidth() + 6.0D)); + } + + @Override + protected void signatureTelegraph(ServerLevel level, int tick, @Nullable LivingEntity target, @Nullable Vec3 aimPoint) { + if (tick % 2 != 0) { + return; + } + Vec3 center = this.fieldCenter(target, aimPoint); + double radius = this.fieldRadius() * (1.0D - tick / 24.0D) + 0.5D; + for (int i = 0; i < 12; i++) { + double angle = i * Mth.TWO_PI / 12.0D - tick * 0.15D; + level.sendParticles(ParticleTypes.REVERSE_PORTAL, center.x + Math.cos(angle) * radius, center.y + 0.2D, + center.z + Math.sin(angle) * radius, 1, 0.0D, 0.05D, 0.0D, 0.0D); + } + Vec3 orb = this.boltOrigin(0); + level.sendParticles(ParticleTypes.END_ROD, orb.x, orb.y, orb.z, 2, 0.1D, 0.1D, 0.1D, 0.01D); + } + + @Override + protected boolean performSignature(ServerLevel level, @Nullable LivingEntity target, @Nullable Vec3 aimPoint, boolean ridden) { + Vec3 center = this.fieldCenter(target, aimPoint); + if (this.ownerInside(center)) { + return false; + } + TimeDilationApi.createField(level, center, this.fieldRadius(), 0.35D, 100 + 10 * Mth.floor(this.getGrowthScale()), this.getUUID()); + level.sendParticles(ParticleTypes.END_ROD, center.x, center.y + 0.5D, center.z, 36, this.fieldRadius() * 0.3D, 0.6D, this.fieldRadius() * 0.3D, 0.03D); + return true; } @Override diff --git a/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/RoyalBoltEntity.java b/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/RoyalBoltEntity.java index 33a6e5d8..10665df2 100644 --- a/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/RoyalBoltEntity.java +++ b/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/RoyalBoltEntity.java @@ -86,6 +86,14 @@ public class RoyalBoltEntity extends AbstractHurtingProjectile { }; } + @Override + protected boolean canHitEntity(Entity target) { + if (this.getOwner() instanceof RoyalMountEntity mount && mount.isFriendlyTo(target)) { + return false; + } + return super.canHitEntity(target); + } + @Override protected void onHitEntity(EntityHitResult result) { super.onHitEntity(result); @@ -101,7 +109,13 @@ public class RoyalBoltEntity extends AbstractHurtingProjectile { && royal.isTame() && other.isTame()) { return; } + if (owner instanceof RoyalMountEntity mount && mount.isFriendlyTo(target)) { + return; + } float damage = (float) AntarchySettings.royalBoltDamage(); + if (owner instanceof RoyalMountEntity mount) { + damage *= mount.projectileDamageMultiplier(); + } target.hurt(this.damageSources().mobProjectile(this, owner instanceof LivingEntity le ? le : null), damage); if (target instanceof LivingEntity living) { living.knockback(0.35D, this.getX() - target.getX(), this.getZ() - target.getZ()); @@ -125,6 +139,9 @@ public class RoyalBoltEntity extends AbstractHurtingProjectile { } } case GENERIC -> { + if (owner instanceof PrincessEntity) { + living.addEffect(new MobEffectInstance(MobEffects.MOVEMENT_SLOWDOWN, 60, 1), owner); + } } } } diff --git a/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/RoyalBossEntity.java b/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/RoyalBossEntity.java index 5545692a..bed01511 100644 --- a/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/RoyalBossEntity.java +++ b/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/RoyalBossEntity.java @@ -2006,7 +2006,8 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult if (source.is(DamageTypes.GENERIC_KILL)) { return super.hurt(source, amount); } - float cappedAmount = (float) Math.min(amount, AntarchySettings.royalBossMaxSingleHitDamage()); + float cap = (float) AntarchySettings.royalBossMaxSingleHitDamage(); + float cappedAmount = amount <= cap ? amount : cap + (amount - cap) * (float) AntarchySettings.royalBossHitOverflowRate(); return super.hurt(source, cappedAmount); } diff --git a/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/RoyalMountCombatGoal.java b/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/RoyalMountCombatGoal.java new file mode 100644 index 00000000..dfa6df54 --- /dev/null +++ b/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/RoyalMountCombatGoal.java @@ -0,0 +1,239 @@ +package com.craisinlord.antarchy.content.entity.royal; + +import java.util.EnumSet; +import net.minecraft.util.Mth; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.ai.goal.Goal; +import net.minecraft.world.phys.Vec3; + +public class RoyalMountCombatGoal extends Goal { + private static final int FLIGHT_DECISION_INTERVAL = 40; + + private final RoyalMountEntity mount; + private LivingEntity target; + private int repathTicks; + private int flightDecisionTicks; + private boolean wantsFlight; + private int stuckTicks; + private Vec3 lastCheckPosition = Vec3.ZERO; + private float orbitAngle; + private int orbitDirection = 1; + private int diveTicks; + private int unseenTicks; + + public RoyalMountCombatGoal(RoyalMountEntity mount) { + this.mount = mount; + this.setFlags(EnumSet.of(Goal.Flag.MOVE, Goal.Flag.LOOK)); + } + + @Override + public boolean canUse() { + LivingEntity candidate = this.mount.getTarget(); + if (!this.isValid(candidate)) { + return false; + } + this.target = candidate; + return true; + } + + @Override + public boolean canContinueToUse() { + LivingEntity current = this.mount.getTarget(); + if (current != this.target || !this.isValid(current)) { + return false; + } + double range = this.mount.getAttributeValue(net.minecraft.world.entity.ai.attributes.Attributes.FOLLOW_RANGE) * 1.2D; + return this.mount.distanceToSqr(current) < range * range && this.unseenTicks < 200; + } + + private boolean isValid(LivingEntity candidate) { + return candidate != null && candidate.isAlive() && !this.mount.isVehicle() && !this.mount.isOrderedToSit() + && candidate.level() == this.mount.level() && this.mount.canAttack(candidate); + } + + @Override + public void start() { + this.repathTicks = 0; + this.flightDecisionTicks = 0; + this.wantsFlight = false; + this.stuckTicks = 0; + this.unseenTicks = 0; + this.diveTicks = 0; + this.lastCheckPosition = this.mount.position(); + Vec3 offset = this.mount.position().subtract(this.target.position()); + this.orbitAngle = (float) Mth.atan2(offset.z, offset.x); + this.orbitDirection = this.mount.getRandom().nextBoolean() ? 1 : -1; + } + + @Override + public void stop() { + this.target = null; + this.mount.getNavigation().stop(); + } + + @Override + public boolean requiresUpdateEveryTick() { + return true; + } + + @Override + public void tick() { + LivingEntity current = this.target; + if (current == null) { + return; + } + boolean canSee = this.mount.getSensing().hasLineOfSight(current); + this.unseenTicks = canSee ? 0 : this.unseenTicks + 1; + double gap = this.mount.combatGap(current); + this.mount.getLookControl().setLookAt(current, 30.0F, 30.0F); + this.trackStuck(); + this.updateFlightDecision(current, gap); + + if (this.mount.isActing()) { + this.holdDuringAction(current); + return; + } + + int chosen = this.mount.chooseCombatAction(current, gap, canSee); + if (chosen != RoyalMountEntity.ACTION_NONE && this.isFacing(current, this.facingTolerance(chosen))) { + if (this.mount.startAction(chosen, current, current.getEyePosition(), false)) { + this.diveTicks = 0; + this.holdDuringAction(current); + return; + } + } + if (chosen != RoyalMountEntity.ACTION_NONE) { + this.mount.turnTowards(current.position(), 18.0F); + } + + if (this.mount.isFlying()) { + this.tickAerial(current, gap); + } else { + this.tickGround(current, gap, canSee); + } + } + + private float facingTolerance(int action) { + return switch (action) { + case RoyalMountEntity.ACTION_BITE -> 50.0F; + case RoyalMountEntity.ACTION_SIGNATURE -> 180.0F; + default -> 120.0F; + }; + } + + private boolean isFacing(LivingEntity current, float tolerance) { + Vec3 toTarget = current.position().subtract(this.mount.position()); + float desired = (float) (Mth.atan2(toTarget.z, toTarget.x) * Mth.RAD_TO_DEG) - 90.0F; + return Math.abs(Mth.wrapDegrees(desired - this.mount.getYRot())) <= tolerance; + } + + private void holdDuringAction(LivingEntity current) { + this.mount.getNavigation().stop(); + if (this.mount.isFlying()) { + this.mount.claimFlight(); + if (this.mount.currentAction() == RoyalMountEntity.ACTION_BITE) { + this.mount.getMoveControl().setWantedPosition(current.getX(), current.getY() + current.getBbHeight() * 0.5D, current.getZ(), 0.6D); + } else { + this.mount.getMoveControl().setWantedPosition(this.mount.getX(), this.mount.getY(), this.mount.getZ(), 0.4D); + } + } else if (this.mount.currentAction() != RoyalMountEntity.ACTION_BITE) { + this.mount.setDeltaMovement(this.mount.getDeltaMovement().multiply(0.5D, 1.0D, 0.5D)); + } + } + + private void trackStuck() { + if (this.mount.tickCount % 20 != 0) { + return; + } + Vec3 position = this.mount.position(); + boolean trying = !this.mount.getNavigation().isDone() || this.mount.isFlying(); + if (trying && position.distanceToSqr(this.lastCheckPosition) < 1.0D) { + this.stuckTicks += 20; + } else { + this.stuckTicks = 0; + } + this.lastCheckPosition = position; + } + + private void updateFlightDecision(LivingEntity current, double gap) { + if (!this.mount.canFly()) { + this.wantsFlight = false; + return; + } + if (--this.flightDecisionTicks <= 0 || this.stuckTicks >= 40) { + this.flightDecisionTicks = FLIGHT_DECISION_INTERVAL; + boolean targetAbove = current.getY() - this.mount.getY() > 4.0D + this.mount.getBbHeight() * 0.25D; + boolean targetAirborne = !current.onGround() && !current.isInWater() && RoyalMountEntity.isAirborne(current) || targetAbove; + boolean groundBlocked = !this.mount.isFlying() && (this.stuckTicks >= 40 + || this.mount.getNavigation().isDone() && gap > 4.0D && this.mount.getNavigation().createPath(current, 0) == null); + boolean prefersAir = this.mount.prefersAerialCombat() && gap > 3.0D; + this.wantsFlight = targetAirborne || groundBlocked || prefersAir || (this.mount.isFlying() && this.diveTicks > 0); + if (this.stuckTicks >= 40) { + this.stuckTicks = 0; + } + } + if (this.wantsFlight) { + this.mount.claimFlight(); + } + } + + private void tickAerial(LivingEntity current, double gap) { + if (!this.wantsFlight) { + return; + } + this.mount.claimFlight(); + float scale = this.mount.getGrowthScale(); + boolean wantsBite = this.mount.isActionAvailable(RoyalMountEntity.ACTION_BITE) + && (!this.mount.prefersAerialCombat() || gap < this.mount.preferredCombatRange() * 0.5D || this.diveTicks > 0); + if (wantsBite && this.mount.isActionReady()) { + this.diveTicks++; + Vec3 approach = current.position().subtract(this.mount.position()).multiply(1.0D, 0.0D, 1.0D); + Vec3 offset = approach.lengthSqr() > 1.0E-4D ? approach.normalize().scale(-(this.mount.getBbWidth() * 0.5D + 0.6D * scale)) : Vec3.ZERO; + this.mount.getMoveControl().setWantedPosition(current.getX() + offset.x, current.getY() + 0.2D, current.getZ() + offset.z, 1.4D); + if (this.diveTicks > 80) { + this.diveTicks = 0; + this.orbitDirection = -this.orbitDirection; + } + return; + } + this.diveTicks = 0; + double radius = Math.max(6.0D + 1.5D * scale, this.mount.preferredCombatRange()) + this.mount.getBbWidth() * 0.5D; + this.orbitAngle += this.orbitDirection * (float) Math.min(0.08D, 1.2D / radius); + double x = current.getX() + Math.cos(this.orbitAngle) * radius; + double z = current.getZ() + Math.sin(this.orbitAngle) * radius; + double y = current.getY() + 3.0D + 0.8D * scale; + if (!this.mount.canOccupy(x, y, z)) { + this.orbitDirection = -this.orbitDirection; + y += 2.0D + scale; + } + this.mount.getMoveControl().setWantedPosition(x, y, z, 1.1D); + } + + private void tickGround(LivingEntity current, double gap, boolean canSee) { + double preferred = this.mount.preferredCombatRange(); + boolean ranged = preferred > 0.0D && this.mount.canSpit(); + if (ranged && canSee && gap < preferred * 0.5D && !this.mount.isActionAvailable(RoyalMountEntity.ACTION_BITE)) { + Vec3 away = this.mount.position().subtract(current.position()).multiply(1.0D, 0.0D, 1.0D); + if (away.lengthSqr() > 1.0E-4D && --this.repathTicks <= 0) { + this.repathTicks = 10; + Vec3 destination = this.mount.position().add(away.normalize().scale(4.0D + this.mount.getBbWidth() * 0.5D)); + this.mount.getNavigation().moveTo(destination.x, destination.y, destination.z, 1.2D); + } + return; + } + if (ranged && canSee && gap <= preferred && gap >= preferred * 0.5D && !this.mount.isActionAvailable(RoyalMountEntity.ACTION_BITE)) { + this.mount.getNavigation().stop(); + this.mount.turnTowards(current.position(), 12.0F); + return; + } + if (this.mount.canBiteReach(current)) { + this.mount.getNavigation().stop(); + this.mount.turnTowards(current.position(), 18.0F); + return; + } + if (--this.repathTicks <= 0) { + this.repathTicks = 6 + this.mount.getRandom().nextInt(6); + this.mount.getNavigation().moveTo(current, 1.2D); + } + } +} diff --git a/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/RoyalMountCompanionGoal.java b/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/RoyalMountCompanionGoal.java new file mode 100644 index 00000000..d715ac1a --- /dev/null +++ b/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/RoyalMountCompanionGoal.java @@ -0,0 +1,175 @@ +package com.craisinlord.antarchy.content.entity.royal; + +import java.util.EnumSet; +import net.minecraft.util.Mth; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.ai.goal.Goal; +import net.minecraft.world.phys.Vec3; + +public class RoyalMountCompanionGoal extends Goal { + private static final double MAX_OWNER_DISTANCE = 40.0D; + + private final RoyalMountEntity mount; + private LivingEntity owner; + private int side = 1; + private float orbitAngle; + private int repathTicks; + private int settledTicks; + private int cooldown; + private Vec3 spot; + + public RoyalMountCompanionGoal(RoyalMountEntity mount) { + this.mount = mount; + this.setFlags(EnumSet.of(Goal.Flag.MOVE, Goal.Flag.LOOK)); + } + + private boolean isEligible(LivingEntity candidate) { + return candidate != null && candidate.isAlive() && !candidate.isSpectator() + && candidate.level() == this.mount.level() + && this.mount.isTame() && !this.mount.isVehicle() && !this.mount.isOrderedToSit() + && !this.mount.isLeashed() && this.mount.getTarget() == null && !this.mount.isActing() + && this.mount.distanceToSqr(candidate) < MAX_OWNER_DISTANCE * MAX_OWNER_DISTANCE; + } + + private double settleDistance() { + return 4.0D + this.mount.getBbWidth() * 0.6D; + } + + private boolean needsToReposition(LivingEntity candidate) { + if (this.mount.canFly() && RoyalMountEntity.isAirborne(candidate)) { + return true; + } + if (this.mount.isFlying()) { + return true; + } + double settle = this.settleDistance() + 2.0D; + return this.mount.distanceToSqr(candidate) > settle * settle; + } + + @Override + public boolean canUse() { + if (this.cooldown > 0) { + this.cooldown--; + return false; + } + LivingEntity candidate = this.mount.getOwner(); + if (!this.isEligible(candidate) || !this.needsToReposition(candidate)) { + return false; + } + this.owner = candidate; + return true; + } + + @Override + public boolean canContinueToUse() { + return this.isEligible(this.owner) && this.owner == this.mount.getOwner() && this.settledTicks < 40; + } + + @Override + public void start() { + this.side = this.mount.getRandom().nextBoolean() ? 1 : -1; + this.repathTicks = 0; + this.settledTicks = 0; + this.spot = null; + Vec3 offset = this.mount.position().subtract(this.owner.position()); + this.orbitAngle = (float) Mth.atan2(offset.z, offset.x); + } + + @Override + public void stop() { + this.owner = null; + this.spot = null; + this.cooldown = 20; + this.mount.getNavigation().stop(); + } + + @Override + public boolean requiresUpdateEveryTick() { + return true; + } + + @Override + public void tick() { + float scale = this.mount.getGrowthScale(); + if (this.mount.canFly() && RoyalMountEntity.isAirborne(this.owner)) { + this.settledTicks = 0; + this.spot = null; + this.mount.claimFlight(); + this.circleOwner(scale); + return; + } + + if (--this.repathTicks <= 0 || this.spot == null) { + this.repathTicks = 20; + this.spot = this.pickSpotBesideOwner(); + } + + if (this.mount.isFlying()) { + this.settledTicks = 0; + this.mount.getLookControl().setLookAt(this.owner, 10.0F, this.mount.getMaxHeadXRot()); + if (this.mount.onGround() || this.mount.isInWater()) { + this.mount.setFlying(false); + return; + } + this.mount.claimFlight(); + Vec3 target = this.spot != null ? this.spot : this.owner.position(); + double horizontal = Math.sqrt(Mth.square(target.x - this.mount.getX()) + Mth.square(target.z - this.mount.getZ())); + double y = horizontal > 3.0D + scale ? Math.max(target.y + 1.5D * scale, this.mount.getY() - 2.0D) : target.y - 0.5D; + this.mount.getMoveControl().setWantedPosition(target.x, y, target.z, 1.0D); + return; + } + + double settle = this.settleDistance(); + boolean nearOwner = this.mount.distanceToSqr(this.owner) <= settle * settle; + boolean nearSpot = this.spot != null && this.mount.position().distanceToSqr(this.spot) <= Mth.square(1.5D + 0.5D * scale); + if (nearOwner || nearSpot) { + this.mount.getNavigation().stop(); + if (this.settledTicks == 0) { + this.mount.turnTowards(this.mount.position().add(this.owner.getLookAngle().multiply(1.0D, 0.0D, 1.0D)), 30.0F); + if (this.mount.getRandom().nextInt(4) == 0) { + this.mount.triggerQuirk(); + } + } + this.mount.getLookControl().setLookAt(this.owner, 10.0F, this.mount.getMaxHeadXRot()); + this.settledTicks++; + return; + } + this.settledTicks = 0; + this.mount.getLookControl().setLookAt(this.owner, 10.0F, this.mount.getMaxHeadXRot()); + if (this.repathTicks == 20) { + Vec3 destination = this.spot != null ? this.spot : this.owner.position(); + if (!this.mount.getNavigation().moveTo(destination.x, destination.y, destination.z, 1.0D)) { + this.mount.getNavigation().moveTo(this.owner, 1.0D); + } + } + } + + private void circleOwner(float scale) { + double radius = 5.0D + this.mount.getBbWidth() * 0.75D; + this.orbitAngle += this.side * (float) Math.min(0.07D, 1.0D / radius); + double x = this.owner.getX() + Math.cos(this.orbitAngle) * radius; + double z = this.owner.getZ() + Math.sin(this.orbitAngle) * radius; + double y = this.owner.getY() + 2.0D + 0.6D * scale; + if (!this.mount.canOccupy(x, y, z)) { + y += 2.0D + scale; + } + this.mount.getMoveControl().setWantedPosition(x, y, z, 1.2D); + this.mount.getLookControl().setLookAt(this.owner, 10.0F, this.mount.getMaxHeadXRot()); + } + + private Vec3 pickSpotBesideOwner() { + Vec3 facing = Vec3.directionFromRotation(0.0F, this.owner.getYRot()); + Vec3 right = new Vec3(-facing.z, 0.0D, facing.x); + double distance = this.mount.getBbWidth() * 0.5D + 1.5D; + for (int attempt = 0; attempt < 2; attempt++) { + int currentSide = attempt == 0 ? this.side : -this.side; + Vec3 base = this.owner.position().add(right.scale(currentSide * distance)); + double ground = this.mount.findGroundY(base.x, this.owner.getY() + 3.0D, base.z, 10); + if (!Double.isNaN(ground) && this.mount.canOccupy(base.x, ground, base.z)) { + this.side = currentSide; + return new Vec3(base.x, ground, base.z); + } + } + return null; + } +} diff --git a/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/RoyalMountEntity.java b/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/RoyalMountEntity.java index 6cf4f91b..612285f2 100644 --- a/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/RoyalMountEntity.java +++ b/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/RoyalMountEntity.java @@ -1,21 +1,20 @@ package com.craisinlord.antarchy.content.entity.royal; -import com.craisinlord.antarchy.config.AntarchySettings; -import com.craisinlord.antarchy.content.AntarchySoundEvents; import com.craisinlord.antarchy.content.AntarchyTags; import com.craisinlord.antarchy.content.entity.royal.beam.RoyalBeamElement; import com.craisinlord.anthology.beam.BeamController; +import com.craisinlord.anthology.beam.BeamOwner; import com.craisinlord.anthology.beam.BeamSettings; import com.craisinlord.anthology.beam.BeamTerrainMode; -import com.craisinlord.antarchy.content.effect.RoyalEffectEligibility; -import com.craisinlord.antarchy.content.effect.RoyalEffectHooks; -import com.craisinlord.anthology.time.TimeDilationApi; -import net.minecraft.resources.ResourceLocation; import net.minecraft.core.BlockPos; +import net.minecraft.core.particles.ParticleOptions; +import net.minecraft.core.particles.ParticleTypes; import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.chat.Component; import net.minecraft.network.syncher.EntityDataAccessor; import net.minecraft.network.syncher.EntityDataSerializers; import net.minecraft.network.syncher.SynchedEntityData; +import net.minecraft.resources.ResourceLocation; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.sounds.SoundEvent; @@ -31,10 +30,12 @@ import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.Mob; import net.minecraft.world.entity.MoverType; +import net.minecraft.world.entity.OwnableEntity; import net.minecraft.world.entity.Pose; import net.minecraft.world.entity.TamableAnimal; -import net.minecraft.world.entity.ai.attributes.AttributeSupplier; +import net.minecraft.world.entity.ai.attributes.AttributeInstance; import net.minecraft.world.entity.ai.attributes.AttributeModifier; +import net.minecraft.world.entity.ai.attributes.AttributeSupplier; import net.minecraft.world.entity.ai.attributes.Attributes; import net.minecraft.world.entity.ai.control.FlyingMoveControl; import net.minecraft.world.entity.ai.control.MoveControl; @@ -49,19 +50,20 @@ import net.minecraft.world.entity.ai.goal.target.OwnerHurtTargetGoal; import net.minecraft.world.entity.ai.navigation.FlyingPathNavigation; import net.minecraft.world.entity.ai.navigation.GroundPathNavigation; import net.minecraft.world.entity.ai.navigation.PathNavigation; -import net.minecraft.world.entity.monster.Enemy; import net.minecraft.world.entity.animal.FlyingAnimal; +import net.minecraft.world.entity.monster.Creeper; +import net.minecraft.world.entity.monster.Enemy; import net.minecraft.world.entity.player.Player; +import net.minecraft.world.entity.projectile.ProjectileUtil; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; +import net.minecraft.world.level.ClipContext; import net.minecraft.world.level.Level; -import net.minecraft.server.level.ServerLevel; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.pathfinder.BlockPathTypes; import net.minecraft.world.phys.AABB; +import net.minecraft.world.phys.EntityHitResult; import net.minecraft.world.phys.Vec3; -import net.minecraft.world.effect.MobEffectInstance; -import net.minecraft.core.particles.ParticleTypes; import java.util.UUID; import org.jetbrains.annotations.Nullable; import software.bernie.geckolib.animatable.GeoEntity; @@ -74,9 +76,16 @@ import software.bernie.geckolib.core.object.PlayState; import software.bernie.geckolib.core.animation.RawAnimation; import software.bernie.geckolib.util.GeckoLibUtil; -public abstract class RoyalMountEntity extends TamableAnimal implements GeoEntity, FlyingAnimal { +public abstract class RoyalMountEntity extends TamableAnimal implements GeoEntity, FlyingAnimal, BeamOwner { private static final ResourceLocation GROWTH_HEALTH_MODIFIER = new ResourceLocation("antarchy", "royal_mount_growth_health"); + private static final ResourceLocation GROWTH_DAMAGE_MODIFIER = new ResourceLocation("antarchy", "royal_mount_growth_damage"); + private static final ResourceLocation GROWTH_SPEED_MODIFIER = new ResourceLocation("antarchy", "royal_mount_growth_speed"); + private static final ResourceLocation GROWTH_FLYING_SPEED_MODIFIER = new ResourceLocation("antarchy", "royal_mount_growth_flying_speed"); private static final UUID GROWTH_HEALTH_MODIFIER_UUID = UUID.nameUUIDFromBytes(GROWTH_HEALTH_MODIFIER.toString().getBytes()); + private static final UUID GROWTH_DAMAGE_MODIFIER_UUID = UUID.nameUUIDFromBytes(GROWTH_DAMAGE_MODIFIER.toString().getBytes()); + private static final UUID GROWTH_SPEED_MODIFIER_UUID = UUID.nameUUIDFromBytes(GROWTH_SPEED_MODIFIER.toString().getBytes()); + private static final UUID GROWTH_FLYING_SPEED_MODIFIER_UUID = UUID.nameUUIDFromBytes(GROWTH_FLYING_SPEED_MODIFIER.toString().getBytes()); + public static final int ANIM_IDLE = 0; public static final int ANIM_WALK = 1; public static final int ANIM_FLY = 2; @@ -85,6 +94,22 @@ public abstract class RoyalMountEntity extends TamableAnimal implements GeoEntit public static final int ANIM_SHOOT = 5; public static final int ANIM_FLY_SHOOT = 6; + public static final int ACTION_NONE = 0; + public static final int ACTION_BITE = 1; + public static final int ACTION_SPIT = 2; + public static final int ACTION_BEAM = 3; + public static final int ACTION_SIGNATURE = 4; + + public static final float MAX_SCALE = 5.0F; + public static final float JUVENILE_PROGRESS = 0.25F; + public static final float ADULT_PROGRESS = 0.75F; + private static final float GOLDEN_APPLE_GROWTH = 0.025F; + private static final float ENCHANTED_APPLE_GROWTH = 0.125F; + + private static final double SEAT_HEIGHT = 17.0D / 16.0D; + private static final double SEAT_BACK = 2.0D / 16.0D; + private static final double FLY_BOB_HEIGHT = 9.0D / 16.0D; + private static final EntityDataAccessor ANIM_STATE = SynchedEntityData.defineId(RoyalMountEntity.class, EntityDataSerializers.INT); private static final EntityDataAccessor FLYING = @@ -117,33 +142,49 @@ public abstract class RoyalMountEntity extends TamableAnimal implements GeoEntit private static final RawAnimation SHOOT_ANIM = RawAnimation.begin().then("shoot", Animation.LoopType.PLAY_ONCE); private static final RawAnimation FLY_SHOOT_ANIM = RawAnimation.begin().then("fly_shoot", Animation.LoopType.PLAY_ONCE); - private static final int ACTION_TICKS = 12; + private static final int BITE_TICKS = 12; private static final int BITE_HIT_TICK = 6; + private static final int BITE_COOLDOWN_TICKS = 14; + private static final int SPIT_TICKS = 20; + private static final int SPIT_FIRE_TICK = 8; + private static final int SPIT_COOLDOWN_TICKS = 50; private static final int BEAM_WINDUP_TICKS = 24; + private static final int BEAM_DURATION_TICKS = 14; + private static final int BEAM_RECOVERY_TICKS = 10; private static final int BEAM_COOLDOWN_TICKS = 240; - private static final int BEAM_DURATION_TICKS = 12; - private static final int COMMAND_COOLDOWN_TICKS = 360; - private static final int TIME_FIELD_COOLDOWN_TICKS = 260; + private static final int SIGNATURE_WINDUP_TICKS = 20; + private static final int SIGNATURE_TICKS = 30; + private static final int ACTION_RECOVERY_TICKS = 8; private final AnimatableInstanceCache geoCache = GeckoLibUtil.createInstanceCache(this); private final MoveControl groundMoveControl; private final MoveControl flyingMoveControl; private final PathNavigation groundNavigation; private final PathNavigation flyingNavigation; + private final BeamController beamController = new BeamController(this); + private int action = ACTION_NONE; private int actionTicks; - private boolean actionHit; - private int spitCooldown; + private boolean actionFired; + private boolean actionRidden; + @Nullable + private LivingEntity actionTarget; + @Nullable + private Vec3 actionAimPoint; + private int actionRecoveryTicks; + private int lastAction = ACTION_NONE; + private int biteCooldownTicks; + private int spitCooldownTicks; + private int beamCooldownTicks; + private int signatureCooldownTicks; + private int beamHead; + private int quirkTimer; private int flapSoundTimer; + private int flightClaimTick = -100; + private int ownerAwayTicks; private boolean riderAscendPressed; private boolean riderDescendPressed; - private final BeamController beamController = new BeamController(this); - private int beamWindupTicks; - private int beamCooldownTicks; - private int beamHead; - private int commandCooldownTicks; - private int timeFieldCooldownTicks; protected RoyalMountEntity(EntityType entityType, Level level) { super(entityType, level); @@ -173,6 +214,22 @@ public abstract class RoyalMountEntity extends TamableAnimal implements GeoEntit .add(Attributes.ARMOR, armor); } + public enum Stage { + HATCHLING("hatchling"), + JUVENILE("juvenile"), + ADULT("adult"); + + private final String id; + + Stage(String id) { + this.id = id; + } + + public Component displayName() { + return Component.translatable("entity.antarchy.royal_mount.stage." + this.id); + } + } + protected abstract String geoName(); protected abstract EntityType boltType(); @@ -187,6 +244,34 @@ public abstract class RoyalMountEntity extends TamableAnimal implements GeoEntit protected abstract SoundEvent stepSound(); + protected abstract int signatureCooldownTicks(); + + protected abstract boolean isSignatureUseful(LivingEntity target); + + protected abstract void signatureTelegraph(ServerLevel level, int tick, @Nullable LivingEntity target, @Nullable Vec3 aimPoint); + + protected abstract boolean performSignature(ServerLevel level, @Nullable LivingEntity target, @Nullable Vec3 aimPoint, boolean ridden); + + protected abstract float beamBaseDamage(); + + protected abstract ParticleOptions beamWindupParticle(); + + public abstract boolean prefersAerialCombat(); + + public abstract double preferredCombatRange(); + + protected abstract float biteWeight(); + + protected abstract float spitWeight(); + + protected abstract float beamWeight(); + + protected abstract float signatureWeight(); + + protected int nextBeamHead(int previous) { + return (previous + 1) % 3; + } + @Override protected SoundEvent getAmbientSound() { return this.idleSound(); @@ -194,7 +279,7 @@ public abstract class RoyalMountEntity extends TamableAnimal implements GeoEntit @Override protected void playStepSound(BlockPos pos, BlockState state) { - this.playSound(this.stepSound(), 0.16F, 1.0F + (this.random.nextFloat() - 0.5F) * 0.2F); + this.playSound(this.stepSound(), 0.16F * this.getGrowthScale(), 1.1F - 0.06F * this.getGrowthScale() + (this.random.nextFloat() - 0.5F) * 0.2F); } @Override @@ -207,61 +292,37 @@ public abstract class RoyalMountEntity extends TamableAnimal implements GeoEntit @Override protected void registerGoals() { this.goalSelector.addGoal(0, new FloatGoal(this)); - this.goalSelector.addGoal(1, new SitWhenOrderedToGoal(this) { - @Override - public boolean canUse() { - return super.canUse(); - } - }); - this.goalSelector.addGoal(2, new RoyalMountSpitGoal(this)); - this.goalSelector.addGoal(3, new net.minecraft.world.entity.ai.goal.MeleeAttackGoal(this, 1.2D, true) { - @Override - protected void checkAndPerformAttack(LivingEntity target, double distance) { - if (this.isTimeToAttack() && distance <= this.getAttackReachSqr(target) - && this.mob.getSensing().hasLineOfSight(target)) { - this.resetAttackCooldown(); - RoyalMountEntity.this.startBite(); - } - } - - @Override - public boolean canUse() { - return !RoyalMountEntity.this.isVehicle() && super.canUse(); - } - }); + this.goalSelector.addGoal(1, new SitWhenOrderedToGoal(this)); + this.goalSelector.addGoal(2, new RoyalMountCombatGoal(this)); this.goalSelector.addGoal(4, new RoyalMountFollowOwnerGoal(this)); + this.goalSelector.addGoal(5, new RoyalMountCompanionGoal(this)); this.goalSelector.addGoal(6, new WaterAvoidingRandomStrollGoal(this, 0.8D) { @Override public boolean canUse() { - return !RoyalMountEntity.this.isVehicle() - && !RoyalMountEntity.this.isOrderedToSit() && super.canUse(); + return RoyalMountEntity.this.canWanderFreely() && super.canUse(); + } + + @Override + public boolean canContinueToUse() { + return RoyalMountEntity.this.canWanderFreely() && super.canContinueToUse(); } }); this.goalSelector.addGoal(7, new LookAtPlayerGoal(this, Player.class, 12.0F)); - this.goalSelector.addGoal(8, new RandomLookAroundGoal(this) { - @Override - public boolean canUse() { - return super.canUse(); - } - }); - this.targetSelector.addGoal(1, new OwnerHurtByTargetGoal(this) { - @Override - public boolean canUse() { - return super.canUse(); - } - }); - this.targetSelector.addGoal(2, new OwnerHurtTargetGoal(this) { - @Override - public boolean canUse() { - return super.canUse(); - } - }); - this.targetSelector.addGoal(3, new HurtByTargetGoal(this) { - @Override - public boolean canUse() { - return super.canUse(); - } - }.setAlertOthers()); + this.goalSelector.addGoal(8, new RandomLookAroundGoal(this)); + this.targetSelector.addGoal(1, new OwnerHurtByTargetGoal(this)); + this.targetSelector.addGoal(2, new OwnerHurtTargetGoal(this)); + this.targetSelector.addGoal(3, new HurtByTargetGoal(this)); + } + + private boolean canWanderFreely() { + if (this.isVehicle() || this.isOrderedToSit() || this.isFlying() || this.isActing()) { + return false; + } + if (!this.isTame()) { + return true; + } + LivingEntity owner = this.getOwner(); + return owner == null || owner.level() != this.level() || this.distanceToSqr(owner) > 48.0D * 48.0D; } @Override @@ -286,8 +347,7 @@ public abstract class RoyalMountEntity extends TamableAnimal implements GeoEntit tag.putBoolean("Saddled", this.isSaddled()); tag.putBoolean("Flying", this.isFlying()); tag.putInt("BeamCooldown", this.beamCooldownTicks); - tag.putInt("CommandCooldown", this.commandCooldownTicks); - tag.putInt("TimeFieldCooldown", this.timeFieldCooldownTicks); + tag.putInt("SignatureCooldown", this.signatureCooldownTicks); tag.putFloat("GrowthProgress", this.getGrowthProgress()); } @@ -295,18 +355,20 @@ public abstract class RoyalMountEntity extends TamableAnimal implements GeoEntit public void readAdditionalSaveData(CompoundTag tag) { super.readAdditionalSaveData(tag); this.setSaddled(tag.getBoolean("Saddled")); - this.setFlying(tag.getBoolean("Flying")); - this.actionTicks = 0; - this.actionHit = false; - this.spitCooldown = 0; + this.clearAction(); + this.biteCooldownTicks = 0; + this.spitCooldownTicks = 0; + this.actionRecoveryTicks = 0; this.beamCooldownTicks = Math.max(0, tag.getInt("BeamCooldown")); - this.commandCooldownTicks = Math.max(0, tag.getInt("CommandCooldown")); - this.timeFieldCooldownTicks = Math.max(0, tag.getInt("TimeFieldCooldown")); + int signatureCooldown = tag.contains("SignatureCooldown") + ? tag.getInt("SignatureCooldown") + : Math.max(tag.getInt("CommandCooldown"), tag.getInt("TimeFieldCooldown")); + this.signatureCooldownTicks = Math.max(0, signatureCooldown); this.setGrowthProgress(tag.contains("GrowthProgress") ? tag.getFloat("GrowthProgress") : 0.0F); - } - - public boolean isFlying() { - return this.entityData.get(FLYING); + if (tag.contains("Health", 99)) { + this.setHealth(tag.getFloat("Health")); + } + this.setFlying(tag.getBoolean("Flying") && this.canFly()); } @Override @@ -323,10 +385,15 @@ public abstract class RoyalMountEntity extends TamableAnimal implements GeoEntit } } + public boolean isFlying() { + return this.entityData.get(FLYING); + } + public void setFlying(boolean flying) { if (this.entityData.get(FLYING) != flying) { this.entityData.set(FLYING, flying); this.moveControl = flying ? this.flyingMoveControl : this.groundMoveControl; + this.navigation.stop(); this.navigation = flying ? this.flyingNavigation : this.groundNavigation; this.setNoGravity(flying); if (flying && !this.level().isClientSide) { @@ -335,6 +402,15 @@ public abstract class RoyalMountEntity extends TamableAnimal implements GeoEntit } } + public boolean claimFlight() { + if (!this.canFly()) { + return false; + } + this.setFlying(true); + this.flightClaimTick = this.tickCount; + return true; + } + public boolean isSaddled() { return this.entityData.get(SADDLED); } @@ -352,35 +428,91 @@ public abstract class RoyalMountEntity extends TamableAnimal implements GeoEntit } public float getGrowthScale() { - return 1.0F + 14.0F * this.getGrowthProgress(); + return 1.0F + (MAX_SCALE - 1.0F) * this.getGrowthProgress(); } public float getGrowthProgress() { return this.entityData.get(GROWTH_PROGRESS); } + public Stage getStage() { + float progress = this.getGrowthProgress(); + if (progress >= ADULT_PROGRESS) { + return Stage.ADULT; + } + return progress >= JUVENILE_PROGRESS ? Stage.JUVENILE : Stage.HATCHLING; + } + + public boolean canBeRidden() { + return this.getStage() != Stage.HATCHLING; + } + + public boolean canFly() { + return this.getStage() == Stage.ADULT; + } + + public boolean canSpit() { + return this.getStage() != Stage.HATCHLING; + } + + public boolean canUseAdultAbilities() { + return this.getStage() == Stage.ADULT; + } + public void setGrowthProgress(float progress) { float clamped = Mth.clamp(progress, 0.0F, 1.0F); if (this.getGrowthProgress() != clamped) { float oldHealth = this.getHealth(); this.entityData.set(GROWTH_PROGRESS, clamped); - var maxHealth = this.getAttribute(Attributes.MAX_HEALTH); - if (maxHealth != null) { - maxHealth.removeModifier(GROWTH_HEALTH_MODIFIER_UUID); - if (clamped > 0.0F) { - maxHealth.addTransientModifier(new AttributeModifier(GROWTH_HEALTH_MODIFIER_UUID, GROWTH_HEALTH_MODIFIER.toString(), - this.getGrowthScale() - 1.0D, AttributeModifier.Operation.MULTIPLY_BASE)); - } - this.setHealth(Math.min(oldHealth, this.getMaxHealth())); - } + this.applyGrowthModifiers(); + this.setHealth(Math.min(oldHealth, this.getMaxHealth())); this.refreshDimensions(); } } + private void applyGrowthModifiers() { + float bonus = this.getGrowthScale() - 1.0F; + this.applyGrowthModifier(this.getAttribute(Attributes.MAX_HEALTH), GROWTH_HEALTH_MODIFIER, GROWTH_HEALTH_MODIFIER_UUID, bonus); + this.applyGrowthModifier(this.getAttribute(Attributes.ATTACK_DAMAGE), GROWTH_DAMAGE_MODIFIER, GROWTH_DAMAGE_MODIFIER_UUID, bonus * 0.5D); + this.applyGrowthModifier(this.getAttribute(Attributes.MOVEMENT_SPEED), GROWTH_SPEED_MODIFIER, GROWTH_SPEED_MODIFIER_UUID, bonus * 0.1D); + this.applyGrowthModifier(this.getAttribute(Attributes.FLYING_SPEED), GROWTH_FLYING_SPEED_MODIFIER, GROWTH_FLYING_SPEED_MODIFIER_UUID, bonus * 0.1D); + } + + private void applyGrowthModifier(@Nullable AttributeInstance attribute, ResourceLocation id, UUID uuid, double amount) { + if (attribute == null) { + return; + } + attribute.removeModifier(uuid); + if (amount > 0.0D) { + attribute.addTransientModifier(new AttributeModifier(uuid, id.toString(), amount, AttributeModifier.Operation.MULTIPLY_BASE)); + } + } + + private void grow(float amount, @Nullable Player feeder) { + Stage before = this.getStage(); + this.setGrowthProgress(this.getGrowthProgress() + amount); + Stage after = this.getStage(); + if (after != before && this.level() instanceof ServerLevel level) { + double radius = this.getBbWidth() * 0.5D; + level.sendParticles(ParticleTypes.TOTEM_OF_UNDYING, this.getX(), this.getY() + this.getBbHeight() * 0.6D, this.getZ(), + 40, radius, this.getBbHeight() * 0.3D, radius, 0.3D); + this.playSound(this.idleSound(), 2.0F, 0.6F); + this.triggerQuirk(); + if (feeder != null) { + feeder.displayClientMessage(Component.translatable("message.antarchy.royal_mount.stage_up", + this.getDisplayName(), after.displayName()), true); + } + } else if (feeder != null) { + feeder.displayClientMessage(Component.translatable("message.antarchy.royal_mount.growth", + this.getDisplayName(), this.getStage().displayName(), Math.round(this.getGrowthProgress() * 100.0F)), true); + } + } + @Override public boolean hurt(DamageSource source, float amount) { Entity attacker = source.getEntity(); - if (attacker != null && this.hasPassenger(attacker)) { + if (attacker != null && attacker != this && (this.hasPassenger(attacker) + || attacker != this.getOwner() && this.isFriendlyTo(attacker))) { return false; } return super.hurt(source, amount); @@ -391,6 +523,11 @@ public abstract class RoyalMountEntity extends TamableAnimal implements GeoEntit return super.getDimensions(pose).scale(this.getGrowthScale()); } + @Override + public float maxUpStep() { + return Math.max(1.1F, 0.5F + 0.6F * this.getGrowthScale()); + } + @Override public boolean isFood(ItemStack stack) { return stack.is(AntarchyTags.Items.ROYAL_MOUNT_FOOD); @@ -399,49 +536,54 @@ public abstract class RoyalMountEntity extends TamableAnimal implements GeoEntit @Override public InteractionResult mobInteract(Player player, InteractionHand hand) { ItemStack stack = player.getItemInHand(hand); + boolean owned = this.isTame() && this.isOwnedBy(player); if (stack.is(Items.GOLDEN_APPLE) && !this.isTame()) { if (!this.level().isClientSide) { - if (!player.getAbilities().instabuild) { - stack.shrink(1); - } + this.consume(player, stack); this.tame(player); + this.setTarget(null); this.heal(8.0F); this.level().broadcastEntityEvent(this, (byte) 7); } return InteractionResult.sidedSuccess(this.level().isClientSide); } - if (stack.is(Items.ENCHANTED_GOLDEN_APPLE) && this.isTame() && this.isOwnedBy(player)) { + if (owned && (stack.is(Items.ENCHANTED_GOLDEN_APPLE) || stack.is(Items.GOLDEN_APPLE))) { + boolean enchanted = stack.is(Items.ENCHANTED_GOLDEN_APPLE); + boolean canGrow = this.getGrowthProgress() < 1.0F; + if (!canGrow && this.getHealth() >= this.getMaxHealth()) { + return InteractionResult.PASS; + } if (!this.level().isClientSide) { - if (!player.getAbilities().instabuild) { - stack.shrink(1); + this.consume(player, stack); + this.heal(enchanted ? 20.0F : 8.0F); + if (canGrow) { + this.grow(enchanted ? ENCHANTED_APPLE_GROWTH : GOLDEN_APPLE_GROWTH, player); } - this.heal(8.0F); - this.setGrowthProgress(this.getGrowthProgress() + (1.0F - this.getGrowthProgress()) * 0.1F); - this.level().broadcastEntityEvent(this, (byte) 6); + this.level().broadcastEntityEvent(this, (byte) 7); + this.triggerQuirk(); } return InteractionResult.sidedSuccess(this.level().isClientSide); } if (this.isFood(stack)) { - if (!this.level().isClientSide && this.isTame() && this.isOwnedBy(player) && this.getHealth() < this.getMaxHealth()) { - if (!player.getAbilities().instabuild) { - stack.shrink(1); + if (owned && this.getHealth() < this.getMaxHealth()) { + if (!this.level().isClientSide) { + this.consume(player, stack); + this.heal(8.0F); + this.level().broadcastEntityEvent(this, (byte) 7); } - this.heal(8.0F); - this.level().broadcastEntityEvent(this, (byte) 6); + return InteractionResult.sidedSuccess(this.level().isClientSide); } - return InteractionResult.sidedSuccess(this.level().isClientSide); + return InteractionResult.PASS; } - if (this.isTame() && this.isOwnedBy(player)) { + if (owned) { if (!this.isSaddled() && stack.is(Items.SADDLE)) { if (!this.level().isClientSide) { this.setSaddled(true); - if (!player.getAbilities().instabuild) { - stack.shrink(1); - } + this.consume(player, stack); this.playSound(SoundEvents.HORSE_SADDLE, 1.0F, 1.0F); } return InteractionResult.sidedSuccess(this.level().isClientSide); @@ -453,15 +595,14 @@ public abstract class RoyalMountEntity extends TamableAnimal implements GeoEntit this.setInSittingPose(this.isOrderedToSit()); this.navigation.stop(); this.setTarget(null); + this.clearAction(); } return InteractionResult.sidedSuccess(this.level().isClientSide); } if (stack.isEmpty() && !player.isPassenger()) { if (!this.level().isClientSide) { - this.setOrderedToSit(false); - this.setInSittingPose(false); - player.startRiding(this); + this.tryMount(player); } return InteractionResult.sidedSuccess(this.level().isClientSide); } @@ -470,6 +611,25 @@ public abstract class RoyalMountEntity extends TamableAnimal implements GeoEntit return super.mobInteract(player, hand); } + private void consume(Player player, ItemStack stack) { + if (!player.getAbilities().instabuild) { + stack.shrink(1); + } + } + + private boolean tryMount(Player player) { + if (!this.canBeRidden()) { + player.displayClientMessage(Component.translatable("message.antarchy.royal_mount.too_young_ride", + this.getDisplayName(), Stage.JUVENILE.displayName()), true); + return false; + } + this.setOrderedToSit(false); + this.setInSittingPose(false); + this.setTarget(null); + this.clearAction(); + return player.startRiding(this); + } + @Nullable @Override public AgeableMob getBreedOffspring(ServerLevel level, AgeableMob other) { @@ -490,35 +650,20 @@ public abstract class RoyalMountEntity extends TamableAnimal implements GeoEntit } LivingEntity target = this.getTarget(); - if (target != null && !this.canAttack(target)) { + if (target != null && (!target.isAlive() || !this.canAttack(target))) { this.setTarget(null); } - if (this.spitCooldown > 0) { - this.spitCooldown--; - } - this.tickRoyalAbilities(); + this.tickCooldowns(); + this.tickAction(); + this.tickFlightState(); + this.tickOwnerGreeting(); if ((this.isFlying() || !this.onGround()) && --this.flapSoundTimer <= 0) { this.flapSoundTimer = 24 + this.random.nextInt(12); this.playSound(this.flySound(), 0.6F, 1.0F + (this.random.nextFloat() - 0.5F) * 0.3F); } - if (this.actionTicks > 0) { - this.entityData.set(QUIRKING, false); - this.actionTicks--; - if (!this.actionHit && this.actionTicks <= ACTION_TICKS - BITE_HIT_TICK - && (this.getAnimState() == ANIM_BITE || this.getAnimState() == ANIM_FLY_BITE)) { - this.actionHit = true; - this.applyBiteDamage(); - } - if (this.actionTicks == 0) { - this.updateAmbientAnimState(); - } - return; - } - - this.tickFlightState(); this.updateAmbientAnimState(); if (this.quirkTimer > 0) { @@ -527,95 +672,475 @@ public abstract class RoyalMountEntity extends TamableAnimal implements GeoEntit if (this.getAnimState() == ANIM_IDLE && this.quirkTimer == 0 && this.random.nextInt(600) == 0) { this.quirkTimer = 80; } - this.entityData.set(QUIRKING, this.quirkTimer > 0 && this.getAnimState() == ANIM_IDLE); + this.entityData.set(QUIRKING, this.quirkTimer > 0 && this.getAnimState() == ANIM_IDLE && !this.isActing()); } - private void tickRoyalAbilities() { - if (this.beamCooldownTicks > 0) this.beamCooldownTicks--; - if (this.commandCooldownTicks > 0) this.commandCooldownTicks--; - if (this.timeFieldCooldownTicks > 0) this.timeFieldCooldownTicks--; - if (this.beamWindupTicks > 0) { - this.beamWindupTicks--; - if (this.beamWindupTicks == 0) { - this.beamController.start(this.beamShootFrom(), this.beamTargetPosition(), BEAM_DURATION_TICKS); - this.entityData.set(BEAM_ACTIVE, true); - } + public void triggerQuirk() { + if (!this.isActing()) { + this.quirkTimer = 80; } - if (this.beamController.isFiring()) { - this.beamController.tick(this.beamShootFrom(), this.getTarget(), this.beamSettings(), BeamTerrainMode.NONE, this::setBeamEndPosition); - if (!this.beamController.isFiring()) { - this.entityData.set(BEAM_ACTIVE, false); - this.entityData.set(BEAM_HEAD, -1); - this.beamCooldownTicks = BEAM_COOLDOWN_TICKS; + } + + private void tickCooldowns() { + if (this.biteCooldownTicks > 0) this.biteCooldownTicks--; + if (this.spitCooldownTicks > 0) this.spitCooldownTicks--; + if (this.beamCooldownTicks > 0) this.beamCooldownTicks--; + if (this.signatureCooldownTicks > 0) this.signatureCooldownTicks--; + if (this.actionRecoveryTicks > 0) this.actionRecoveryTicks--; + } + + private void tickFlightState() { + if (!this.isFlying()) { + return; + } + if (this.isVehicle()) { + if (!this.canFly()) { + this.setFlying(false); + } else if (this.onGround() && this.riderDescendPressed) { + this.setFlying(false); } return; } - if (this.isOrderedToSit()) return; - LivingEntity target = this.getTarget(); - if (target == null && this.getControllingPassenger() instanceof Player) { - target = this.frontTarget(); - if (target != null) this.setTarget(target); + if (this.canFly() && this.tickCount - this.flightClaimTick <= 10) { + return; } - if (target == null || !target.isAlive() || !this.hasLineOfSight(target)) return; - double targetDistance = this.distanceTo(target); - if (this.beamCooldownTicks <= 0 && this.beamWindupTicks <= 0 && targetDistance > 7.0D && targetDistance < 28.0D && this.tickCount % 20 == 0) { - this.beamHead = (this.beamHead + 1) % 3; - this.entityData.set(BEAM_HEAD, this.beamHead); - this.beamWindupTicks = BEAM_WINDUP_TICKS; - this.setAnimState(this.isFlying() ? ANIM_FLY_SHOOT : ANIM_SHOOT); - this.triggerAnim("main", this.isFlying() ? "fly_shoot" : "shoot"); - this.playSound(this.shootSound(), 0.9F, 0.75F); + if (this.onGround() || this.isInWater() || this.isInLava()) { + this.setFlying(false); + return; } - if (this instanceof PrinceEntity && this.commandCooldownTicks <= 0 && this.tickCount % 20 == 0) { - this.performPrinceCommand(); + this.navigation.stop(); + this.moveControl.setWantedPosition(this.getX(), this.getY() - 3.0D, this.getZ(), 0.8D); + } + + private void tickOwnerGreeting() { + if (!this.isTame() || this.isVehicle() || this.tickCount % 10 != 0) { + return; } - if (this instanceof PrincessEntity && this.timeFieldCooldownTicks <= 0 && this.tickCount % 20 == 0) { - this.performPrincessTimeField(target); + LivingEntity owner = this.getOwner(); + if (owner == null || owner.level() != this.level()) { + this.ownerAwayTicks = Math.min(this.ownerAwayTicks + 10, 2400); + return; + } + double distance = this.distanceTo(owner); + if (distance > 24.0D + this.getBbWidth()) { + this.ownerAwayTicks = Math.min(this.ownerAwayTicks + 10, 2400); + } else if (distance < 8.0D + this.getBbWidth()) { + if (this.ownerAwayTicks >= 400 && this.getTarget() == null) { + this.triggerQuirk(); + this.playSound(this.idleSound(), 1.4F, 1.15F); + this.lookControl.setLookAt(owner, 30.0F, 30.0F); + } + this.ownerAwayTicks = 0; } } - private void performPrinceCommand() { - if (!(this.level() instanceof ServerLevel level) || RoyalEffectHooks.commandedHolder() == null) return; - int applied = 0; - for (Mob mob : level.getEntitiesOfClass(Mob.class, this.getBoundingBox().inflate(16.0D), mob -> - mob != this && mob.isAlive() && mob instanceof Enemy && !mob.isAlliedTo(this) - && !(mob instanceof RoyalBossEntity) && RoyalEffectEligibility.canApplyCommanded(mob))) { - if (applied >= 5) break; - if (mob.addEffect(new MobEffectInstance(RoyalEffectHooks.commandedHolder(), 300), this)) { - applied++; - level.sendParticles(ParticleTypes.HAPPY_VILLAGER, mob.getX(), mob.getY() + mob.getBbHeight(), mob.getZ(), 4, 0.2D, 0.2D, 0.2D, 0.02D); + private void updateAmbientAnimState() { + if (this.isActing()) { + return; + } + if (this.isFlying() || (!this.onGround() && !this.isInWater())) { + this.setAnimState(ANIM_FLY); + return; + } + boolean moving; + if (this.getControllingPassenger() instanceof Player rider) { + moving = Math.abs(rider.zza) > 0.05F || Math.abs(rider.xxa) > 0.05F; + } else { + moving = this.getDeltaMovement().horizontalDistanceSqr() > 0.0025D; + } + this.setAnimState(moving ? ANIM_WALK : ANIM_IDLE); + } + + public boolean isActing() { + return this.action != ACTION_NONE; + } + + public int currentAction() { + return this.action; + } + + public boolean isActionReady() { + return this.action == ACTION_NONE && this.actionRecoveryTicks <= 0; + } + + @Nullable + public LivingEntity actionTarget() { + return this.actionTarget; + } + + public boolean isActionCommitted() { + return switch (this.action) { + case ACTION_BEAM -> this.actionTicks >= BEAM_WINDUP_TICKS - 6; + case ACTION_SPIT -> this.actionTicks >= SPIT_FIRE_TICK - 3; + case ACTION_SIGNATURE -> this.actionTicks >= SIGNATURE_WINDUP_TICKS - 6; + default -> this.action != ACTION_NONE; + }; + } + + public boolean isActionAvailable(int type) { + return switch (type) { + case ACTION_BITE -> this.biteCooldownTicks <= 0; + case ACTION_SPIT -> this.canSpit() && this.spitCooldownTicks <= 0; + case ACTION_BEAM -> this.canUseAdultAbilities() && this.beamCooldownTicks <= 0; + case ACTION_SIGNATURE -> this.canUseAdultAbilities() && this.signatureCooldownTicks <= 0; + default -> false; + }; + } + + public boolean startAction(int type, @Nullable LivingEntity target, @Nullable Vec3 aimPoint, boolean ridden) { + if (this.level().isClientSide || !this.isActionReady() || !this.isActionAvailable(type)) { + return false; + } + this.action = type; + this.actionTicks = 0; + this.actionFired = false; + this.actionRidden = ridden; + this.actionTarget = target; + this.actionAimPoint = aimPoint; + this.lastAction = type; + this.entityData.set(QUIRKING, false); + this.quirkTimer = 0; + boolean flying = this.isFlying(); + switch (type) { + case ACTION_BITE -> { + this.biteCooldownTicks = BITE_COOLDOWN_TICKS; + this.setAnimState(flying ? ANIM_FLY_BITE : ANIM_BITE); + this.triggerAnim("main", flying ? "fly_bite" : "bite"); + this.playSound(this.biteSound(), 1.2F, 0.95F - 0.04F * this.getGrowthScale() + this.random.nextFloat() * 0.2F); + } + case ACTION_SPIT -> { + this.spitCooldownTicks = SPIT_COOLDOWN_TICKS; + this.setAnimState(flying ? ANIM_FLY_SHOOT : ANIM_SHOOT); + this.triggerAnim("main", flying ? "fly_shoot" : "shoot"); + } + case ACTION_BEAM -> { + this.beamCooldownTicks = BEAM_COOLDOWN_TICKS; + this.beamHead = this.nextBeamHead(this.beamHead); + this.entityData.set(BEAM_HEAD, this.beamHead); + this.setAnimState(flying ? ANIM_FLY_SHOOT : ANIM_SHOOT); + this.triggerAnim("main", flying ? "fly_shoot" : "shoot"); + this.playSound(this.shootSound(), 1.0F, 0.7F); + } + case ACTION_SIGNATURE -> { + this.signatureCooldownTicks = this.signatureCooldownTicks(); + this.setAnimState(flying ? ANIM_FLY_SHOOT : ANIM_SHOOT); + this.triggerAnim("main", flying ? "fly_shoot" : "shoot"); + this.playSound(this.idleSound(), 2.0F, 0.6F); + } + default -> { + this.clearAction(); + return false; } } - if (applied > 0) { - this.commandCooldownTicks = COMMAND_COOLDOWN_TICKS; - this.playSound(this.shootSound(), 1.0F, 0.65F); + return true; + } + + private void tickAction() { + if (this.action == ACTION_NONE) { + if (this.beamController.isFiring()) { + this.stopBeam(); + } + return; + } + if (!(this.level() instanceof ServerLevel level)) { + return; + } + if (this.actionRidden && this.getControllingPassenger() == null) { + this.actionRidden = false; + } + this.actionTicks++; + if (!this.actionRidden && this.actionTarget != null && !this.isActionCommitted()) { + this.turnTowards(this.actionTarget.position(), 14.0F); + } + switch (this.action) { + case ACTION_BITE -> { + if (!this.actionFired && this.actionTicks >= BITE_HIT_TICK) { + this.actionFired = true; + this.applyBiteDamage(); + } + if (this.actionTicks >= BITE_TICKS) { + this.finishAction(); + } + } + case ACTION_SPIT -> { + if (!this.actionFired && this.actionTicks >= SPIT_FIRE_TICK) { + this.actionFired = true; + this.fireSpitVolley(); + } + if (this.actionTicks >= SPIT_TICKS) { + this.finishAction(); + } + } + case ACTION_BEAM -> this.tickBeamAction(level); + case ACTION_SIGNATURE -> { + if (!this.actionFired) { + this.signatureTelegraph(level, this.actionTicks, this.actionTarget, this.actionAimPoint); + if (this.actionTicks >= SIGNATURE_WINDUP_TICKS) { + this.actionFired = true; + if (this.performSignature(level, this.validActionTarget(), this.actionAimPoint, this.actionRidden)) { + this.playSound(this.shootSound(), 1.4F, 0.6F); + } else { + this.signatureCooldownTicks = Math.min(this.signatureCooldownTicks, 60); + } + } + } + if (this.actionTicks >= SIGNATURE_TICKS) { + this.finishAction(); + } + } + default -> this.finishAction(); } } - private void performPrincessTimeField(@Nullable LivingEntity target) { - if (!(this.level() instanceof ServerLevel level)) return; - Vec3 center = target != null && target.isAlive() ? target.position() : this.position(); - TimeDilationApi.createField(level, center, 8.0D, 0.45D, 100, this.getUUID()); - this.timeFieldCooldownTicks = TIME_FIELD_COOLDOWN_TICKS; - level.sendParticles(ParticleTypes.END_ROD, center.x, center.y + 0.5D, center.z, 28, 1.2D, 0.6D, 1.2D, 0.03D); + @Nullable + private LivingEntity validActionTarget() { + LivingEntity target = this.actionTarget; + return target != null && target.isAlive() && target.level() == this.level() ? target : null; + } + + private void tickBeamAction(ServerLevel level) { + if (!this.actionFired) { + if (this.actionTicks % 2 == 0) { + Vec3 origin = this.beamShootFrom(); + level.sendParticles(this.beamWindupParticle(), origin.x, origin.y, origin.z, 3, + 0.15D * this.getGrowthScale(), 0.15D * this.getGrowthScale(), 0.15D * this.getGrowthScale(), 0.02D); + } + if (this.actionTicks >= BEAM_WINDUP_TICKS) { + this.actionFired = true; + Vec3 aim = this.currentBeamAim(); + this.beamController.start(this.beamShootFrom(), aim, BEAM_DURATION_TICKS); + this.entityData.set(BEAM_ACTIVE, true); + this.playSound(this.shootSound(), 1.4F, 0.6F); + } + return; + } + if (this.beamController.isFiring()) { + Vec3 from = this.beamShootFrom(); + LivingEntity target = this.actionRidden ? null : this.validActionTarget(); + Vec3 direction; + if (this.actionRidden && this.getControllingPassenger() instanceof Player rider) { + direction = this.riderAimPoint(rider, this.beamRange()).subtract(from); + } else { + direction = this.getLookAngle(); + } + this.beamController.tick(from, direction, target, this.beamSettings(), BeamTerrainMode.NONE, + this::setBeamEndPosition, !this.actionRidden); + if (!this.beamController.isFiring()) { + this.entityData.set(BEAM_ACTIVE, false); + this.entityData.set(HAS_BEAM_END, false); + } + return; + } + if (this.actionTicks >= BEAM_WINDUP_TICKS + BEAM_DURATION_TICKS + BEAM_RECOVERY_TICKS) { + this.finishAction(); + } + } + + private Vec3 currentBeamAim() { + if (this.actionRidden && this.getControllingPassenger() instanceof Player rider) { + return this.riderAimPoint(rider, this.beamRange()); + } + LivingEntity target = this.validActionTarget(); + if (target != null) { + return target.getEyePosition(); + } + return this.actionAimPoint != null ? this.actionAimPoint : this.getEyePosition().add(this.getLookAngle().scale(20.0D)); + } + + private void finishAction() { + this.clearAction(); + this.actionRecoveryTicks = ACTION_RECOVERY_TICKS; + } + + public void clearAction() { + this.action = ACTION_NONE; + this.actionTicks = 0; + this.actionFired = false; + this.actionRidden = false; + this.actionTarget = null; + this.actionAimPoint = null; + if (this.beamController.isFiring() || this.entityData.get(BEAM_ACTIVE)) { + this.stopBeam(); + } + } + + private void stopBeam() { + this.beamController.stop(); + this.entityData.set(BEAM_ACTIVE, false); + this.entityData.set(HAS_BEAM_END, false); + this.entityData.set(BEAM_HEAD, -1); + } + + public int chooseCombatAction(LivingEntity target, double gap, boolean canSee) { + if (!this.isActionReady()) { + return ACTION_NONE; + } + float bite = this.isActionAvailable(ACTION_BITE) && this.canBiteReach(target) ? this.biteWeight() : 0.0F; + float spit = this.isActionAvailable(ACTION_SPIT) && canSee && gap > 2.0D && gap < 22.0D + this.getGrowthScale() ? this.spitWeight() : 0.0F; + float beam = this.isActionAvailable(ACTION_BEAM) && canSee && gap > 5.0D && gap < this.beamRange() - 2.0D ? this.beamWeight() : 0.0F; + float signature = this.isActionAvailable(ACTION_SIGNATURE) && this.isSignatureUseful(target) ? this.signatureWeight() : 0.0F; + if (this.lastAction == ACTION_SPIT) spit *= 0.5F; + if (this.lastAction == ACTION_BITE && spit > 0.0F) bite *= 0.7F; + float total = bite + spit + beam + signature; + if (total <= 0.0F) { + return ACTION_NONE; + } + float roll = this.random.nextFloat() * total; + if ((roll -= signature) < 0.0F) return ACTION_SIGNATURE; + if ((roll -= beam) < 0.0F) return ACTION_BEAM; + if ((roll -= bite) < 0.0F) return ACTION_BITE; + return ACTION_SPIT; + } + + public void turnTowards(Vec3 point, float maxTurn) { + double dx = point.x - this.getX(); + double dz = point.z - this.getZ(); + if (dx * dx + dz * dz < 1.0E-4D) { + return; + } + float desired = (float) (Mth.atan2(dz, dx) * Mth.RAD_TO_DEG) - 90.0F; + float yaw = Mth.approachDegrees(this.getYRot(), desired, maxTurn); + this.setYRot(yaw); + this.yBodyRot = yaw; + this.yHeadRot = yaw; + } + + public double combatGap(LivingEntity target) { + double horizontal = Math.sqrt(Mth.square(target.getX() - this.getX()) + Mth.square(target.getZ() - this.getZ())); + double vertical = Math.max(0.0D, Math.max(target.getY() - (this.getY() + this.getBbHeight()), this.getY() - (target.getY() + target.getBbHeight()))); + double flat = Math.max(0.0D, horizontal - this.getBbWidth() * 0.5D - target.getBbWidth() * 0.5D); + return Math.sqrt(flat * flat + vertical * vertical); + } + + public AABB biteBox() { + float scale = this.getGrowthScale(); + Vec3 forward = Vec3.directionFromRotation(0.0F, this.getYRot()); + double reach = this.getBbWidth() * 0.5D + 1.0D * scale; + double radius = 1.1D * scale + 0.6D; + Vec3 center = this.position().add(forward.scale(reach)); + return new AABB(center.x - radius, this.getY() - 0.75D, center.z - radius, + center.x + radius, this.getY() + this.getBbHeight() + 0.75D * scale, center.z + radius); + } + + public boolean canBiteReach(LivingEntity target) { + return this.biteBox().intersects(target.getBoundingBox()); + } + + private void applyBiteDamage() { + AABB box = this.biteBox(); + float damage = (float) this.getAttributeValue(Attributes.ATTACK_DAMAGE); + DamageSource source = this.damageSources().mobAttack(this); + int hits = 0; + LivingEntity primary = this.validActionTarget(); + if (primary != null && box.intersects(primary.getBoundingBox()) && this.canAttack(primary)) { + this.biteHit(primary, damage, source); + hits++; + } + for (LivingEntity candidate : this.level().getEntitiesOfClass(LivingEntity.class, box, + living -> living != primary && living.isAlive() && this.canAttack(living))) { + if (hits >= 3) { + break; + } + if (!this.actionRidden && !(candidate instanceof Enemy) && candidate != this.getTarget()) { + continue; + } + this.biteHit(candidate, damage, source); + hits++; + } + } + + private void biteHit(LivingEntity victim, float damage, DamageSource source) { + if (victim.hurt(source, damage)) { + Vec3 push = victim.position().subtract(this.position()).multiply(1.0D, 0.0D, 1.0D); + if (push.lengthSqr() > 1.0E-4D) { + push = push.normalize(); + victim.knockback(0.3D + 0.1D * this.getGrowthScale(), -push.x, -push.z); + } + } + } + + private void fireSpitVolley() { + Vec3 aim; + LivingEntity target = this.validActionTarget(); + if (this.actionRidden && this.getControllingPassenger() instanceof Player rider) { + aim = this.riderAimPoint(rider, 48.0D).subtract(this.getEyePosition()).normalize(); + } else if (target != null) { + aim = target.getEyePosition().subtract(this.getEyePosition()).normalize(); + } else { + aim = this.getLookAngle(); + } + Vec3 flat = new Vec3(-aim.z, 0.0D, aim.x); + Vec3 right = flat.lengthSqr() < 1.0E-6D ? new Vec3(1.0D, 0.0D, 0.0D) : flat.normalize(); + float speed = 1.3F + 0.06F * this.getGrowthScale(); + for (int i = -1; i <= 1; i++) { + Vec3 origin = this.boltOrigin(i); + Vec3 dir; + if (target != null && !this.actionRidden) { + dir = target.getEyePosition().subtract(origin).normalize().add(right.scale(i * 0.05D)).normalize(); + } else { + dir = aim.add(right.scale(i * 0.06D)).normalize(); + } + RoyalBoltEntity bolt = new RoyalBoltEntity(this.boltType(), this.level()); + bolt.setOwner(this); + bolt.setElement(this.boltElement(i)); + bolt.setPos(origin.x, origin.y, origin.z); + bolt.shoot(dir.x, dir.y, dir.z, speed, 1.0F); + this.level().addFreshEntity(bolt); + } + this.playSound(this.shootSound(), 1.2F, 0.95F - 0.03F * this.getGrowthScale() + this.random.nextFloat() * 0.2F); + } + + protected Vec3 boltOrigin(int lateralIndex) { + float scale = this.getGrowthScale(); + Vec3 forward = Vec3.directionFromRotation(0.0F, this.getYRot()); + Vec3 side = new Vec3(-forward.z, 0.0D, forward.x); + return this.position() + .add(0.0D, 1.05D * scale, 0.0D) + .add(forward.scale(1.75D * scale)) + .add(side.scale(lateralIndex * 0.85D * scale)); + } + + public float projectileDamageMultiplier() { + return 1.0F + 0.35F * (this.getGrowthScale() - 1.0F); + } + + protected RoyalBeamElement boltElement(int lateralIndex) { + return RoyalBeamElement.GENERIC; + } + + public double beamRange() { + return 26.0D + 2.0D * this.getGrowthScale(); } private BeamSettings beamSettings() { - float damage = this instanceof PrincessEntity ? 10.0F : 12.0F; - return new BeamSettings(28.0D, 0.08D, 4.0D, BEAM_DURATION_TICKS, 6, BEAM_COOLDOWN_TICKS, - 0.35F, 0.45F, damage, 0.45F, 3, 0.0D, 0.0F, 0.0F, 0, 0.0F, 0.0F, 0.0F, false, true); + return new BeamSettings(this.beamRange(), 0.08D, 4.0D, BEAM_DURATION_TICKS, 6, BEAM_COOLDOWN_TICKS, + 0.35F + 0.08F * this.getGrowthScale(), 0.45F + 0.15F * this.getGrowthScale(), this.beamBaseDamage(), + 0.45F, 3, 0.0D, 0.0F, 0.0F, 0, 0.0F, 0.0F, 0.0F, false, true); } - private Vec3 beamTargetPosition() { - LivingEntity target = this.getTarget(); - return target != null ? target.getEyePosition() : this.getEyePosition().add(this.getLookAngle().scale(20.0D)); + @Override + public float scaleBeamDamage(float damage) { + return damage * (1.0F + 0.25F * (this.getGrowthScale() - 1.0F)); + } + + @Override + public boolean canBeamDamage(LivingEntity target) { + if (this.isFriendlyTo(target)) { + return false; + } + if (this.actionRidden) { + return true; + } + return target instanceof Enemy || target == this.getTarget() || target == this.actionTarget + || target instanceof Mob mob && mob.getTarget() != null && this.isFriendlyTo(mob.getTarget()); } public Vec3 beamShootFrom() { float scale = this.getGrowthScale(); Vec3 forward = Vec3.directionFromRotation(0.0F, this.getYRot()); Vec3 side = new Vec3(-forward.z, 0.0D, forward.x); - return this.position().add(0.0D, 1.0D * scale, 0.0D).add(forward.scale(1.05D * scale)).add(side.scale((this.getBeamHead() - 1) * 0.72D * scale)); + int head = Math.max(0, this.getBeamHead()); + return this.position().add(0.0D, 1.05D * scale, 0.0D).add(forward.scale(1.75D * scale)).add(side.scale((head - 1) * 0.85D * scale)); } public int getBeamHead() { return this.entityData.get(BEAM_HEAD); } @@ -634,127 +1159,77 @@ public abstract class RoyalMountEntity extends TamableAnimal implements GeoEntit } } - private void tickFlightState() { - if (this.isVehicle()) { - return; - } - if (this.isFlying() && this.onGround() && this.getTarget() == null) { - this.setFlying(false); - } + public Vec3 riderAimPoint(Player rider, double range) { + Vec3 eye = rider.getEyePosition(); + Vec3 end = eye.add(rider.getViewVector(1.0F).scale(range)); + var blockHit = this.level().clip(new ClipContext(eye, end, ClipContext.Block.COLLIDER, ClipContext.Fluid.NONE, rider)); + Vec3 limit = blockHit.getLocation(); + EntityHitResult entityHit = ProjectileUtil.getEntityHitResult(this.level(), rider, eye, limit, + new AABB(eye, limit).inflate(1.0D), + entity -> entity instanceof LivingEntity living && living.isAlive() && !entity.isSpectator() && !this.isFriendlyTo(entity)); + return entityHit != null ? entityHit.getLocation() : limit; } - private void updateAmbientAnimState() { - if (this.actionTicks > 0) { - return; - } - if (this.isFlying() || !this.onGround()) { - this.setAnimState(ANIM_FLY); - return; - } - boolean moving; - if (this.getControllingPassenger() instanceof Player rider) { - moving = Math.abs(rider.zza) > 0.05F || Math.abs(rider.xxa) > 0.05F; - } else { - moving = this.getDeltaMovement().horizontalDistanceSqr() > 0.0025D; - } - this.setAnimState(moving ? ANIM_WALK : ANIM_IDLE); + @Nullable + public LivingEntity riderAimEntity(Player rider, double range) { + Vec3 eye = rider.getEyePosition(); + Vec3 end = eye.add(rider.getViewVector(1.0F).scale(range)); + var blockHit = this.level().clip(new ClipContext(eye, end, ClipContext.Block.COLLIDER, ClipContext.Fluid.NONE, rider)); + Vec3 limit = blockHit.getLocation(); + EntityHitResult entityHit = ProjectileUtil.getEntityHitResult(this.level(), rider, eye, limit, + new AABB(eye, limit).inflate(1.0D), + entity -> entity instanceof LivingEntity living && living.isAlive() && !entity.isSpectator() && !this.isFriendlyTo(entity)); + return entityHit != null && entityHit.getEntity() instanceof LivingEntity living ? living : null; } - public void startBite() { - if (this.actionTicks > 0) { - return; + public boolean isFriendlyTo(@Nullable Entity entity) { + if (entity == null) { + return false; } - this.actionTicks = ACTION_TICKS; - this.actionHit = false; - this.setAnimState(this.isFlying() ? ANIM_FLY_BITE : ANIM_BITE); - this.triggerAnim("main", this.isFlying() ? "fly_bite" : "bite"); - this.playSound(this.biteSound(), 1.2F, 0.9F + this.random.nextFloat() * 0.2F); - } - - private void applyBiteDamage() { - LivingEntity target = this.getTarget(); - if (target == null && this.getControllingPassenger() instanceof Player) { - target = this.frontTarget(); + if (entity == this || this.hasPassenger(entity)) { + return true; } - if (target != null && this.canAttack(target) && target.distanceTo(this) < 5.0D) { - target.hurt(this.damageSources().mobAttack(this), (float) this.getAttributeValue(Attributes.ATTACK_DAMAGE)); + if (entity instanceof RoyalMountEntity other && this.isTame() && other.isTame()) { + return true; } + if (this.isAlliedTo(entity) || entity.isAlliedTo(this)) { + return true; + } + if (!this.isTame()) { + return false; + } + LivingEntity owner = this.getOwner(); + if (owner != null && (entity == owner || entity.isAlliedTo(owner) || owner.isAlliedTo(entity))) { + return true; + } + return entity instanceof OwnableEntity ownable && this.getOwnerUUID() != null + && this.getOwnerUUID().equals(ownable.getOwnerUUID()); } @Override public boolean canAttack(LivingEntity target) { - return !(target instanceof RoyalMountEntity other && this.isTame() && other.isTame()) - && super.canAttack(target); + return !this.isFriendlyTo(target) && super.canAttack(target); } - @Nullable - private LivingEntity frontTarget() { - Vec3 look = this.getLookAngle(); - Vec3 eye = this.getEyePosition(); - LivingEntity best = null; - double bestDot = 0.3D; - for (LivingEntity candidate : this.level().getEntitiesOfClass(LivingEntity.class, this.getBoundingBox().inflate(6.0D))) { - if (candidate == this || candidate == this.getControllingPassenger() || candidate == this.getOwner()) { - continue; - } - if (!this.canAttack(candidate)) { - continue; - } - Vec3 to = candidate.getEyePosition().subtract(eye); - double dist = to.length(); - if (dist > 6.0D || dist < 1.0E-3D) { - continue; - } - double dot = to.scale(1.0D / dist).dot(look); - if (dot > bestDot) { - bestDot = dot; - best = candidate; - } + @Override + public boolean wantsToAttack(LivingEntity target, LivingEntity owner) { + if (target instanceof Creeper || this.isFriendlyTo(target)) { + return false; } - return best; - } - - public void startSpitVolley(@Nullable LivingEntity target) { - if (this.spitCooldown > 0 || this.level().isClientSide) { - return; + if (target instanceof Player victim && owner instanceof Player ownerPlayer && !ownerPlayer.canHarmPlayer(victim)) { + return false; } - this.spitCooldown = 40; - this.actionTicks = ACTION_TICKS; - this.actionHit = true; - this.setAnimState(this.isFlying() ? ANIM_FLY_SHOOT : ANIM_SHOOT); - this.triggerAnim("main", this.isFlying() ? "fly_shoot" : "shoot"); - - Vec3 aim; - if (target != null) { - aim = target.getEyePosition().subtract(this.getEyePosition()).normalize(); - } else if (this.getControllingPassenger() instanceof Player rider) { - aim = rider.getLookAngle(); - } else { - aim = this.getLookAngle(); - } - - Vec3 right = new Vec3(-aim.z, 0.0D, aim.x).normalize(); - for (int i = -1; i <= 1; i++) { - Vec3 dir = aim.add(right.scale(i * 0.14D)).normalize(); - RoyalBoltEntity bolt = new RoyalBoltEntity(this.boltType(), this.level()); - bolt.setOwner(this); - bolt.setElement(this.boltElement(i)); - Vec3 spawn = this.getEyePosition().add(aim.scale(1.6D)).add(right.scale(i * 0.9D)); - bolt.setPos(spawn.x, spawn.y, spawn.z); - bolt.shoot(dir.x, dir.y, dir.z, 1.4F, 1.0F); - this.level().addFreshEntity(bolt); - } - this.playSound(this.shootSound(), 1.2F, 0.9F + this.random.nextFloat() * 0.2F); - } - - protected RoyalBeamElement boltElement(int lateralIndex) { - return RoyalBeamElement.GENERIC; + return super.wantsToAttack(target, owner); } public boolean toggleMountedFlight(ServerPlayer player) { if (!this.canControl(player)) { return false; } + if (!this.isFlying() && !this.canFly()) { + this.notifyLocked(player, "message.antarchy.royal_mount.too_young_fly", Stage.ADULT); + return false; + } this.setFlying(!this.isFlying()); if (this.isFlying()) { this.setDeltaMovement(this.getDeltaMovement().add(0.0D, 0.4D, 0.0D)); @@ -765,19 +1240,51 @@ public abstract class RoyalMountEntity extends TamableAnimal implements GeoEntit public void handleMountedBite(ServerPlayer player) { if (this.canControl(player)) { - this.startBite(); + this.startAction(ACTION_BITE, null, null, true); } } public void handleMountedSpit(ServerPlayer player) { - if (this.canControl(player)) { - this.startSpitVolley(null); + if (!this.canControl(player)) { + return; } + if (!this.canSpit()) { + this.notifyLocked(player, "message.antarchy.royal_mount.too_young_ability", Stage.JUVENILE); + return; + } + this.startAction(ACTION_SPIT, null, null, true); + } + + public void handleMountedBeam(ServerPlayer player) { + if (!this.canControl(player)) { + return; + } + if (!this.canUseAdultAbilities()) { + this.notifyLocked(player, "message.antarchy.royal_mount.too_young_ability", Stage.ADULT); + return; + } + this.startAction(ACTION_BEAM, null, this.riderAimPoint(player, this.beamRange()), true); + } + + public void handleMountedSignature(ServerPlayer player) { + if (!this.canControl(player)) { + return; + } + if (!this.canUseAdultAbilities()) { + this.notifyLocked(player, "message.antarchy.royal_mount.too_young_ability", Stage.ADULT); + return; + } + LivingEntity aimed = this.riderAimEntity(player, 32.0D); + this.startAction(ACTION_SIGNATURE, aimed, this.riderAimPoint(player, 32.0D), true); + } + + private void notifyLocked(Player player, String key, Stage required) { + player.displayClientMessage(Component.translatable(key, this.getDisplayName(), required.displayName()), true); } public void setRiderAscend(boolean pressed) { this.riderAscendPressed = pressed; - if (pressed && !this.isFlying() && this.getControllingPassenger() != null) { + if (pressed && !this.isFlying() && this.canFly() && this.getControllingPassenger() != null) { this.setFlying(true); this.setDeltaMovement(this.getDeltaMovement().add(0.0D, 0.35D, 0.0D)); this.hasImpulse = true; @@ -809,7 +1316,7 @@ public abstract class RoyalMountEntity extends TamableAnimal implements GeoEntit Vec3 end = eye.add(player.getViewVector(1.0F).scale(com.craisinlord.antarchy.compat.entity.EntityReach.entityInteractionRange(player) + extraReach)); RoyalMountEntity best = null; double bestDistance = Double.MAX_VALUE; - for (RoyalMountEntity mount : player.level().getEntitiesOfClass(RoyalMountEntity.class, player.getBoundingBox().inflate(80.0D), + for (RoyalMountEntity mount : player.level().getEntitiesOfClass(RoyalMountEntity.class, player.getBoundingBox().inflate(40.0D), mount -> mount.isAlive() && mount.isTame() && mount.isOwnedBy(player) && !mount.isVehicle())) { AABB box = mount.assistedMountBox(); double distance; @@ -832,12 +1339,7 @@ public abstract class RoyalMountEntity extends TamableAnimal implements GeoEntit public static boolean tryAssistedMount(ServerPlayer player) { RoyalMountEntity mount = findAssistedMountTarget(player, 2.0D); - if (mount == null) { - return false; - } - mount.setOrderedToSit(false); - mount.setInSittingPose(false); - return player.startRiding(mount); + return mount != null && mount.tryMount(player); } @Nullable @@ -848,7 +1350,23 @@ public abstract class RoyalMountEntity extends TamableAnimal implements GeoEntit @Override public boolean canAddPassenger(Entity passenger) { - return this.getPassengers().isEmpty(); + return this.getPassengers().isEmpty() && this.canBeRidden(); + } + + @Override + protected void removePassenger(Entity passenger) { + super.removePassenger(passenger); + this.riderAscendPressed = false; + this.riderDescendPressed = false; + if (this.actionRidden) { + this.clearAction(); + } + } + + public double seatHeight() { + double bob = this.getAnimState() == ANIM_FLY || this.getAnimState() == ANIM_FLY_BITE || this.getAnimState() == ANIM_FLY_SHOOT + ? FLY_BOB_HEIGHT : 0.0D; + return (SEAT_HEIGHT + bob) * this.getGrowthScale(); } @Override @@ -857,10 +1375,17 @@ public abstract class RoyalMountEntity extends TamableAnimal implements GeoEntit return; } float scale = this.getGrowthScale(); - double yOffset = 1.3D * scale; - double backDistance = 0.35D * scale; - Vec3 back = Vec3.directionFromRotation(0.0F, this.getYRot()).scale(-backDistance); - moveFunction.accept(passenger, this.getX() + back.x, this.getY() + yOffset, this.getZ() + back.z); + Vec3 back = Vec3.directionFromRotation(0.0F, this.yBodyRot).scale(-SEAT_BACK * scale); + double attach = -passenger.getMyRidingOffset(); + moveFunction.accept(passenger, this.getX() + back.x, this.getY() + this.seatHeight() - attach, this.getZ() + back.z); + } + + public float thirdPersonCameraBonus() { + return 1.5F + 2.6F * (this.getGrowthScale() - 1.0F); + } + + public float thirdPersonCameraLift() { + return 0.3F * this.getGrowthScale(); } @Override @@ -893,13 +1418,18 @@ public abstract class RoyalMountEntity extends TamableAnimal implements GeoEntit return; } - this.setYRot(rider.getYRot()); - this.yBodyRot = this.getYRot(); - this.yHeadRot = this.getYRot(); + float yaw = this.isActionCommittedForRider() ? Mth.approachDegrees(this.getYRot(), rider.getYRot(), 6.0F) : rider.getYRot(); + this.setYRot(yaw); + this.yBodyRot = yaw; + this.yHeadRot = yaw; this.setXRot(rider.getXRot() * 0.5F); Vec3 input = this.getRiddenInput(rider, travelVector); - this.setSpeed(this.getRiddenSpeed(rider)); + float speed = this.getRiddenSpeed(rider); + if (this.action == ACTION_BEAM || this.action == ACTION_SIGNATURE) { + speed *= 0.45F; + } + this.setSpeed(speed); if (this.isFlying()) { this.moveRelative(this.getSpeed(), new Vec3(input.x, 0.0D, input.z)); @@ -913,21 +1443,91 @@ public abstract class RoyalMountEntity extends TamableAnimal implements GeoEntit } } + private boolean isActionCommittedForRider() { + return this.action == ACTION_BEAM && this.actionFired; + } + @Override public boolean causeFallDamage(float fallDistance, float multiplier, DamageSource source) { return false; } - @Override - public float maxUpStep() { - return 1.5F; - } - @Override public boolean removeWhenFarAway(double distanceToClosestPlayer) { return false; } + public static boolean isAirborne(LivingEntity entity) { + if (entity.isFallFlying()) { + return true; + } + if (entity instanceof Player player && player.getAbilities().flying) { + return true; + } + Entity vehicle = entity.getVehicle(); + return vehicle != null && !vehicle.onGround() && !vehicle.isInWater() && vehicle.isNoGravity(); + } + + public double findGroundY(double x, double startY, double z, int maxDepth) { + BlockPos.MutableBlockPos cursor = BlockPos.containing(x, startY, z).mutable(); + for (int i = 0; i < maxDepth; i++) { + BlockState state = this.level().getBlockState(cursor); + if (!state.getCollisionShape(this.level(), cursor).isEmpty() || !state.getFluidState().isEmpty()) { + return cursor.getY() + 1.0D; + } + cursor.move(0, -1, 0); + if (cursor.getY() < this.level().getMinBuildHeight()) { + break; + } + } + return Double.NaN; + } + + public boolean canOccupy(double x, double y, double z) { + AABB box = this.getBoundingBox().move(x - this.getX(), y - this.getY(), z - this.getZ()); + return this.level().noCollision(this, box) && !this.level().containsAnyLiquid(box); + } + + public boolean tryTeleportNear(LivingEntity anchor) { + boolean allowAir = this.canFly() && isAirborne(anchor); + double base = this.getBbWidth() * 0.5D + 1.5D; + float startAngle = this.random.nextFloat() * Mth.TWO_PI; + for (int ring = 0; ring < 3; ring++) { + double radius = base + ring * (1.5D + this.getBbWidth() * 0.25D); + for (int step = 0; step < 12; step++) { + double angle = startAngle + step * (Mth.TWO_PI / 12.0D); + double x = anchor.getX() + Math.cos(angle) * radius; + double z = anchor.getZ() + Math.sin(angle) * radius; + if (allowAir) { + double y = anchor.getY() + 1.0D; + if (this.canOccupy(x, y, z)) { + this.finishTeleport(x, y, z, true); + return true; + } + continue; + } + double ground = this.findGroundY(x, anchor.getY() + 3.0D, z, 8); + if (Double.isNaN(ground) || !this.level().getBlockState(BlockPos.containing(x, ground - 1.0D, z)).getFluidState().isEmpty()) { + continue; + } + if (this.canOccupy(x, ground, z)) { + this.finishTeleport(x, ground, z, false); + return true; + } + } + } + return false; + } + + private void finishTeleport(double x, double y, double z, boolean flying) { + this.navigation.stop(); + this.setFlying(flying && this.canFly()); + if (flying) { + this.flightClaimTick = this.tickCount; + } + this.teleportTo(x, y, z); + } + @Override public void registerControllers(AnimatableManager.ControllerRegistrar controllers) { controllers.add(new AnimationController<>(this, "main", 2, this::animController) diff --git a/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/RoyalMountFollowOwnerGoal.java b/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/RoyalMountFollowOwnerGoal.java index 0235984e..57095136 100644 --- a/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/RoyalMountFollowOwnerGoal.java +++ b/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/RoyalMountFollowOwnerGoal.java @@ -1,81 +1,76 @@ package com.craisinlord.antarchy.content.entity.royal; -import net.minecraft.core.BlockPos; +import java.util.EnumSet; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.ai.goal.Goal; import net.minecraft.world.phys.Vec3; public class RoyalMountFollowOwnerGoal extends Goal { - private static final double START_DISTANCE = 12.0D; - private static final double STOP_DISTANCE = 6.0D; - private static final double TELEPORT_DISTANCE = 48.0D; - private final RoyalMountEntity mount; private LivingEntity owner; private int recalcTimer; + private int stuckTicks; + private int teleportCooldown; + private Vec3 lastCheckPosition = Vec3.ZERO; public RoyalMountFollowOwnerGoal(RoyalMountEntity mount) { this.mount = mount; + this.setFlags(EnumSet.of(Goal.Flag.MOVE, Goal.Flag.LOOK)); + } + + private double startDistance() { + return 14.0D + 2.0D * this.mount.getGrowthScale(); + } + + private double stopDistance() { + return 6.0D + 1.5D * this.mount.getGrowthScale(); + } + + private double teleportDistance() { + return 48.0D + 4.0D * this.mount.getGrowthScale(); + } + + private boolean canFollow(LivingEntity candidate) { + return candidate != null && candidate.isAlive() && !candidate.isSpectator() + && candidate.level() == this.mount.level() + && this.mount.isTame() && !this.mount.isVehicle() && !this.mount.isOrderedToSit() + && !this.mount.isLeashed() && this.mount.getTarget() == null; } @Override public boolean canUse() { - if (!this.mount.isTame() || this.mount.isVehicle() || this.mount.isOrderedToSit()) { + LivingEntity candidate = this.mount.getOwner(); + if (!this.canFollow(candidate)) { return false; } - LivingEntity potentialOwner = this.mount.getOwner(); - if (potentialOwner == null || potentialOwner.isSpectator()) { + double start = this.startDistance(); + if (this.mount.distanceToSqr(candidate) < start * start) { return false; } - if (this.mount.distanceToSqr(potentialOwner) < START_DISTANCE * START_DISTANCE) { - return false; - } - this.owner = potentialOwner; + this.owner = candidate; return true; } @Override public boolean canContinueToUse() { - return !this.mount.getNavigation().isDone() - && this.mount.distanceToSqr(this.owner) > STOP_DISTANCE * STOP_DISTANCE - && !this.mount.isOrderedToSit(); + if (!this.canFollow(this.owner) || this.owner != this.mount.getOwner()) { + return false; + } + double stop = this.stopDistance(); + return this.mount.distanceToSqr(this.owner) > stop * stop; } @Override public void start() { this.recalcTimer = 0; + this.stuckTicks = 0; + this.lastCheckPosition = this.mount.position(); } @Override public void stop() { this.owner = null; this.mount.getNavigation().stop(); - this.mount.setFlying(false); - } - - @Override - public void tick() { - this.mount.getLookControl().setLookAt(this.owner, 10.0F, this.mount.getMaxHeadXRot()); - - double distanceSqr = this.mount.distanceToSqr(this.owner); - if (distanceSqr > TELEPORT_DISTANCE * TELEPORT_DISTANCE) { - this.trySafeTeleport(); - return; - } - - boolean ownerAirborne = !this.owner.onGround() || (this.owner.getY() - this.mount.getY()) > 3.0D; - this.mount.setFlying(ownerAirborne || distanceSqr > 24.0D * 24.0D); - - if (--this.recalcTimer <= 0) { - this.recalcTimer = 10; - if (this.mount.isFlying()) { - Vec3 to = this.owner.position().subtract(this.mount.position()); - Vec3 step = this.mount.position().add(to.normalize().scale(Math.min(to.length(), 8.0D))); - this.mount.getMoveControl().setWantedPosition(step.x, step.y + 1.0D, step.z, 1.2D); - } else { - this.mount.getNavigation().moveTo(this.owner, 1.1D); - } - } } @Override @@ -83,28 +78,57 @@ public class RoyalMountFollowOwnerGoal extends Goal { return true; } - private void trySafeTeleport() { - Vec3 ownerPosition = this.owner.position(); - BlockPos ownerBlock = this.owner.blockPosition(); - for (int radius = 0; radius <= 2; radius++) { - for (int dx = -radius; dx <= radius; dx++) { - for (int dz = -radius; dz <= radius; dz++) { - for (int dy = -1; dy <= 2; dy++) { - double x = ownerPosition.x + dx + 0.5D; - double y = ownerBlock.getY() + dy; - double z = ownerPosition.z + dz + 0.5D; - BlockPos floor = BlockPos.containing(x, y - 0.1D, z); - var destination = this.mount.getBoundingBox().move(x - this.mount.getX(), y - this.mount.getY(), z - this.mount.getZ()); - if (!this.mount.level().noCollision(this.mount, destination)) { - continue; - } - if (this.mount.isFlying() || !this.mount.level().getBlockState(floor).getCollisionShape(this.mount.level(), floor).isEmpty()) { - this.mount.teleportTo(x, y, z); - return; - } - } - } + @Override + public void tick() { + this.mount.getLookControl().setLookAt(this.owner, 10.0F, this.mount.getMaxHeadXRot()); + if (this.teleportCooldown > 0) { + this.teleportCooldown--; + } + this.trackStuck(); + + double distance = this.mount.distanceTo(this.owner); + if ((distance > this.teleportDistance() || this.stuckTicks >= 100 && distance > 24.0D) && this.teleportCooldown <= 0) { + this.teleportCooldown = 20; + if (this.mount.tryTeleportNear(this.owner)) { + this.stuckTicks = 0; + return; } } + + boolean ownerAirborne = RoyalMountEntity.isAirborne(this.owner); + boolean wantsFlight = this.mount.canFly() && (ownerAirborne || distance > 24.0D + this.mount.getBbWidth() + || this.stuckTicks >= 40 || this.owner.getY() - this.mount.getY() > 4.0D); + if (wantsFlight && this.mount.claimFlight()) { + if (--this.recalcTimer <= 0) { + this.recalcTimer = 5; + float scale = this.mount.getGrowthScale(); + Vec3 destination = this.owner.position().add(0.0D, 2.0D + 0.6D * scale, 0.0D); + Vec3 to = destination.subtract(this.mount.position()); + double stepLength = Math.min(to.length(), 12.0D + 2.0D * scale); + Vec3 step = to.lengthSqr() > 1.0E-4D ? this.mount.position().add(to.normalize().scale(stepLength)) : destination; + this.mount.getMoveControl().setWantedPosition(step.x, step.y, step.z, 1.3D); + } + return; + } + if (this.mount.isFlying()) { + return; + } + if (--this.recalcTimer <= 0) { + this.recalcTimer = 10; + this.mount.getNavigation().moveTo(this.owner, 1.2D); + } + } + + private void trackStuck() { + if (this.mount.tickCount % 20 != 0) { + return; + } + Vec3 position = this.mount.position(); + if (position.distanceToSqr(this.lastCheckPosition) < 1.0D) { + this.stuckTicks += 20; + } else { + this.stuckTicks = 0; + } + this.lastCheckPosition = position; } } diff --git a/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/RoyalMountSpitGoal.java b/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/RoyalMountSpitGoal.java deleted file mode 100644 index 1dfa4f6b..00000000 --- a/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/RoyalMountSpitGoal.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.craisinlord.antarchy.content.entity.royal; - -import net.minecraft.world.entity.LivingEntity; -import net.minecraft.world.entity.ai.goal.Goal; - -public class RoyalMountSpitGoal extends Goal { - private final RoyalMountEntity mount; - private int cooldown; - - public RoyalMountSpitGoal(RoyalMountEntity mount) { - this.mount = mount; - } - - @Override - public boolean canUse() { - if (this.mount.isVehicle() || this.mount.isOrderedToSit()) { - return false; - } - LivingEntity target = this.mount.getTarget(); - if (target == null || !target.isAlive() || !this.mount.canAttack(target)) { - return false; - } - double distance = this.mount.distanceTo(target); - return distance > 5.0D && distance < 20.0D && this.mount.hasLineOfSight(target); - } - - @Override - public void start() { - this.cooldown = 20; - } - - @Override - public void tick() { - LivingEntity target = this.mount.getTarget(); - if (target == null || !this.mount.canAttack(target)) { - return; - } - this.mount.getLookControl().setLookAt(target, 30.0F, 30.0F); - if (this.cooldown-- <= 0) { - this.mount.startSpitVolley(target); - this.cooldown = 60; - } - } - - @Override - public boolean requiresUpdateEveryTick() { - return true; - } -} diff --git a/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/item/AttitudeAdjusterItem.java b/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/item/AttitudeAdjusterItem.java index 23e58e8a..b5457b5d 100644 --- a/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/item/AttitudeAdjusterItem.java +++ b/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/item/AttitudeAdjusterItem.java @@ -29,6 +29,14 @@ public class AttitudeAdjusterItem extends SwordItem { this.tier = tier; } + @Override + public com.google.common.collect.Multimap getDefaultAttributeModifiers(net.minecraft.world.entity.EquipmentSlot slot) { + if (slot != net.minecraft.world.entity.EquipmentSlot.MAINHAND) { + return super.getDefaultAttributeModifiers(slot); + } + return com.craisinlord.antarchy.compat.item.WeaponAttributes.base(this.tier, (float) Math.round(AntarchySettings.attitudeAdjusterBaseDamage()), ATTACK_SPEED).build(); + } + @Override public void appendHoverText(ItemStack stack, Level level, List tooltipComponents, TooltipFlag tooltipFlag) { tooltipComponents.add(Component.translatable("tooltip.antarchy.attitude_adjuster.grounded").withStyle(ChatFormatting.DARK_GRAY)); diff --git a/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/item/BasiliskDaggerItem.java b/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/item/BasiliskDaggerItem.java index 032e057c..80c1440d 100644 --- a/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/item/BasiliskDaggerItem.java +++ b/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/item/BasiliskDaggerItem.java @@ -21,6 +21,14 @@ public class BasiliskDaggerItem extends SwordItem { this.attackSpeed = attackSpeed; } + @Override + public com.google.common.collect.Multimap getDefaultAttributeModifiers(net.minecraft.world.entity.EquipmentSlot slot) { + if (slot != net.minecraft.world.entity.EquipmentSlot.MAINHAND) { + return super.getDefaultAttributeModifiers(slot); + } + return com.craisinlord.antarchy.compat.item.WeaponAttributes.base(this.tier, (float) Math.round(AntarchySettings.basiliskDaggerAttackDamage()), (float) AntarchySettings.basiliskDaggerAttackSpeed()).build(); + } + @Override public boolean hurtEnemy(ItemStack stack, LivingEntity target, LivingEntity attacker) { boolean didHurt = super.hurtEnemy(stack, target, attacker); diff --git a/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/item/BattleAxeItem.java b/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/item/BattleAxeItem.java index 260d58f9..ebd31d20 100644 --- a/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/item/BattleAxeItem.java +++ b/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/item/BattleAxeItem.java @@ -2,13 +2,23 @@ package com.craisinlord.antarchy.content.item; import com.craisinlord.antarchy.config.AntarchySettings; import com.craisinlord.antarchy.content.client.model.ResourceBackedGeoItemModel; +import com.craisinlord.antarchy.content.item.ultimate.UltimateToolHelper; +import java.util.List; import java.util.function.Consumer; import java.util.function.DoubleSupplier; +import net.minecraft.ChatFormatting; +import net.minecraft.core.BlockPos; +import net.minecraft.network.chat.Component; import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.item.AxeItem; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Tier; +import net.minecraft.world.item.TooltipFlag; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.state.BlockState; import software.bernie.geckolib.animatable.GeoItem; import com.craisinlord.antarchy.content.client.AntarchyGeoItemRenderer; import software.bernie.geckolib.core.animatable.instance.AnimatableInstanceCache; @@ -34,6 +44,33 @@ public class BattleAxeItem extends AxeItem implements GeoItem, com.craisinlord.a GeoItem.registerSyncedAnimatable(this); } + @Override + public com.google.common.collect.Multimap getDefaultAttributeModifiers(net.minecraft.world.entity.EquipmentSlot slot) { + if (slot != net.minecraft.world.entity.EquipmentSlot.MAINHAND) { + return super.getDefaultAttributeModifiers(slot); + } + return com.craisinlord.antarchy.compat.item.WeaponAttributes.base(this.tier, (float) this.attackDamage.getAsDouble(), (float) AntarchySettings.battleAxeAttackSpeed()).build(); + } + + @Override + public void appendHoverText(ItemStack stack, Level level, List tooltipComponents, TooltipFlag tooltipFlag) { + super.appendHoverText(stack, level, tooltipComponents, tooltipFlag); + if (AntarchySettings.ultimateToolsVeinMiningEnabled()) { + tooltipComponents.add(Component.translatable("tooltip.antarchy.battle_axe_tree_felling").withStyle(ChatFormatting.GRAY)); + } + } + + @Override + public boolean mineBlock(ItemStack stack, Level level, BlockState state, BlockPos pos, LivingEntity miningEntity) { + boolean result = super.mineBlock(stack, level, state, pos, miningEntity); + if (AntarchySettings.ultimateToolsVeinMiningEnabled() + && miningEntity instanceof ServerPlayer player + && !player.isShiftKeyDown()) { + UltimateToolHelper.mineTree(stack, level, state, pos, player, AntarchySettings.ultimateToolsTreeFellingMaxLogs()); + } + return result; + } + @Override public boolean isEnchantable(ItemStack stack) { return stack.getCount() == 1; diff --git a/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/item/ultimate/UltimateAxeItem.java b/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/item/ultimate/UltimateAxeItem.java index a4cd9514..afcd81f0 100644 --- a/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/item/ultimate/UltimateAxeItem.java +++ b/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/item/ultimate/UltimateAxeItem.java @@ -31,10 +31,18 @@ public class UltimateAxeItem extends AxeItem { this.attackSpeed = attackSpeed; } + @Override + public com.google.common.collect.Multimap getDefaultAttributeModifiers(net.minecraft.world.entity.EquipmentSlot slot) { + if (slot != net.minecraft.world.entity.EquipmentSlot.MAINHAND) { + return super.getDefaultAttributeModifiers(slot); + } + return com.craisinlord.antarchy.compat.item.WeaponAttributes.base(this.tier, (float) this.attackDamage.getAsDouble(), (float) AntarchySettings.ultimateAxeAttackSpeed()).build(); + } + @Override public void appendHoverText(ItemStack stack, Level level, List tooltipComponents, TooltipFlag tooltipFlag) { super.appendHoverText(stack, level, tooltipComponents, tooltipFlag); - UltimateToolHelper.appendThreeByThreeTooltip(tooltipComponents); + UltimateToolHelper.appendModeTooltip(UltimateToolHelper.ToolKind.AXE, tooltipComponents); } @Override diff --git a/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/item/ultimate/UltimateHoeItem.java b/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/item/ultimate/UltimateHoeItem.java index fb737915..8408b794 100644 --- a/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/item/ultimate/UltimateHoeItem.java +++ b/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/item/ultimate/UltimateHoeItem.java @@ -31,10 +31,18 @@ public class UltimateHoeItem extends HoeItem { this.attackSpeed = attackSpeed; } + @Override + public com.google.common.collect.Multimap getDefaultAttributeModifiers(net.minecraft.world.entity.EquipmentSlot slot) { + if (slot != net.minecraft.world.entity.EquipmentSlot.MAINHAND) { + return super.getDefaultAttributeModifiers(slot); + } + return com.craisinlord.antarchy.compat.item.WeaponAttributes.base(this.tier, (float) Math.round(this.attackDamage.getAsDouble()), (float) AntarchySettings.ultimateHoeAttackSpeed()).build(); + } + @Override public void appendHoverText(ItemStack stack, Level level, List tooltipComponents, TooltipFlag tooltipFlag) { super.appendHoverText(stack, level, tooltipComponents, tooltipFlag); - UltimateToolHelper.appendThreeByThreeTooltip(tooltipComponents); + UltimateToolHelper.appendModeTooltip(UltimateToolHelper.ToolKind.HOE, tooltipComponents); } @Override diff --git a/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/item/ultimate/UltimatePickaxeItem.java b/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/item/ultimate/UltimatePickaxeItem.java index 0b306189..a1548cae 100644 --- a/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/item/ultimate/UltimatePickaxeItem.java +++ b/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/item/ultimate/UltimatePickaxeItem.java @@ -31,10 +31,18 @@ public class UltimatePickaxeItem extends PickaxeItem { this.attackSpeed = attackSpeed; } + @Override + public com.google.common.collect.Multimap getDefaultAttributeModifiers(net.minecraft.world.entity.EquipmentSlot slot) { + if (slot != net.minecraft.world.entity.EquipmentSlot.MAINHAND) { + return super.getDefaultAttributeModifiers(slot); + } + return com.craisinlord.antarchy.compat.item.WeaponAttributes.base(this.tier, (float) Math.round(this.attackDamage.getAsDouble()), (float) AntarchySettings.ultimatePickaxeAttackSpeed()).build(); + } + @Override public void appendHoverText(ItemStack stack, Level level, List tooltipComponents, TooltipFlag tooltipFlag) { super.appendHoverText(stack, level, tooltipComponents, tooltipFlag); - UltimateToolHelper.appendThreeByThreeTooltip(tooltipComponents); + UltimateToolHelper.appendModeTooltip(UltimateToolHelper.ToolKind.PICKAXE, tooltipComponents); } @Override diff --git a/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/item/ultimate/UltimateSwordItem.java b/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/item/ultimate/UltimateSwordItem.java index 9eec0d49..897bd322 100644 --- a/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/item/ultimate/UltimateSwordItem.java +++ b/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/item/ultimate/UltimateSwordItem.java @@ -19,6 +19,14 @@ public class UltimateSwordItem extends SwordItem { this.attackSpeed = attackSpeed; } + @Override + public com.google.common.collect.Multimap getDefaultAttributeModifiers(net.minecraft.world.entity.EquipmentSlot slot) { + if (slot != net.minecraft.world.entity.EquipmentSlot.MAINHAND) { + return super.getDefaultAttributeModifiers(slot); + } + return com.craisinlord.antarchy.compat.item.WeaponAttributes.base(this.tier, (float) Math.round(this.attackDamage.getAsDouble()), (float) AntarchySettings.ultimateSwordAttackSpeed()).build(); + } + @Override public boolean isEnchantable(ItemStack stack) { return stack.getCount() == 1; diff --git a/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/item/ultimate/UltimateToolHelper.java b/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/item/ultimate/UltimateToolHelper.java index c72b4775..f46a824e 100644 --- a/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/item/ultimate/UltimateToolHelper.java +++ b/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/item/ultimate/UltimateToolHelper.java @@ -3,12 +3,16 @@ package com.craisinlord.antarchy.content.item.ultimate; import com.craisinlord.antarchy.config.AntarchySettings; import com.craisinlord.antarchy.content.item.GiantFryingPanItem; import com.craisinlord.antarchy.content.item.GiantFryingPanToolHelper; +import java.util.ArrayDeque; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Set; import java.util.function.Function; import net.minecraft.ChatFormatting; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; +import net.minecraft.nbt.CompoundTag; import net.minecraft.network.chat.Component; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; @@ -23,12 +27,16 @@ import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Tier; import net.minecraft.world.item.context.UseOnContext; import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.properties.BlockStateProperties; import net.minecraft.world.phys.BlockHitResult; import net.minecraft.world.phys.Vec3; public final class UltimateToolHelper { private static final String THREE_BY_THREE_TAG = "antarchy.ultimate_three_by_three"; + private static final String MINING_MODE_TAG = "antarchy.ultimate_mining_mode"; private static final ThreadLocal BREAKING_EXTRA_BLOCKS = ThreadLocal.withInitial(() -> false); private static final float GENERAL_MINING_MULTIPLIER = 1.1F; private static final float ULTIMATE_SHOVEL_MINING_SPEED = 24.0F; @@ -38,26 +46,43 @@ public final class UltimateToolHelper { public static InteractionResultHolder handleToggleUse(Level level, Player player, InteractionHand hand) { ItemStack stack = player.getItemInHand(hand); - if (!AntarchySettings.ultimateToolsThreeByThreeEnabled() || !player.isShiftKeyDown()) { + if (!player.isShiftKeyDown() || availableModes(toolKindOf(stack.getItem())).size() < 2) { return InteractionResultHolder.pass(stack); } - toggleThreeByThree(level, player, stack); + cycleMiningMode(level, player, stack); return InteractionResultHolder.sidedSuccess(stack, level.isClientSide); } public static InteractionResult handleToggleUseOn(UseOnContext context) { Player player = context.getPlayer(); - if (!AntarchySettings.ultimateToolsThreeByThreeEnabled() || player == null || !player.isShiftKeyDown()) { + if (player == null || !player.isShiftKeyDown() || availableModes(toolKindOf(context.getItemInHand().getItem())).size() < 2) { return InteractionResult.PASS; } - toggleThreeByThree(context.getLevel(), player, context.getItemInHand()); + cycleMiningMode(context.getLevel(), player, context.getItemInHand()); return InteractionResult.sidedSuccess(context.getLevel().isClientSide); } public static boolean isThreeByThreeEnabled(ItemStack stack) { - return stack.hasTag() && stack.getTag().getBoolean(THREE_BY_THREE_TAG); + return getMiningMode(stack) == MiningMode.THREE_BY_THREE; + } + + public static boolean isVeinEnabled(ItemStack stack) { + return getMiningMode(stack) == MiningMode.VEIN; + } + + public static MiningMode getMiningMode(ItemStack stack) { + CompoundTag tag = stack.getTag(); + if (tag == null) { + return MiningMode.DEFAULT; + } + if (tag.contains(MINING_MODE_TAG)) { + int index = tag.getByte(MINING_MODE_TAG); + MiningMode[] modes = MiningMode.values(); + return index >= 0 && index < modes.length ? modes[index] : MiningMode.DEFAULT; + } + return tag.getBoolean(THREE_BY_THREE_TAG) ? MiningMode.THREE_BY_THREE : MiningMode.DEFAULT; } public static float getDestroySpeed(ToolKind toolKind, Tier tier, ItemStack stack, BlockState state, float defaultSpeed) { @@ -74,23 +99,127 @@ public final class UltimateToolHelper { return defaultSpeed * GENERAL_MINING_MULTIPLIER; } - public static void appendThreeByThreeTooltip(List tooltip) { - if (!AntarchySettings.ultimateToolsThreeByThreeEnabled()) { + public static void appendModeTooltip(ToolKind toolKind, List tooltip) { + List modes = availableModes(toolKind); + boolean area = modes.contains(MiningMode.THREE_BY_THREE); + boolean vein = modes.contains(MiningMode.VEIN); + String key; + if (area && vein) { + key = "tooltip.antarchy.ultimate_tools_3x3_vein"; + } else if (area) { + key = "tooltip.antarchy.ultimate_tools_3x3"; + } else if (vein) { + key = "tooltip.antarchy.ultimate_tools_vein"; + } else { return; } - tooltip.add(Component.translatable("tooltip.antarchy.ultimate_tools_3x3").withStyle(ChatFormatting.GRAY)); + tooltip.add(Component.translatable(key).withStyle(ChatFormatting.GRAY)); } public static void handleAreaMining(ToolKind toolKind, ItemStack stack, Level level, BlockState originState, BlockPos origin, LivingEntity entity) { - if (!AntarchySettings.ultimateToolsThreeByThreeEnabled() - || level.isClientSide + if (level.isClientSide || BREAKING_EXTRA_BLOCKS.get() - || !isThreeByThreeEnabled(stack) || !(entity instanceof ServerPlayer player)) { return; } - mineArea(toolKind, stack, level, originState, origin, player, 1); + MiningMode mode = getMiningMode(stack); + if (mode == MiningMode.DEFAULT || !availableModes(toolKind).contains(mode)) { + return; + } + if (mode == MiningMode.THREE_BY_THREE) { + mineArea(toolKind, stack, level, originState, origin, player, 1); + } else { + mineVein(toolKind, stack, level, originState, origin, player, veinLimit(originState)); + } + } + + public static int veinLimit(BlockState state) { + return state.is(BlockTags.LOGS) + ? AntarchySettings.ultimateToolsTreeFellingMaxLogs() + : AntarchySettings.ultimateToolsVeinMiningMaxBlocks(); + } + + public static void mineVein(ToolKind toolKind, ItemStack stack, Level level, BlockState originState, BlockPos origin, ServerPlayer player, int maxBlocks) { + if (level.isClientSide || BREAKING_EXTRA_BLOCKS.get() || maxBlocks <= 0 || !matchesToolKind(toolKind, originState)) { + return; + } + + breakAll(toolKind, stack, level, player, collectConnected(level, origin, originState.getBlock(), maxBlocks, false)); + } + + public static void mineTree(ItemStack stack, Level level, BlockState originState, BlockPos origin, ServerPlayer player, int maxBlocks) { + if (level.isClientSide || BREAKING_EXTRA_BLOCKS.get() || maxBlocks <= 0 || !originState.is(BlockTags.LOGS)) { + return; + } + + breakAll(ToolKind.AXE, stack, level, player, collectConnected(level, origin, originState.getBlock(), maxBlocks, true)); + } + + private static void breakAll(ToolKind toolKind, ItemStack stack, Level level, ServerPlayer player, List targets) { + if (targets.isEmpty()) { + return; + } + + BREAKING_EXTRA_BLOCKS.set(true); + try { + for (BlockPos targetPos : targets) { + if (!canBreak(toolKind, player, stack, level, targetPos, level.getBlockState(targetPos))) { + continue; + } + + player.gameMode.destroyBlock(targetPos); + if (stack.isEmpty()) { + break; + } + } + } finally { + BREAKING_EXTRA_BLOCKS.set(false); + } + } + + private static List collectConnected(Level level, BlockPos origin, Block block, int maxBlocks, boolean requireFoliage) { + List found = new ArrayList<>(); + Set visited = new HashSet<>(); + ArrayDeque queue = new ArrayDeque<>(); + visited.add(origin); + queue.add(origin); + boolean foliage = false; + + while (!queue.isEmpty() && found.size() < maxBlocks) { + BlockPos current = queue.poll(); + for (int dx = -1; dx <= 1 && found.size() < maxBlocks; dx++) { + for (int dy = -1; dy <= 1 && found.size() < maxBlocks; dy++) { + for (int dz = -1; dz <= 1 && found.size() < maxBlocks; dz++) { + if (dx == 0 && dy == 0 && dz == 0) { + continue; + } + + BlockPos next = current.offset(dx, dy, dz); + if (!visited.add(next) || !level.isLoaded(next)) { + continue; + } + + BlockState state = level.getBlockState(next); + if (state.is(block)) { + found.add(next); + queue.add(next); + } else if (requireFoliage && !foliage && isNaturalFoliage(state)) { + foliage = true; + } + } + } + } + } + + return requireFoliage && !foliage ? List.of() : found; + } + + private static boolean isNaturalFoliage(BlockState state) { + if (state.is(BlockTags.LEAVES)) { + return !state.hasProperty(BlockStateProperties.PERSISTENT) || !state.getValue(BlockStateProperties.PERSISTENT); + } + return state.is(BlockTags.WART_BLOCKS) || state.is(Blocks.SHROOMLIGHT); } public static void mineArea(ToolKind toolKind, ItemStack stack, Level level, BlockState originState, BlockPos origin, ServerPlayer player, int radius) { @@ -211,23 +340,50 @@ public final class UltimateToolHelper { HOE } - private static void toggleThreeByThree(Level level, Player player, ItemStack stack) { - boolean enabled = !isThreeByThreeEnabled(stack); - if (enabled) { - stack.getOrCreateTag().putBoolean(THREE_BY_THREE_TAG, true); - } else if (stack.hasTag()) { - stack.getTag().remove(THREE_BY_THREE_TAG); + public enum MiningMode { + DEFAULT, + THREE_BY_THREE, + VEIN + } + + private static List availableModes(ToolKind toolKind) { + List modes = new ArrayList<>(3); + if (toolKind == null) { + return modes; + } + modes.add(MiningMode.DEFAULT); + if (AntarchySettings.ultimateToolsThreeByThreeEnabled()) { + modes.add(MiningMode.THREE_BY_THREE); + } + if (AntarchySettings.ultimateToolsVeinMiningEnabled() && (toolKind == ToolKind.PICKAXE || toolKind == ToolKind.AXE)) { + modes.add(MiningMode.VEIN); + } + return modes; + } + + private static void cycleMiningMode(Level level, Player player, ItemStack stack) { + List modes = availableModes(toolKindOf(stack.getItem())); + int current = modes.indexOf(getMiningMode(stack)); + MiningMode next = modes.get((current + 1) % modes.size()); + if (next == MiningMode.DEFAULT) { + CompoundTag tag = stack.getTag(); + if (tag != null) { + tag.remove(THREE_BY_THREE_TAG); + tag.remove(MINING_MODE_TAG); + } + } else { + CompoundTag tag = stack.getOrCreateTag(); + tag.remove(THREE_BY_THREE_TAG); + tag.putByte(MINING_MODE_TAG, (byte) next.ordinal()); } if (!level.isClientSide) { - player.displayClientMessage( - Component.translatable( - enabled - ? "message.antarchy.ultimate_tool.mode_3x3" - : "message.antarchy.ultimate_tool.mode_default" - ), - true - ); + String key = switch (next) { + case DEFAULT -> "message.antarchy.ultimate_tool.mode_default"; + case THREE_BY_THREE -> "message.antarchy.ultimate_tool.mode_3x3"; + case VEIN -> "message.antarchy.ultimate_tool.mode_vein"; + }; + player.displayClientMessage(Component.translatable(key), true); } } diff --git a/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/item/ultimate/UtlimateShovelItem.java b/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/item/ultimate/UtlimateShovelItem.java index 3fb3bc68..20743781 100644 --- a/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/item/ultimate/UtlimateShovelItem.java +++ b/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/item/ultimate/UtlimateShovelItem.java @@ -34,10 +34,18 @@ public class UtlimateShovelItem extends ShovelItem { this.attackSpeed = attackSpeed; } + @Override + public com.google.common.collect.Multimap getDefaultAttributeModifiers(net.minecraft.world.entity.EquipmentSlot slot) { + if (slot != net.minecraft.world.entity.EquipmentSlot.MAINHAND) { + return super.getDefaultAttributeModifiers(slot); + } + return com.craisinlord.antarchy.compat.item.WeaponAttributes.base(this.tier, (float) this.attackDamage.getAsDouble(), (float) AntarchySettings.ultimateShovelAttackSpeed()).build(); + } + @Override public void appendHoverText(ItemStack stack, Level level, List tooltipComponents, TooltipFlag tooltipFlag) { super.appendHoverText(stack, level, tooltipComponents, tooltipFlag); - UltimateToolHelper.appendThreeByThreeTooltip(tooltipComponents); + UltimateToolHelper.appendModeTooltip(UltimateToolHelper.ToolKind.SHOVEL, tooltipComponents); } @Override diff --git a/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/network/RoyalMountActionPayload.java b/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/network/RoyalMountActionPayload.java index 78324b44..9076d4f9 100644 --- a/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/network/RoyalMountActionPayload.java +++ b/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/network/RoyalMountActionPayload.java @@ -12,6 +12,8 @@ public record RoyalMountActionPayload(int action) implements CustomPacketPayload public static final int BITE = 1; public static final int SPIT = 2; public static final int MOUNT = 3; + public static final int BEAM = 4; + public static final int SIGNATURE = 5; public static final Type TYPE = new Type<>(new ResourceLocation(Antarchy.MODID, "royal_mount_action")); diff --git a/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/worldgen/ants/TermiteNestFeature.java b/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/worldgen/ants/TermiteNestFeature.java index f21f9441..1ef5a9fe 100644 --- a/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/worldgen/ants/TermiteNestFeature.java +++ b/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/worldgen/ants/TermiteNestFeature.java @@ -34,7 +34,7 @@ public class TermiteNestFeature extends SurfaceAntNestFeature { protected BlockPos resolveNestPos(WorldGenLevel level, BlockPos origin) { int x = origin.getX(); int z = origin.getZ(); - int startY = level.getHeight(Heightmap.Types.WORLD_SURFACE_WG, x, z) - 1; + int startY = Math.min(origin.getY(), level.getHeight(Heightmap.Types.WORLD_SURFACE_WG, x, z) - 1); int minY = level.getMinBuildHeight() + 1; for (int y = startY; y >= minY; y--) { diff --git a/common-1.20.1/src/main/java/com/craisinlord/antarchy/mixins/MantisClawWallClimbMixin.java b/common-1.20.1/src/main/java/com/craisinlord/antarchy/mixins/MantisClawWallClimbMixin.java index 9896bdbd..f6707386 100644 --- a/common-1.20.1/src/main/java/com/craisinlord/antarchy/mixins/MantisClawWallClimbMixin.java +++ b/common-1.20.1/src/main/java/com/craisinlord/antarchy/mixins/MantisClawWallClimbMixin.java @@ -1,5 +1,6 @@ package com.craisinlord.antarchy.mixins; +import com.craisinlord.antarchy.content.AntarchyTags; import com.craisinlord.antarchy.content.item.MantisClawItem; import net.minecraft.server.level.ServerPlayer; import net.minecraft.sounds.SoundEvents; @@ -91,6 +92,7 @@ public abstract class MantisClawWallClimbMixin extends LivingEntity { Player player = (Player) (Object) this; return MantisClawItem.isDualWielding(player) && this.horizontalCollision + && !this.level().dimensionTypeRegistration().is(AntarchyTags.DimensionTypes.NO_MANTIS_CLIMB) && !this.onGround() && !this.isInWaterOrBubble() && !this.isPassenger() diff --git a/common-1.20.1/src/main/java/com/craisinlord/antarchy/mixins/client/MountedVehicleCameraMixin.java b/common-1.20.1/src/main/java/com/craisinlord/antarchy/mixins/client/MountedVehicleCameraMixin.java index 5a4e5c7a..ef5158e6 100644 --- a/common-1.20.1/src/main/java/com/craisinlord/antarchy/mixins/client/MountedVehicleCameraMixin.java +++ b/common-1.20.1/src/main/java/com/craisinlord/antarchy/mixins/client/MountedVehicleCameraMixin.java @@ -1,6 +1,7 @@ package com.craisinlord.antarchy.mixins.client; import com.craisinlord.antarchy.content.entity.HerculesBeetleEntity; +import com.craisinlord.antarchy.content.entity.royal.RoyalMountEntity; import net.minecraft.client.Camera; import net.minecraft.world.entity.Entity; import net.minecraft.world.level.BlockGetter; @@ -10,6 +11,7 @@ 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.ModifyArg; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @Mixin(Camera.class) @@ -23,6 +25,18 @@ public abstract class MountedVehicleCameraMixin { @Shadow protected abstract void setPosition(Vec3 position); + @ModifyArg( + method = "setup", + at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Camera;getMaxZoom(D)D"), + index = 0 + ) + private double antarchy$extendRoyalMountZoom(double distance) { + if (this.entity != null && this.entity.getVehicle() instanceof RoyalMountEntity mount) { + return distance + mount.thirdPersonCameraBonus(); + } + return distance; + } + @Inject(method = "setup", at = @At("RETURN")) private void antarchy$adjustMountedCamera( BlockGetter level, @@ -32,6 +46,11 @@ public abstract class MountedVehicleCameraMixin { float partialTick, CallbackInfo ci ) { + if (this.detached && this.entity != null && this.entity.getVehicle() instanceof RoyalMountEntity mount) { + Camera camera = (Camera) (Object) this; + this.setPosition(camera.getPosition().add(new Vec3(camera.getUpVector()).scale(mount.thirdPersonCameraLift()))); + return; + } if (!this.antarchy$ridingHerculesBeetle()) { return; } diff --git a/common-1.20.1/src/main/resources/assets/antarchy/lang/de_de.json b/common-1.20.1/src/main/resources/assets/antarchy/lang/de_de.json index 45fb7005..871e012f 100644 --- a/common-1.20.1/src/main/resources/assets/antarchy/lang/de_de.json +++ b/common-1.20.1/src/main/resources/assets/antarchy/lang/de_de.json @@ -517,6 +517,7 @@ "message.antarchy.big_bertha.mode.molevore": "Molevore", "message.antarchy.big_bertha.mode.toreterror": "Toreterror", "message.antarchy.ultimate_tool.mode_3x3": "Auf 3x3-Abbau umgeschaltet", + "message.antarchy.ultimate_tool.mode_vein": "Auf Adernabbau umgeschaltet", "message.antarchy.ultimate_tool.mode_default": "Auf normalen Abbau umgeschaltet", "advancements.antarchy.hey_listen.title": "Hey, hör zu!", "advancements.antarchy.hey_listen.description": "Eine Reverie hat sich dir angeschlossen.", @@ -549,6 +550,9 @@ "tooltip.antarchy.big_bertha.mode.molevore": "Rechtsklick, um dich drehend nach vorn zu bohren und Molevore-Blöcke zu durchdringen.", "tooltip.antarchy.big_bertha.mode.toreterror": "Rechtsklick, um in die Luft zu springen und mit einer Flächenschockwelle herabzustürzen.", "tooltip.antarchy.ultimate_tools_3x3": "Umschalt-Rechtsklick, um 3x3-Abbau umzuschalten.", + "tooltip.antarchy.ultimate_tools_vein": "Umschalt-Rechtsklick, um Adernabbau umzuschalten.", + "tooltip.antarchy.ultimate_tools_3x3_vein": "Umschalt-Rechtsklick wechselt zwischen 3x3-Abbau und Adernabbau.", + "tooltip.antarchy.battle_axe_tree_felling": "Fällt ganze Bäume. Schleichen, um einen einzelnen Stamm zu hacken.", "tooltip.antarchy.moggles.reveal": "Gewährt Nachtsicht und weist auf nahe Höhlen hin.", "tooltip.antarchy.moggles.underground": "Funktioniert nur, wenn du keinen freien Blick zum Himmel hast.", "tooltip.antarchy.cornea_ear.vision": "Gewährt %s Sekunden lang Nachtsicht.", diff --git a/common-1.20.1/src/main/resources/assets/antarchy/lang/en_us.json b/common-1.20.1/src/main/resources/assets/antarchy/lang/en_us.json index 94a13611..ad57d8f3 100644 --- a/common-1.20.1/src/main/resources/assets/antarchy/lang/en_us.json +++ b/common-1.20.1/src/main/resources/assets/antarchy/lang/en_us.json @@ -604,6 +604,16 @@ "message.antarchy.royal_boundary.activated": "Royal Boundary raised", "message.antarchy.royal_boundary.recharging": "Royal Boundary is recharging", "key.antarchy.mount_flight_toggle": "Mount: Toggle Flight", + "key.antarchy.mount_beam": "Royal Mount: Beam", + "key.antarchy.mount_signature": "Royal Mount: Command / Time Field", + "entity.antarchy.royal_mount.stage.hatchling": "Hatchling", + "entity.antarchy.royal_mount.stage.juvenile": "Juvenile", + "entity.antarchy.royal_mount.stage.adult": "Adult", + "message.antarchy.royal_mount.stage_up": "%s has reached the %s stage!", + "message.antarchy.royal_mount.growth": "%s: %s, %s%% grown", + "message.antarchy.royal_mount.too_young_ride": "%s must reach the %s stage before it can be ridden", + "message.antarchy.royal_mount.too_young_fly": "%s must reach the %s stage before it can fly", + "message.antarchy.royal_mount.too_young_ability": "%s must reach the %s stage to use that", "item.antarchy.springbug_leg": "Springbug Leg", "item.antarchy.springy_boots": "Springy Boots", "tooltip.antarchy.springy_boots": "Sneak to charge, then jump to spring upward.", @@ -791,6 +801,7 @@ "message.antarchy.big_bertha.mode.molevore": "Molevore", "message.antarchy.big_bertha.mode.toreterror": "Toreterror", "message.antarchy.ultimate_tool.mode_3x3": "Switched to 3x3 Mining", + "message.antarchy.ultimate_tool.mode_vein": "Switched to Vein Mining", "message.antarchy.ultimate_tool.mode_default": "Switched to Default Mining", "advancements.antarchy.hey_listen.title": "Hey Listen", "advancements.antarchy.hey_listen.description": "A Reverie has joined you.", @@ -829,6 +840,9 @@ "tooltip.antarchy.big_bertha.mode.molevore": "Use to spin forward and drill through Molevore blocks.", "tooltip.antarchy.big_bertha.mode.toreterror": "Use to launch into the air and crash down with an AOE shockwave.", "tooltip.antarchy.ultimate_tools_3x3": "Shift + use to toggle 3x3 Mining.", + "tooltip.antarchy.ultimate_tools_vein": "Shift + use to toggle Vein Mining.", + "tooltip.antarchy.ultimate_tools_3x3_vein": "Shift + use to cycle 3x3 and Vein Mining.", + "tooltip.antarchy.battle_axe_tree_felling": "Fells whole trees. Sneak to chop a single log.", "tooltip.antarchy.moggles.reveal": "Gives night vision and points to nearby caves.", "tooltip.antarchy.moggles.underground": "Works only while you have no sky access.", "tooltip.antarchy.cornea_ear.vision": "Grants Night Vision for %s seconds.", @@ -1644,12 +1658,12 @@ ,"guide.antarchy.entry.queen.description6": "In her final phase she also casts her own time dilation field, slowing anyone caught inside it." ,"guide.antarchy.entry.prince.title": "Prince" ,"guide.antarchy.entry.prince.subtitle": "Royal mount record" - ,"guide.antarchy.entry.prince.description": "Prince Eggs hatch into small Princes that grow when fed golden items." - ,"guide.antarchy.entry.prince.description2": "A grown Prince can be tamed and saddled for flight, fires elemental bolts at your enemies, and can command nearby hostile mobs. Feed responsibly; you are raising a small flying warlord." + ,"guide.antarchy.entry.prince.description": "Prince Eggs hatch into Hatchlings bonded to whoever placed the egg. Golden apples grow them a little and enchanted golden apples grow them a lot: Juveniles can be ridden and spit bolts, and Adults can fly, fire beams, and use their signature ability." + ,"guide.antarchy.entry.prince.description2": "The Prince is a ground brawler: he closes in to bite with all three heads, spits fire, ice, and lightning, and as an Adult commands nearby hostile mobs to fight for him. Feed responsibly; you are raising a small flying warlord." ,"guide.antarchy.entry.princess.title": "Princess" ,"guide.antarchy.entry.princess.subtitle": "Royal mount record" - ,"guide.antarchy.entry.princess.description": "Princess Eggs hatch into small Princesses that grow when fed golden items." - ,"guide.antarchy.entry.princess.description2": "A grown Princess can be tamed and saddled for flight, fires elemental bolts, and creates time-dilation fields around enemies." + ,"guide.antarchy.entry.princess.description": "Princess Eggs hatch into Hatchlings bonded to whoever placed the egg. Golden apples grow them a little and enchanted golden apples grow them a lot: Juveniles can be ridden and spit bolts, and Adults can fly, fire beams, and use their signature ability." + ,"guide.antarchy.entry.princess.description2": "The Princess fights at range: she keeps her distance, fires slowing bolts from her orbs, and as an Adult hovers above her foes and traps them in time-dilation fields." ,"subtitles.antarchy.hoverboard_mount": "Hoverboard starts" ,"subtitles.antarchy.hoverboard_dismount": "Hoverboard stops" ,"subtitles.antarchy.hoverboard_idle": "Hoverboard hums" diff --git a/common-1.20.1/src/main/resources/assets/antarchy/lang/es_es.json b/common-1.20.1/src/main/resources/assets/antarchy/lang/es_es.json index 0850ea36..efdc47ff 100644 --- a/common-1.20.1/src/main/resources/assets/antarchy/lang/es_es.json +++ b/common-1.20.1/src/main/resources/assets/antarchy/lang/es_es.json @@ -517,6 +517,7 @@ "message.antarchy.big_bertha.mode.molevore": "Molevore", "message.antarchy.big_bertha.mode.toreterror": "Toreterror", "message.antarchy.ultimate_tool.mode_3x3": "Cambiado a minería 3x3", + "message.antarchy.ultimate_tool.mode_vein": "Cambiado a minería de vetas", "message.antarchy.ultimate_tool.mode_default": "Cambiado a minería normal", "advancements.antarchy.hey_listen.title": "¡Oye, escucha!", "advancements.antarchy.hey_listen.description": "Una Reverie se ha unido a ti.", @@ -549,6 +550,9 @@ "tooltip.antarchy.big_bertha.mode.molevore": "Clic derecho para girar hacia delante y perforar bloques de Molevore.", "tooltip.antarchy.big_bertha.mode.toreterror": "Clic derecho para lanzarte al aire y caer con una onda de choque en área.", "tooltip.antarchy.ultimate_tools_3x3": "Mayús + clic derecho para alternar la minería 3x3.", + "tooltip.antarchy.ultimate_tools_vein": "Mayús + clic derecho para alternar la minería de vetas.", + "tooltip.antarchy.ultimate_tools_3x3_vein": "Mayús + clic derecho para alternar entre minería 3x3 y de vetas.", + "tooltip.antarchy.battle_axe_tree_felling": "Tala árboles enteros. Agáchate para cortar un solo tronco.", "tooltip.antarchy.moggles.reveal": "Otorga Visión nocturna y señala cuevas cercanas.", "tooltip.antarchy.moggles.underground": "Solo funciona cuando no tienes acceso al cielo.", "tooltip.antarchy.cornea_ear.vision": "Otorga Visión nocturna durante %s segundos.", diff --git a/common-1.20.1/src/main/resources/assets/antarchy/lang/fr_fr.json b/common-1.20.1/src/main/resources/assets/antarchy/lang/fr_fr.json index 4a8625e7..c7276816 100644 --- a/common-1.20.1/src/main/resources/assets/antarchy/lang/fr_fr.json +++ b/common-1.20.1/src/main/resources/assets/antarchy/lang/fr_fr.json @@ -517,6 +517,7 @@ "message.antarchy.big_bertha.mode.molevore": "Molevore", "message.antarchy.big_bertha.mode.toreterror": "Toreterror", "message.antarchy.ultimate_tool.mode_3x3": "Mode minage 3x3 activé", + "message.antarchy.ultimate_tool.mode_vein": "Mode minage de filons activé", "message.antarchy.ultimate_tool.mode_default": "Mode minage normal activé", "advancements.antarchy.hey_listen.title": "Hé, écoute !", "advancements.antarchy.hey_listen.description": "Une Reverie vous a rejoint.", @@ -549,6 +550,9 @@ "tooltip.antarchy.big_bertha.mode.molevore": "Clic droit pour tournoyer vers l’avant et forer les blocs de Molevore.", "tooltip.antarchy.big_bertha.mode.toreterror": "Clic droit pour bondir dans les airs et retomber avec une onde de choc de zone.", "tooltip.antarchy.ultimate_tools_3x3": "Maj + clic droit pour activer ou désactiver le minage 3x3.", + "tooltip.antarchy.ultimate_tools_vein": "Maj + clic droit pour activer ou désactiver le minage de filons.", + "tooltip.antarchy.ultimate_tools_3x3_vein": "Maj + clic droit pour alterner entre minage 3x3 et minage de filons.", + "tooltip.antarchy.battle_axe_tree_felling": "Abat des arbres entiers. Accroupis-toi pour couper une seule bûche.", "tooltip.antarchy.moggles.reveal": "Accorde Vision nocturne et indique les grottes proches.", "tooltip.antarchy.moggles.underground": "Fonctionne uniquement lorsque vous n’avez pas accès au ciel.", "tooltip.antarchy.cornea_ear.vision": "Accorde Vision nocturne pendant %s secondes.", diff --git a/common-1.20.1/src/main/resources/assets/antarchy/lang/it_it.json b/common-1.20.1/src/main/resources/assets/antarchy/lang/it_it.json index 46a76f73..2447e7eb 100644 --- a/common-1.20.1/src/main/resources/assets/antarchy/lang/it_it.json +++ b/common-1.20.1/src/main/resources/assets/antarchy/lang/it_it.json @@ -517,6 +517,7 @@ "message.antarchy.big_bertha.mode.molevore": "Molevore", "message.antarchy.big_bertha.mode.toreterror": "Toreterror", "message.antarchy.ultimate_tool.mode_3x3": "Scavo 3×3 attivato", + "message.antarchy.ultimate_tool.mode_vein": "Scavo di vene attivato", "message.antarchy.ultimate_tool.mode_default": "Scavo normale attivato", "advancements.antarchy.hey_listen.title": "Ehi, ascolta!", "advancements.antarchy.hey_listen.description": "Una Reverie si è unita a te.", @@ -549,6 +550,9 @@ "tooltip.antarchy.big_bertha.mode.molevore": "Fai clic destro per ruotare in avanti e perforare i blocchi di Molevore.", "tooltip.antarchy.big_bertha.mode.toreterror": "Fai clic destro per balzare in aria e schiantarti con un’onda d’urto ad area.", "tooltip.antarchy.ultimate_tools_3x3": "Maiusc + clic destro per attivare o disattivare lo scavo 3×3.", + "tooltip.antarchy.ultimate_tools_vein": "Maiusc + clic destro per attivare o disattivare lo scavo di vene.", + "tooltip.antarchy.ultimate_tools_3x3_vein": "Maiusc + clic destro per alternare scavo 3×3 e scavo di vene.", + "tooltip.antarchy.battle_axe_tree_felling": "Abbatte alberi interi. Accovacciati per tagliare un solo tronco.", "tooltip.antarchy.moggles.reveal": "Fornisce Visione notturna e indica le grotte vicine.", "tooltip.antarchy.moggles.underground": "Funziona solo quando non puoi vedere il cielo.", "tooltip.antarchy.cornea_ear.vision": "Conferisce Visione notturna per %s secondi.", diff --git a/common-1.20.1/src/main/resources/assets/antarchy/lang/ja_jp.json b/common-1.20.1/src/main/resources/assets/antarchy/lang/ja_jp.json index 58d8c476..4dacd3cc 100644 --- a/common-1.20.1/src/main/resources/assets/antarchy/lang/ja_jp.json +++ b/common-1.20.1/src/main/resources/assets/antarchy/lang/ja_jp.json @@ -517,6 +517,7 @@ "message.antarchy.big_bertha.mode.molevore": "モールヴォア", "message.antarchy.big_bertha.mode.toreterror": "トアテラー", "message.antarchy.ultimate_tool.mode_3x3": "3×3採掘に切り替えました", + "message.antarchy.ultimate_tool.mode_vein": "鉱脈採掘に切り替えました", "message.antarchy.ultimate_tool.mode_default": "通常採掘に切り替えました", "advancements.antarchy.hey_listen.title": "ねえ、聞いて!", "advancements.antarchy.hey_listen.description": "レヴェリーが仲間になりました。", @@ -549,6 +550,9 @@ "tooltip.antarchy.big_bertha.mode.molevore": "右クリックで回転しながら前進し、モールヴォアブロックを掘り抜きます。", "tooltip.antarchy.big_bertha.mode.toreterror": "右クリックで空中へ飛び上がり、範囲衝撃波とともに落下します。", "tooltip.antarchy.ultimate_tools_3x3": "シフトキー+右クリックで3×3採掘を切り替えます。", + "tooltip.antarchy.ultimate_tools_vein": "シフトキー+右クリックで鉱脈採掘を切り替えます。", + "tooltip.antarchy.ultimate_tools_3x3_vein": "シフトキー+右クリックで3×3採掘と鉱脈採掘を切り替えます。", + "tooltip.antarchy.battle_axe_tree_felling": "木を丸ごと伐採します。スニーク中は原木を1つだけ切ります。", "tooltip.antarchy.moggles.reveal": "暗視を付与し、近くの洞窟を示します。", "tooltip.antarchy.moggles.underground": "空が見えない場所でのみ機能します。", "tooltip.antarchy.cornea_ear.vision": "%s秒間暗視を付与します。", diff --git a/common-1.20.1/src/main/resources/assets/antarchy/lang/ko_kr.json b/common-1.20.1/src/main/resources/assets/antarchy/lang/ko_kr.json index 86001626..9eccad8c 100644 --- a/common-1.20.1/src/main/resources/assets/antarchy/lang/ko_kr.json +++ b/common-1.20.1/src/main/resources/assets/antarchy/lang/ko_kr.json @@ -792,6 +792,7 @@ "message.antarchy.big_bertha.mode.molevore": "몰레보어", "message.antarchy.big_bertha.mode.toreterror": "토레테러", "message.antarchy.ultimate_tool.mode_3x3": "3x3 채굴 모드로 전환됨", + "message.antarchy.ultimate_tool.mode_vein": "광맥 채굴 모드로 전환됨", "message.antarchy.ultimate_tool.mode_default": "기본 채굴 모드로 전환됨", "advancements.antarchy.hey_listen.title": "야, 들어봐!", "advancements.antarchy.hey_listen.description": "레버리가 당신과 함께합니다.", @@ -830,6 +831,9 @@ "tooltip.antarchy.big_bertha.mode.molevore": "공격 시 추가 피해를 입히고 대상을 공중 높이 띄웁니다.", "tooltip.antarchy.big_bertha.mode.toreterror": "사용하여 앞으로 회전하며 몰레보어 블록을 뚫고 나아갑니다.", "tooltip.antarchy.ultimate_tools_3x3": "Shift + 사용: 3x3 채굴을 켜거나 끕니다.", + "tooltip.antarchy.ultimate_tools_vein": "Shift + 사용: 광맥 채굴을 켜거나 끕니다.", + "tooltip.antarchy.ultimate_tools_3x3_vein": "Shift + 사용: 3x3 채굴과 광맥 채굴을 전환합니다.", + "tooltip.antarchy.battle_axe_tree_felling": "나무 전체를 벱니다. 웅크리면 통나무 하나만 벱니다.", "tooltip.antarchy.moggles.reveal": "야간 시야를 부여하고 근처 동굴을 가리킵니다.", "tooltip.antarchy.moggles.underground": "하늘이 가려진 곳에서만 작동합니다.", "tooltip.antarchy.cornea_ear.vision": "%s초 동안 야간 시야를 부여합니다.", diff --git a/common-1.20.1/src/main/resources/assets/antarchy/lang/pt_br.json b/common-1.20.1/src/main/resources/assets/antarchy/lang/pt_br.json index 62f2f944..9e812e1a 100644 --- a/common-1.20.1/src/main/resources/assets/antarchy/lang/pt_br.json +++ b/common-1.20.1/src/main/resources/assets/antarchy/lang/pt_br.json @@ -517,6 +517,7 @@ "message.antarchy.big_bertha.mode.molevore": "Molevore", "message.antarchy.big_bertha.mode.toreterror": "Toreterror", "message.antarchy.ultimate_tool.mode_3x3": "Alternado para Mineração 3x3", + "message.antarchy.ultimate_tool.mode_vein": "Alternado para Mineração de Veios", "message.antarchy.ultimate_tool.mode_default": "Alternado para Mineração Padrão", "advancements.antarchy.hey_listen.title": "Ei, Escute!", "advancements.antarchy.hey_listen.description": "Uma Reverie se juntou a você.", @@ -549,6 +550,9 @@ "tooltip.antarchy.big_bertha.mode.molevore": "Clique com o botão direito para girar para a frente e perfurar blocos de Molevore.", "tooltip.antarchy.big_bertha.mode.toreterror": "Clique com o botão direito para se lançar ao ar e cair com uma onda de choque em área.", "tooltip.antarchy.ultimate_tools_3x3": "Shift + clique com o botão direito para alternar a Mineração 3x3.", + "tooltip.antarchy.ultimate_tools_vein": "Shift + clique com o botão direito para alternar a Mineração de Veios.", + "tooltip.antarchy.ultimate_tools_3x3_vein": "Shift + clique com o botão direito para alternar entre Mineração 3x3 e de Veios.", + "tooltip.antarchy.battle_axe_tree_felling": "Derruba árvores inteiras. Agache para cortar um único tronco.", "tooltip.antarchy.moggles.reveal": "Concede Visão Noturna e aponta para cavernas próximas.", "tooltip.antarchy.moggles.underground": "Funciona apenas quando você não tem acesso ao céu.", "tooltip.antarchy.cornea_ear.vision": "Concede Visão Noturna por %s segundos.", diff --git a/common-1.20.1/src/main/resources/assets/antarchy/lang/ru_ru.json b/common-1.20.1/src/main/resources/assets/antarchy/lang/ru_ru.json index b6259002..893d17ec 100644 --- a/common-1.20.1/src/main/resources/assets/antarchy/lang/ru_ru.json +++ b/common-1.20.1/src/main/resources/assets/antarchy/lang/ru_ru.json @@ -488,6 +488,7 @@ "message.antarchy.big_bertha.mode.molevore": "Кротожор", "message.antarchy.big_bertha.mode.toreterror": "Тортеррор", "message.antarchy.ultimate_tool.mode_3x3": "Включено копание 3x3", + "message.antarchy.ultimate_tool.mode_vein": "Включена добыча жил", "message.antarchy.ultimate_tool.mode_default": "Включено обычное копание", "advancements.antarchy.hey_listen.title": "Эй, послушай", "advancements.antarchy.hey_listen.description": "К вам присоединилась грёза.", @@ -520,6 +521,9 @@ "tooltip.antarchy.big_bertha.mode.molevore": "ПКМ для рывка вперёд и бурения сквозь блоки кротожора.", "tooltip.antarchy.big_bertha.mode.toreterror": "ПКМ запускает вас в воздух и обрушивает вниз с ударной волной по площади.", "tooltip.antarchy.ultimate_tools_3x3": "Shift + ПКМ для переключения режима копания 3x3.", + "tooltip.antarchy.ultimate_tools_vein": "Shift + ПКМ для переключения добычи жил.", + "tooltip.antarchy.ultimate_tools_3x3_vein": "Shift + ПКМ переключает копание 3x3 и добычу жил.", + "tooltip.antarchy.battle_axe_tree_felling": "Срубает дерево целиком. Присядьте, чтобы срубить одно бревно.", "tooltip.antarchy.moggles.reveal": "Дают ночное зрение и указывают на ближайшие пещеры.", "tooltip.antarchy.moggles.underground": "Работает только если над вами нет открытого неба.", "tooltip.antarchy.cornea_ear.vision": "Даёт Ночное зрение на %s секунд.", diff --git a/common-1.20.1/src/main/resources/assets/antarchy/lang/zh_cn.json b/common-1.20.1/src/main/resources/assets/antarchy/lang/zh_cn.json index 643d8f0e..0cf821af 100644 --- a/common-1.20.1/src/main/resources/assets/antarchy/lang/zh_cn.json +++ b/common-1.20.1/src/main/resources/assets/antarchy/lang/zh_cn.json @@ -517,6 +517,7 @@ "message.antarchy.big_bertha.mode.molevore": "莫尔沃尔", "message.antarchy.big_bertha.mode.toreterror": "托尔泰罗尔", "message.antarchy.ultimate_tool.mode_3x3": "已切换至3×3挖掘", + "message.antarchy.ultimate_tool.mode_vein": "已切换至连锁挖矿", "message.antarchy.ultimate_tool.mode_default": "已切换至默认挖掘", "advancements.antarchy.hey_listen.title": "嘿,听着!", "advancements.antarchy.hey_listen.description": "一只雷弗里加入了你。", @@ -549,6 +550,9 @@ "tooltip.antarchy.big_bertha.mode.molevore": "右键旋转向前并钻穿莫尔沃尔方块。", "tooltip.antarchy.big_bertha.mode.toreterror": "右键跃向空中并以范围冲击波砸落。", "tooltip.antarchy.ultimate_tools_3x3": "潜行键+右键切换3×3挖掘。", + "tooltip.antarchy.ultimate_tools_vein": "潜行键+右键切换连锁挖矿。", + "tooltip.antarchy.ultimate_tools_3x3_vein": "潜行键+右键在3×3挖掘与连锁挖矿之间切换。", + "tooltip.antarchy.battle_axe_tree_felling": "一次砍倒整棵树。潜行时只砍一块原木。", "tooltip.antarchy.moggles.reveal": "获得夜视并指向附近洞穴。", "tooltip.antarchy.moggles.underground": "仅在无法接触天空时生效。", "tooltip.antarchy.cornea_ear.vision": "获得%s秒夜视。", diff --git a/common-1.20.1/src/main/resources/assets/antarchy/lang/zh_tw.json b/common-1.20.1/src/main/resources/assets/antarchy/lang/zh_tw.json index 246f582c..3d88f3f5 100644 --- a/common-1.20.1/src/main/resources/assets/antarchy/lang/zh_tw.json +++ b/common-1.20.1/src/main/resources/assets/antarchy/lang/zh_tw.json @@ -517,6 +517,7 @@ "message.antarchy.big_bertha.mode.molevore": "莫爾沃爾", "message.antarchy.big_bertha.mode.toreterror": "托爾泰羅爾", "message.antarchy.ultimate_tool.mode_3x3": "已切換至3×3挖掘", + "message.antarchy.ultimate_tool.mode_vein": "已切換至連鎖挖礦", "message.antarchy.ultimate_tool.mode_default": "已切換至預設挖掘", "advancements.antarchy.hey_listen.title": "嘿,聽著!", "advancements.antarchy.hey_listen.description": "一隻雷弗里加入了你。", @@ -549,6 +550,9 @@ "tooltip.antarchy.big_bertha.mode.molevore": "右鍵旋轉向前並鑽穿莫爾沃爾方塊。", "tooltip.antarchy.big_bertha.mode.toreterror": "右鍵躍向空中並以範圍衝擊波砸落。", "tooltip.antarchy.ultimate_tools_3x3": "潛行鍵+右鍵切換3×3挖掘。", + "tooltip.antarchy.ultimate_tools_vein": "潛行鍵+右鍵切換連鎖挖礦。", + "tooltip.antarchy.ultimate_tools_3x3_vein": "潛行鍵+右鍵在3×3挖掘與連鎖挖礦之間切換。", + "tooltip.antarchy.battle_axe_tree_felling": "一次砍倒整棵樹。潛行時只砍一塊原木。", "tooltip.antarchy.moggles.reveal": "獲得夜視並指向附近洞穴。", "tooltip.antarchy.moggles.underground": "僅在無法接觸天空時生效。", "tooltip.antarchy.cornea_ear.vision": "獲得%s秒夜視。", diff --git a/common-1.20.1/src/main/resources/data/antarchy/worldgen/biome/cavaryn_moleworm_caves.json b/common-1.20.1/src/main/resources/data/antarchy/worldgen/biome/cavaryn_moleworm_caves.json index 869ded6c..76649eb1 100644 --- a/common-1.20.1/src/main/resources/data/antarchy/worldgen/biome/cavaryn_moleworm_caves.json +++ b/common-1.20.1/src/main/resources/data/antarchy/worldgen/biome/cavaryn_moleworm_caves.json @@ -55,7 +55,8 @@ ], [ "antarchy:moleworm_caves_creepvine", - "antarchy:moleworm_caves_hanging_creeproots" + "antarchy:moleworm_caves_hanging_creeproots", + "antarchy:termite_nest" ], [] ], diff --git a/common-1.20.1/src/main/resources/data/antarchy/worldgen/biome/wormy_caves.json b/common-1.20.1/src/main/resources/data/antarchy/worldgen/biome/wormy_caves.json index 709f910c..a29b0c88 100644 --- a/common-1.20.1/src/main/resources/data/antarchy/worldgen/biome/wormy_caves.json +++ b/common-1.20.1/src/main/resources/data/antarchy/worldgen/biome/wormy_caves.json @@ -52,7 +52,8 @@ "antarchy:wormy_caves_creepvine", "antarchy:wormy_caves_hanging_creeproots", "antarchy:wormy_caves_molting_vines", - "antarchy:wormy_caves_ground_molting_vines" + "antarchy:wormy_caves_ground_molting_vines", + "antarchy:termite_nest" ], [] ], diff --git a/common-1.20.1/src/main/resources/data/antarchy/worldgen/placed_feature/red_ant_nest.json b/common-1.20.1/src/main/resources/data/antarchy/worldgen/placed_feature/red_ant_nest.json index 81555f52..bd766f59 100644 --- a/common-1.20.1/src/main/resources/data/antarchy/worldgen/placed_feature/red_ant_nest.json +++ b/common-1.20.1/src/main/resources/data/antarchy/worldgen/placed_feature/red_ant_nest.json @@ -3,7 +3,7 @@ "placement": [ { "type": "minecraft:rarity_filter", - "chance": 24 + "chance": 8 }, { "type": "minecraft:in_square" diff --git a/common-1.20.1/src/main/resources/data/antarchy/worldgen/placed_feature/red_ant_nest_magma.json b/common-1.20.1/src/main/resources/data/antarchy/worldgen/placed_feature/red_ant_nest_magma.json index 850042eb..f8e3a1d2 100644 --- a/common-1.20.1/src/main/resources/data/antarchy/worldgen/placed_feature/red_ant_nest_magma.json +++ b/common-1.20.1/src/main/resources/data/antarchy/worldgen/placed_feature/red_ant_nest_magma.json @@ -3,7 +3,7 @@ "placement": [ { "type": "minecraft:rarity_filter", - "chance": 24 + "chance": 8 }, { "type": "minecraft:in_square" diff --git a/common-1.20.1/src/main/resources/data/antarchy/worldgen/placed_feature/termite_nest.json b/common-1.20.1/src/main/resources/data/antarchy/worldgen/placed_feature/termite_nest.json index 70a93fad..0b01f7bd 100644 --- a/common-1.20.1/src/main/resources/data/antarchy/worldgen/placed_feature/termite_nest.json +++ b/common-1.20.1/src/main/resources/data/antarchy/worldgen/placed_feature/termite_nest.json @@ -3,14 +3,22 @@ "placement": [ { "type": "minecraft:rarity_filter", - "chance": 88 + "chance": 24 }, { "type": "minecraft:in_square" }, { - "type": "minecraft:heightmap", - "heightmap": "WORLD_SURFACE_WG" + "type": "minecraft:height_range", + "height": { + "type": "minecraft:uniform", + "min_inclusive": { + "absolute": 8 + }, + "max_inclusive": { + "absolute": 190 + } + } }, { "type": "minecraft:biome" diff --git a/common-1.20.1/src/main/resources/data/antarchy/worldgen/placed_feature/termite_nest_cavaryn.json b/common-1.20.1/src/main/resources/data/antarchy/worldgen/placed_feature/termite_nest_cavaryn.json index 933bd153..3136c852 100644 --- a/common-1.20.1/src/main/resources/data/antarchy/worldgen/placed_feature/termite_nest_cavaryn.json +++ b/common-1.20.1/src/main/resources/data/antarchy/worldgen/placed_feature/termite_nest_cavaryn.json @@ -3,14 +3,22 @@ "placement": [ { "type": "minecraft:rarity_filter", - "chance": 44 + "chance": 16 }, { "type": "minecraft:in_square" }, { - "type": "minecraft:heightmap", - "heightmap": "WORLD_SURFACE_WG" + "type": "minecraft:height_range", + "height": { + "type": "minecraft:uniform", + "min_inclusive": { + "absolute": 8 + }, + "max_inclusive": { + "absolute": 190 + } + } }, { "type": "minecraft:biome" 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 9236a00b..28f33995 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 @@ -302,6 +302,7 @@ public final class AntarchySettings { private static double royalBossKnockbackResistance = 1.0D; private static double royalBossStepHeight = 3.0D; private static double royalBossMaxSingleHitDamage = 250.0D; + private static double royalBossHitOverflowRate = 0.25D; private static double royalBossBiteReach = 12.0D; private static double royalBossBiteDamageMultiplier = 1.6D; private static int royalBossBiteCooldownTicks = 45; @@ -2257,6 +2258,9 @@ public final class AntarchySettings { public static double royalBossMaxSingleHitDamage() { return royalBossMaxSingleHitDamage; } public static void setRoyalBossMaxSingleHitDamage(double v) { royalBossMaxSingleHitDamage = v; } + public static double royalBossHitOverflowRate() { return royalBossHitOverflowRate; } + public static void setRoyalBossHitOverflowRate(double v) { royalBossHitOverflowRate = v; } + public static double royalBossBiteReach() { return royalBossBiteReach; } public static void setRoyalBossBiteReach(double v) { royalBossBiteReach = v; } diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/effect/BloodglassWardEffect.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/effect/BloodglassWardEffect.java index cbc03936..4b60ccdc 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/effect/BloodglassWardEffect.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/effect/BloodglassWardEffect.java @@ -21,7 +21,7 @@ public final class BloodglassWardEffect extends MobEffect { public void addAttributeModifiers(AttributeMap attributeMap, int amplifier) { AttributeInstance instance = attributeMap.getInstance(AntarchyObjects.BLOODGLASS_MAX_HEARTS.get()); if (instance == null) return; - instance.addTransientModifier( + instance.addOrUpdateTransientModifier( new AttributeModifier(MODIFIER_ID, amplifier + 1, AttributeModifier.Operation.ADD_VALUE) ); } diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/effect/GrowthMobEffect.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/effect/GrowthMobEffect.java index 3ae4fe5d..fa9a2190 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/effect/GrowthMobEffect.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/effect/GrowthMobEffect.java @@ -23,7 +23,7 @@ public final class GrowthMobEffect extends MobEffect { AttributeInstance instance = attributeMap.getInstance(Attributes.SCALE); if (instance == null) return; double amount = AntarchySettings.growthPotionDelta() * (amplifier + 1); - instance.addTransientModifier( + instance.addOrUpdateTransientModifier( new AttributeModifier(MODIFIER_ID, amount, AttributeModifier.Operation.ADD_MULTIPLIED_TOTAL) ); } diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/effect/RoyalVitalityPenaltyEffect.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/effect/RoyalVitalityPenaltyEffect.java index 9df462f3..64c6e1a5 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/effect/RoyalVitalityPenaltyEffect.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/effect/RoyalVitalityPenaltyEffect.java @@ -21,7 +21,7 @@ public final class RoyalVitalityPenaltyEffect extends MobEffect { public void addAttributeModifiers(AttributeMap attributeMap, int amplifier) { AttributeInstance maxHealth = attributeMap.getInstance(Attributes.MAX_HEALTH); if (maxHealth == null) return; - maxHealth.addTransientModifier(new AttributeModifier( + maxHealth.addOrUpdateTransientModifier(new AttributeModifier( MODIFIER_ID, -0.5D, AttributeModifier.Operation.ADD_MULTIPLIED_TOTAL)); } diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/effect/ShrinkMobEffect.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/effect/ShrinkMobEffect.java index e69d2c27..109aa19c 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/effect/ShrinkMobEffect.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/effect/ShrinkMobEffect.java @@ -23,7 +23,7 @@ public final class ShrinkMobEffect extends MobEffect { AttributeInstance instance = attributeMap.getInstance(Attributes.SCALE); if (instance == null) return; double amount = -(AntarchySettings.shrinkingPotionDelta() * (amplifier + 1)); - instance.addTransientModifier( + instance.addOrUpdateTransientModifier( new AttributeModifier(MODIFIER_ID, amount, AttributeModifier.Operation.ADD_MULTIPLIED_TOTAL) ); } 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 c4f75325..b1089641 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 @@ -2009,7 +2009,8 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult if (source.is(DamageTypes.GENERIC_KILL)) { return super.hurt(source, amount); } - float cappedAmount = (float) Math.min(amount, AntarchySettings.royalBossMaxSingleHitDamage()); + float cap = (float) AntarchySettings.royalBossMaxSingleHitDamage(); + float cappedAmount = amount <= cap ? amount : cap + (amount - cap) * (float) AntarchySettings.royalBossHitOverflowRate(); return super.hurt(source, cappedAmount); } diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/worldgen/ants/TermiteNestFeature.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/worldgen/ants/TermiteNestFeature.java index fb05d06e..e924a55c 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/worldgen/ants/TermiteNestFeature.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/worldgen/ants/TermiteNestFeature.java @@ -34,7 +34,8 @@ public class TermiteNestFeature extends SurfaceAntNestFeature { protected BlockPos resolveNestPos(WorldGenLevel level, BlockPos origin) { int x = origin.getX(); int z = origin.getZ(); - int startY = level.getHeight(Heightmap.Types.WORLD_SURFACE_WG, x, z) - 1; + // Start from the placement height so roofed dimensions like Cavaryn get nests on every cave layer, not just the top one. + int startY = Math.min(origin.getY(), level.getHeight(Heightmap.Types.WORLD_SURFACE_WG, x, z) - 1); int minY = level.getMinBuildHeight() + 1; for (int y = startY; y >= minY; y--) { diff --git a/common-1.21.1/src/main/resources/data/antarchy/loot_table/entities/apple_cow.json b/common-1.21.1/src/main/resources/data/antarchy/loot_table/entities/apple_cow.json index a7437c82..b0d9a41a 100644 --- a/common-1.21.1/src/main/resources/data/antarchy/loot_table/entities/apple_cow.json +++ b/common-1.21.1/src/main/resources/data/antarchy/loot_table/entities/apple_cow.json @@ -48,13 +48,35 @@ "function": "minecraft:furnace_smelt", "conditions": [ { - "condition": "minecraft:entity_properties", - "entity": "this", - "predicate": { - "flags": { - "is_on_fire": true + "condition": "minecraft:any_of", + "terms": [ + { + "condition": "minecraft:entity_properties", + "entity": "this", + "predicate": { + "flags": { + "is_on_fire": true + } + } + }, + { + "condition": "minecraft:entity_properties", + "entity": "direct_attacker", + "predicate": { + "equipment": { + "mainhand": { + "predicates": { + "minecraft:enchantments": [ + { + "enchantments": "#minecraft:smelts_loot" + } + ] + } + } + } + } } - } + ] } ] }, diff --git a/common-1.21.1/src/main/resources/data/antarchy/loot_table/entities/enchanted_golden_apple_cow.json b/common-1.21.1/src/main/resources/data/antarchy/loot_table/entities/enchanted_golden_apple_cow.json index a7437c82..b0d9a41a 100644 --- a/common-1.21.1/src/main/resources/data/antarchy/loot_table/entities/enchanted_golden_apple_cow.json +++ b/common-1.21.1/src/main/resources/data/antarchy/loot_table/entities/enchanted_golden_apple_cow.json @@ -48,13 +48,35 @@ "function": "minecraft:furnace_smelt", "conditions": [ { - "condition": "minecraft:entity_properties", - "entity": "this", - "predicate": { - "flags": { - "is_on_fire": true + "condition": "minecraft:any_of", + "terms": [ + { + "condition": "minecraft:entity_properties", + "entity": "this", + "predicate": { + "flags": { + "is_on_fire": true + } + } + }, + { + "condition": "minecraft:entity_properties", + "entity": "direct_attacker", + "predicate": { + "equipment": { + "mainhand": { + "predicates": { + "minecraft:enchantments": [ + { + "enchantments": "#minecraft:smelts_loot" + } + ] + } + } + } + } } - } + ] } ] }, diff --git a/common-1.21.1/src/main/resources/data/antarchy/loot_table/entities/golden_apple_cow.json b/common-1.21.1/src/main/resources/data/antarchy/loot_table/entities/golden_apple_cow.json index a7437c82..b0d9a41a 100644 --- a/common-1.21.1/src/main/resources/data/antarchy/loot_table/entities/golden_apple_cow.json +++ b/common-1.21.1/src/main/resources/data/antarchy/loot_table/entities/golden_apple_cow.json @@ -48,13 +48,35 @@ "function": "minecraft:furnace_smelt", "conditions": [ { - "condition": "minecraft:entity_properties", - "entity": "this", - "predicate": { - "flags": { - "is_on_fire": true + "condition": "minecraft:any_of", + "terms": [ + { + "condition": "minecraft:entity_properties", + "entity": "this", + "predicate": { + "flags": { + "is_on_fire": true + } + } + }, + { + "condition": "minecraft:entity_properties", + "entity": "direct_attacker", + "predicate": { + "equipment": { + "mainhand": { + "predicates": { + "minecraft:enchantments": [ + { + "enchantments": "#minecraft:smelts_loot" + } + ] + } + } + } + } } - } + ] } ] }, diff --git a/common-1.21.1/src/main/resources/data/antarchy/loot_table/entities/ouranwood_deer.json b/common-1.21.1/src/main/resources/data/antarchy/loot_table/entities/ouranwood_deer.json index 5bd5aaec..27dda19c 100644 --- a/common-1.21.1/src/main/resources/data/antarchy/loot_table/entities/ouranwood_deer.json +++ b/common-1.21.1/src/main/resources/data/antarchy/loot_table/entities/ouranwood_deer.json @@ -48,13 +48,35 @@ "function": "minecraft:furnace_smelt", "conditions": [ { - "condition": "minecraft:entity_properties", - "entity": "this", - "predicate": { - "flags": { - "is_on_fire": true + "condition": "minecraft:any_of", + "terms": [ + { + "condition": "minecraft:entity_properties", + "entity": "this", + "predicate": { + "flags": { + "is_on_fire": true + } + } + }, + { + "condition": "minecraft:entity_properties", + "entity": "direct_attacker", + "predicate": { + "equipment": { + "mainhand": { + "predicates": { + "minecraft:enchantments": [ + { + "enchantments": "#minecraft:smelts_loot" + } + ] + } + } + } + } } - } + ] } ] }, diff --git a/common-1.21.1/src/main/resources/data/antarchy/worldgen/biome/cavaryn_moleworm_caves.json b/common-1.21.1/src/main/resources/data/antarchy/worldgen/biome/cavaryn_moleworm_caves.json index 869ded6c..76649eb1 100644 --- a/common-1.21.1/src/main/resources/data/antarchy/worldgen/biome/cavaryn_moleworm_caves.json +++ b/common-1.21.1/src/main/resources/data/antarchy/worldgen/biome/cavaryn_moleworm_caves.json @@ -55,7 +55,8 @@ ], [ "antarchy:moleworm_caves_creepvine", - "antarchy:moleworm_caves_hanging_creeproots" + "antarchy:moleworm_caves_hanging_creeproots", + "antarchy:termite_nest" ], [] ], diff --git a/common-1.21.1/src/main/resources/data/antarchy/worldgen/biome/wormy_caves.json b/common-1.21.1/src/main/resources/data/antarchy/worldgen/biome/wormy_caves.json index 709f910c..a29b0c88 100644 --- a/common-1.21.1/src/main/resources/data/antarchy/worldgen/biome/wormy_caves.json +++ b/common-1.21.1/src/main/resources/data/antarchy/worldgen/biome/wormy_caves.json @@ -52,7 +52,8 @@ "antarchy:wormy_caves_creepvine", "antarchy:wormy_caves_hanging_creeproots", "antarchy:wormy_caves_molting_vines", - "antarchy:wormy_caves_ground_molting_vines" + "antarchy:wormy_caves_ground_molting_vines", + "antarchy:termite_nest" ], [] ], diff --git a/common-1.21.1/src/main/resources/data/antarchy/worldgen/placed_feature/red_ant_nest.json b/common-1.21.1/src/main/resources/data/antarchy/worldgen/placed_feature/red_ant_nest.json index 81555f52..bd766f59 100644 --- a/common-1.21.1/src/main/resources/data/antarchy/worldgen/placed_feature/red_ant_nest.json +++ b/common-1.21.1/src/main/resources/data/antarchy/worldgen/placed_feature/red_ant_nest.json @@ -3,7 +3,7 @@ "placement": [ { "type": "minecraft:rarity_filter", - "chance": 24 + "chance": 8 }, { "type": "minecraft:in_square" diff --git a/common-1.21.1/src/main/resources/data/antarchy/worldgen/placed_feature/red_ant_nest_magma.json b/common-1.21.1/src/main/resources/data/antarchy/worldgen/placed_feature/red_ant_nest_magma.json index 850042eb..f8e3a1d2 100644 --- a/common-1.21.1/src/main/resources/data/antarchy/worldgen/placed_feature/red_ant_nest_magma.json +++ b/common-1.21.1/src/main/resources/data/antarchy/worldgen/placed_feature/red_ant_nest_magma.json @@ -3,7 +3,7 @@ "placement": [ { "type": "minecraft:rarity_filter", - "chance": 24 + "chance": 8 }, { "type": "minecraft:in_square" diff --git a/common-1.21.1/src/main/resources/data/antarchy/worldgen/placed_feature/termite_nest.json b/common-1.21.1/src/main/resources/data/antarchy/worldgen/placed_feature/termite_nest.json index 70a93fad..0b01f7bd 100644 --- a/common-1.21.1/src/main/resources/data/antarchy/worldgen/placed_feature/termite_nest.json +++ b/common-1.21.1/src/main/resources/data/antarchy/worldgen/placed_feature/termite_nest.json @@ -3,14 +3,22 @@ "placement": [ { "type": "minecraft:rarity_filter", - "chance": 88 + "chance": 24 }, { "type": "minecraft:in_square" }, { - "type": "minecraft:heightmap", - "heightmap": "WORLD_SURFACE_WG" + "type": "minecraft:height_range", + "height": { + "type": "minecraft:uniform", + "min_inclusive": { + "absolute": 8 + }, + "max_inclusive": { + "absolute": 190 + } + } }, { "type": "minecraft:biome" diff --git a/common-1.21.1/src/main/resources/data/antarchy/worldgen/placed_feature/termite_nest_cavaryn.json b/common-1.21.1/src/main/resources/data/antarchy/worldgen/placed_feature/termite_nest_cavaryn.json index 933bd153..3136c852 100644 --- a/common-1.21.1/src/main/resources/data/antarchy/worldgen/placed_feature/termite_nest_cavaryn.json +++ b/common-1.21.1/src/main/resources/data/antarchy/worldgen/placed_feature/termite_nest_cavaryn.json @@ -3,14 +3,22 @@ "placement": [ { "type": "minecraft:rarity_filter", - "chance": 44 + "chance": 16 }, { "type": "minecraft:in_square" }, { - "type": "minecraft:heightmap", - "heightmap": "WORLD_SURFACE_WG" + "type": "minecraft:height_range", + "height": { + "type": "minecraft:uniform", + "min_inclusive": { + "absolute": 8 + }, + "max_inclusive": { + "absolute": 190 + } + } }, { "type": "minecraft:biome" diff --git a/forge-1.20.1/gradle.properties b/forge-1.20.1/gradle.properties index 0324db1f..10d39a65 100644 --- a/forge-1.20.1/gradle.properties +++ b/forge-1.20.1/gradle.properties @@ -6,7 +6,7 @@ mod_name=Antarchy mod_description=ANTS! ANTS! ANTS! ANTS! mod_authors=CraisinLord mod_group_id=com.craisinlord -mod_version=2.2.7 +mod_version=2.2.9 mod_license=All Rights Reserved minecraft_version=1.20.1 diff --git a/forge-1.20.1/src/main/java/com/craisinlord/antarchy/forge/AntarchyConfigModuleForge.java b/forge-1.20.1/src/main/java/com/craisinlord/antarchy/forge/AntarchyConfigModuleForge.java index 39da2532..71473ff6 100644 --- a/forge-1.20.1/src/main/java/com/craisinlord/antarchy/forge/AntarchyConfigModuleForge.java +++ b/forge-1.20.1/src/main/java/com/craisinlord/antarchy/forge/AntarchyConfigModuleForge.java @@ -154,6 +154,7 @@ public final class AntarchyConfigModuleForge { AntarchySettings.setRoyalBossKnockbackResistance(AntarchyMobsConfig.royalBossKnockbackResistance()); AntarchySettings.setRoyalBossStepHeight(AntarchyMobsConfig.royalBossStepHeight()); AntarchySettings.setRoyalBossMaxSingleHitDamage(AntarchyMobsConfig.royalBossMaxSingleHitDamage()); + AntarchySettings.setRoyalBossHitOverflowRate(AntarchyMobsConfig.royalBossHitOverflowRate()); AntarchySettings.setRoyalBossBiteReach(AntarchyMobsConfig.royalBossBiteReach()); AntarchySettings.setRoyalBossBiteDamageMultiplier(AntarchyMobsConfig.royalBossBiteDamageMultiplier()); AntarchySettings.setRoyalBossBiteCooldownTicks(AntarchyMobsConfig.royalBossBiteCooldownTicks()); @@ -439,6 +440,9 @@ public final class AntarchyConfigModuleForge { AntarchySettings.setUltimateHoeAttackSpeed(AntarchyToolsConfig.ultimateHoeAttackSpeed()); AntarchySettings.setUltimateToolEnchantability(AntarchyToolsConfig.ultimateToolEnchantability()); AntarchySettings.setUltimateToolsThreeByThreeEnabled(AntarchyToolsConfig.ultimateToolsThreeByThreeEnabled()); + AntarchySettings.setUltimateToolsVeinMiningEnabled(AntarchyToolsConfig.ultimateToolsVeinMiningEnabled()); + AntarchySettings.setUltimateToolsVeinMiningMaxBlocks(AntarchyToolsConfig.ultimateToolsVeinMiningMaxBlocks()); + AntarchySettings.setUltimateToolsTreeFellingMaxLogs(AntarchyToolsConfig.ultimateToolsTreeFellingMaxLogs()); AntarchySettings.setUltimateBowAttackDamage(AntarchyToolsConfig.ultimateBowAttackDamage()); AntarchySettings.setUltimateBowPlayerHeal(AntarchyToolsConfig.ultimateBowPlayerHeal()); diff --git a/forge-1.20.1/src/main/java/com/craisinlord/antarchy/forge/AntarchyMobsConfig.java b/forge-1.20.1/src/main/java/com/craisinlord/antarchy/forge/AntarchyMobsConfig.java index b993f2e9..c5cf78fd 100644 --- a/forge-1.20.1/src/main/java/com/craisinlord/antarchy/forge/AntarchyMobsConfig.java +++ b/forge-1.20.1/src/main/java/com/craisinlord/antarchy/forge/AntarchyMobsConfig.java @@ -104,6 +104,7 @@ public final class AntarchyMobsConfig { private static final ForgeConfigSpec.DoubleValue ROYAL_BOSS_KNOCKBACK_RESISTANCE; private static final ForgeConfigSpec.DoubleValue ROYAL_BOSS_STEP_HEIGHT; private static final ForgeConfigSpec.DoubleValue ROYAL_BOSS_MAX_SINGLE_HIT_DAMAGE; + private static final ForgeConfigSpec.DoubleValue ROYAL_BOSS_HIT_OVERFLOW_RATE; private static final ForgeConfigSpec.DoubleValue ROYAL_BOSS_BITE_REACH; private static final ForgeConfigSpec.DoubleValue ROYAL_BOSS_BITE_DAMAGE_MULTIPLIER; private static final ForgeConfigSpec.IntValue ROYAL_BOSS_BITE_COOLDOWN_TICKS; @@ -531,6 +532,7 @@ public final class AntarchyMobsConfig { ROYAL_BOSS_KNOCKBACK_RESISTANCE = b.comment("Base knockback resistance shared by King and Queen.").defineInRange("knockbackResistance", 1.0D, 0.0D, 1.0D); ROYAL_BOSS_STEP_HEIGHT = b.comment("Base step height shared by King and Queen.").defineInRange("stepHeight", 3.0D, 0.0D, 32.0D); ROYAL_BOSS_MAX_SINGLE_HIT_DAMAGE = b.comment("Maximum damage a single hit can deal to a Royal boss before later anti-burst systems are added.").defineInRange("maxSingleHitDamage", 250.0D, 1.0D, 32768.0D); + ROYAL_BOSS_HIT_OVERFLOW_RATE = b.comment("Share of damage above maxSingleHitDamage that still lands. 0 makes the cap a hard limit.").defineInRange("hitOverflowRate", 0.25D, 0.0D, 1.0D); ROYAL_BOSS_BITE_REACH = b.comment("Radius around each head anchor that a King or Queen bite can hit.").defineInRange("biteReach", 12.0D, 1.0D, 64.0D); ROYAL_BOSS_BITE_DAMAGE_MULTIPLIER = b.comment("Multiplier applied to base attack damage for King and Queen head bites.").defineInRange("biteDamageMultiplier", 1.6D, 0.0D, 32.0D); ROYAL_BOSS_BITE_COOLDOWN_TICKS = b.comment("Base cooldown in ticks between bites for a single King or Queen head (scaled down at later phases).").defineInRange("biteCooldownTicks", 45, 1, 1200); @@ -1046,6 +1048,7 @@ public final class AntarchyMobsConfig { static double royalBossKnockbackResistance() { return ROYAL_BOSS_KNOCKBACK_RESISTANCE.get(); } static double royalBossStepHeight() { return ROYAL_BOSS_STEP_HEIGHT.get(); } static double royalBossMaxSingleHitDamage() { return ROYAL_BOSS_MAX_SINGLE_HIT_DAMAGE.get(); } + static double royalBossHitOverflowRate() { return ROYAL_BOSS_HIT_OVERFLOW_RATE.get(); } static double royalBossBiteReach() { return ROYAL_BOSS_BITE_REACH.get(); } static double royalBossBiteDamageMultiplier() { return ROYAL_BOSS_BITE_DAMAGE_MULTIPLIER.get(); } static int royalBossBiteCooldownTicks() { return ROYAL_BOSS_BITE_COOLDOWN_TICKS.get(); } diff --git a/forge-1.20.1/src/main/java/com/craisinlord/antarchy/forge/AntarchyToolsConfig.java b/forge-1.20.1/src/main/java/com/craisinlord/antarchy/forge/AntarchyToolsConfig.java index 20f29b03..8ca5c132 100644 --- a/forge-1.20.1/src/main/java/com/craisinlord/antarchy/forge/AntarchyToolsConfig.java +++ b/forge-1.20.1/src/main/java/com/craisinlord/antarchy/forge/AntarchyToolsConfig.java @@ -22,6 +22,9 @@ public final class AntarchyToolsConfig { private static final ForgeConfigSpec.DoubleValue ULTIMATE_HOE_ATTACK_SPEED; private static final ForgeConfigSpec.IntValue ULTIMATE_TOOL_ENCHANTABILITY; private static final ForgeConfigSpec.BooleanValue ULTIMATE_TOOLS_THREE_BY_THREE_ENABLED; + private static final ForgeConfigSpec.BooleanValue ULTIMATE_TOOLS_VEIN_MINING_ENABLED; + private static final ForgeConfigSpec.IntValue ULTIMATE_TOOLS_VEIN_MINING_MAX_BLOCKS; + private static final ForgeConfigSpec.IntValue ULTIMATE_TOOLS_TREE_FELLING_MAX_LOGS; private static final ForgeConfigSpec.DoubleValue ULTIMATE_BOW_ATTACK_DAMAGE; private static final ForgeConfigSpec.DoubleValue ULTIMATE_BOW_PLAYER_HEAL; @@ -248,6 +251,11 @@ public final class AntarchyToolsConfig { ULTIMATE_TOOLS_THREE_BY_THREE_ENABLED = b .comment("If false, Ultimate pickaxe, axe, shovel, and hoe lose their 3x3 mining mode and tooltip.") .define("threeByThreeEnabled", true); + ULTIMATE_TOOLS_VEIN_MINING_ENABLED = b + .comment("If false, the Ultimate Pickaxe and Axe lose Vein Mining and the Battle Axe stops felling trees.") + .define("veinMiningEnabled", true); + ULTIMATE_TOOLS_VEIN_MINING_MAX_BLOCKS = b.comment("Most extra blocks the Ultimate Pickaxe or Axe breaks in one Vein Mining swing. Logs use treeFellingMaxLogs instead.").defineInRange("veinMiningMaxBlocks", 64, 1, 1024); + ULTIMATE_TOOLS_TREE_FELLING_MAX_LOGS = b.comment("Most extra logs broken at once by Vein Mining on logs or by the Battle Axe.") .defineInRange("treeFellingMaxLogs", 128, 1, 1024); b.pop(); b.push("ultimateBow"); @@ -574,6 +582,9 @@ public final class AntarchyToolsConfig { static double ultimateHoeAttackSpeed() { return ULTIMATE_HOE_ATTACK_SPEED.get(); } static int ultimateToolEnchantability() { return ULTIMATE_TOOL_ENCHANTABILITY.get(); } static boolean ultimateToolsThreeByThreeEnabled() { return ULTIMATE_TOOLS_THREE_BY_THREE_ENABLED.get(); } + static boolean ultimateToolsVeinMiningEnabled() { return ULTIMATE_TOOLS_VEIN_MINING_ENABLED.get(); } + static int ultimateToolsVeinMiningMaxBlocks() { return ULTIMATE_TOOLS_VEIN_MINING_MAX_BLOCKS.get(); } + static int ultimateToolsTreeFellingMaxLogs() { return ULTIMATE_TOOLS_TREE_FELLING_MAX_LOGS.get(); } static double ultimateBowAttackDamage() { return ULTIMATE_BOW_ATTACK_DAMAGE.get(); } static double ultimateBowPlayerHeal() { return ULTIMATE_BOW_PLAYER_HEAL.get(); } diff --git a/forge-1.20.1/src/main/java/com/craisinlord/antarchy/forge/client/AntarchyKeyBindings.java b/forge-1.20.1/src/main/java/com/craisinlord/antarchy/forge/client/AntarchyKeyBindings.java index 89dbb95f..935a8509 100644 --- a/forge-1.20.1/src/main/java/com/craisinlord/antarchy/forge/client/AntarchyKeyBindings.java +++ b/forge-1.20.1/src/main/java/com/craisinlord/antarchy/forge/client/AntarchyKeyBindings.java @@ -36,6 +36,21 @@ public final class AntarchyKeyBindings { InputConstants.KEY_R, CATEGORY ); + public static final KeyMapping MOUNT_BEAM = new KeyMapping( + "key.antarchy.mount_beam", + KeyConflictContext.IN_GAME, + InputConstants.Type.KEYSYM, + InputConstants.KEY_H, + CATEGORY + ); + + public static final KeyMapping MOUNT_SIGNATURE = new KeyMapping( + "key.antarchy.mount_signature", + KeyConflictContext.IN_GAME, + InputConstants.Type.KEYSYM, + InputConstants.KEY_J, + CATEGORY + ); public static final KeyMapping TIGERS_EYE_CAMOUFLAGE = new KeyMapping( "key.antarchy.tigers_eye_camouflage", KeyConflictContext.IN_GAME, @@ -80,6 +95,8 @@ public final class AntarchyKeyBindings { event.register(BRUTALFLY_FLAP); event.register(MOUNT_SPECIAL); event.register(MOUNT_FLIGHT_TOGGLE); + event.register(MOUNT_BEAM); + event.register(MOUNT_SIGNATURE); event.register(TIGERS_EYE_CAMOUFLAGE); event.register(ROYAL_INVERSION_TOGGLE); event.register(ROYAL_BOUNDARY); @@ -91,6 +108,14 @@ public final class AntarchyKeyBindings { return MOUNT_FLIGHT_TOGGLE.isDown(); } + public static boolean isMountBeamPressed() { + return MOUNT_BEAM.isDown(); + } + + public static boolean isMountSignaturePressed() { + return MOUNT_SIGNATURE.isDown(); + } + public static boolean isMountSpecialPressed() { return MOUNT_SPECIAL.isDown(); } diff --git a/forge-1.20.1/src/main/java/com/craisinlord/antarchy/forge/client/RoyalMountClientHandler.java b/forge-1.20.1/src/main/java/com/craisinlord/antarchy/forge/client/RoyalMountClientHandler.java index 97ccdd99..3b720635 100644 --- a/forge-1.20.1/src/main/java/com/craisinlord/antarchy/forge/client/RoyalMountClientHandler.java +++ b/forge-1.20.1/src/main/java/com/craisinlord/antarchy/forge/client/RoyalMountClientHandler.java @@ -18,6 +18,8 @@ public final class RoyalMountClientHandler { private static boolean wasAttack; private static boolean wasSpecial; private static boolean wasFlightToggle; + private static boolean wasBeam; + private static boolean wasSignature; private static boolean wasAscend; private static boolean wasDescend; private static boolean wasUse; @@ -65,6 +67,18 @@ public final class RoyalMountClientHandler { } wasFlightToggle = flightToggle; + boolean beam = AntarchyKeyBindings.isMountBeamPressed(); + if (beam && !wasBeam) { + AntarchyForgeNetworkCore.sendToServer(new RoyalMountActionPayload(RoyalMountActionPayload.BEAM)); + } + wasBeam = beam; + + boolean signature = AntarchyKeyBindings.isMountSignaturePressed(); + if (signature && !wasSignature) { + AntarchyForgeNetworkCore.sendToServer(new RoyalMountActionPayload(RoyalMountActionPayload.SIGNATURE)); + } + wasSignature = signature; + boolean ascend = mc.options.keyJump.isDown(); boolean descend = mc.options.keyShift.isDown(); if (ascend != wasAscend || descend != wasDescend) { @@ -81,6 +95,8 @@ public final class RoyalMountClientHandler { wasAttack = false; wasSpecial = false; wasFlightToggle = false; + wasBeam = false; + wasSignature = false; wasAscend = false; wasDescend = false; } diff --git a/forge-1.20.1/src/main/java/com/craisinlord/antarchy/forge/registry/AntarchyForgePayloadHandlers.java b/forge-1.20.1/src/main/java/com/craisinlord/antarchy/forge/registry/AntarchyForgePayloadHandlers.java index 2b8fba24..69e56001 100644 --- a/forge-1.20.1/src/main/java/com/craisinlord/antarchy/forge/registry/AntarchyForgePayloadHandlers.java +++ b/forge-1.20.1/src/main/java/com/craisinlord/antarchy/forge/registry/AntarchyForgePayloadHandlers.java @@ -102,6 +102,8 @@ public class AntarchyForgePayloadHandlers { case com.craisinlord.antarchy.content.network.RoyalMountActionPayload.FLIGHT_TOGGLE -> mount.toggleMountedFlight(player); case com.craisinlord.antarchy.content.network.RoyalMountActionPayload.BITE -> mount.handleMountedBite(player); case com.craisinlord.antarchy.content.network.RoyalMountActionPayload.SPIT -> mount.handleMountedSpit(player); + case com.craisinlord.antarchy.content.network.RoyalMountActionPayload.BEAM -> mount.handleMountedBeam(player); + case com.craisinlord.antarchy.content.network.RoyalMountActionPayload.SIGNATURE -> mount.handleMountedSignature(player); default -> { } } diff --git a/gradle.properties b/gradle.properties index b2227b4e..276b3cc7 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,7 +6,7 @@ org.gradle.configureondemand=true minecraft_version_1_21_1=1.21.1 modid=antarchy mod_id=antarchy -mod_version=2.2.7 +mod_version=2.2.9 maven_group=com.craisinlord mixin_version=0.15.4+mixin.0.8.7 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 c6227e01..24159b8b 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 @@ -154,6 +154,7 @@ public final class AntarchyConfigModuleNeoforge { AntarchySettings.setRoyalBossKnockbackResistance(AntarchyMobsConfig.royalBossKnockbackResistance()); AntarchySettings.setRoyalBossStepHeight(AntarchyMobsConfig.royalBossStepHeight()); AntarchySettings.setRoyalBossMaxSingleHitDamage(AntarchyMobsConfig.royalBossMaxSingleHitDamage()); + AntarchySettings.setRoyalBossHitOverflowRate(AntarchyMobsConfig.royalBossHitOverflowRate()); AntarchySettings.setRoyalBossBiteReach(AntarchyMobsConfig.royalBossBiteReach()); AntarchySettings.setRoyalBossBiteDamageMultiplier(AntarchyMobsConfig.royalBossBiteDamageMultiplier()); AntarchySettings.setRoyalBossBiteCooldownTicks(AntarchyMobsConfig.royalBossBiteCooldownTicks()); 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 55c654d7..fff49178 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 @@ -128,6 +128,7 @@ public final class AntarchyMobsConfig { private static final ModConfigSpec.DoubleValue ROYAL_BOSS_KNOCKBACK_RESISTANCE; private static final ModConfigSpec.DoubleValue ROYAL_BOSS_STEP_HEIGHT; private static final ModConfigSpec.DoubleValue ROYAL_BOSS_MAX_SINGLE_HIT_DAMAGE; + private static final ModConfigSpec.DoubleValue ROYAL_BOSS_HIT_OVERFLOW_RATE; private static final ModConfigSpec.DoubleValue ROYAL_BOSS_BITE_REACH; private static final ModConfigSpec.DoubleValue ROYAL_BOSS_BITE_DAMAGE_MULTIPLIER; private static final ModConfigSpec.IntValue ROYAL_BOSS_BITE_COOLDOWN_TICKS; @@ -642,6 +643,7 @@ public final class AntarchyMobsConfig { ROYAL_BOSS_KNOCKBACK_RESISTANCE = b.comment("Base knockback resistance shared by King and Queen.").defineInRange("knockbackResistance", 1.0D, 0.0D, 1.0D); ROYAL_BOSS_STEP_HEIGHT = b.comment("Base step height shared by King and Queen.").defineInRange("stepHeight", 3.0D, 0.0D, 32.0D); ROYAL_BOSS_MAX_SINGLE_HIT_DAMAGE = b.comment("Maximum damage a single hit can deal to a Royal boss before later anti-burst systems are added.").defineInRange("maxSingleHitDamage", 250.0D, 1.0D, 32768.0D); + ROYAL_BOSS_HIT_OVERFLOW_RATE = b.comment("Share of damage above maxSingleHitDamage that still lands. 0 makes the cap a hard limit.").defineInRange("hitOverflowRate", 0.25D, 0.0D, 1.0D); ROYAL_BOSS_BITE_REACH = b.comment("Radius around each head anchor that a King or Queen bite can hit.").defineInRange("biteReach", 12.0D, 1.0D, 64.0D); ROYAL_BOSS_BITE_DAMAGE_MULTIPLIER = b.comment("Multiplier applied to base attack damage for King and Queen head bites.").defineInRange("biteDamageMultiplier", 1.6D, 0.0D, 32.0D); ROYAL_BOSS_BITE_COOLDOWN_TICKS = b.comment("Base cooldown in ticks between bites for a single King or Queen head (scaled down at later phases).").defineInRange("biteCooldownTicks", 45, 1, 1200); @@ -1195,6 +1197,7 @@ public final class AntarchyMobsConfig { static double royalBossKnockbackResistance() { return ROYAL_BOSS_KNOCKBACK_RESISTANCE.get(); } static double royalBossStepHeight() { return ROYAL_BOSS_STEP_HEIGHT.get(); } static double royalBossMaxSingleHitDamage() { return ROYAL_BOSS_MAX_SINGLE_HIT_DAMAGE.get(); } + static double royalBossHitOverflowRate() { return ROYAL_BOSS_HIT_OVERFLOW_RATE.get(); } static double royalBossBiteReach() { return ROYAL_BOSS_BITE_REACH.get(); } static double royalBossBiteDamageMultiplier() { return ROYAL_BOSS_BITE_DAMAGE_MULTIPLIER.get(); } static int royalBossBiteCooldownTicks() { return ROYAL_BOSS_BITE_COOLDOWN_TICKS.get(); } diff --git a/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/mixins/infinity/ShaderPayloadRepeatSkipMixin.java b/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/mixins/infinity/ShaderPayloadRepeatSkipMixin.java new file mode 100644 index 00000000..063a744c --- /dev/null +++ b/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/mixins/infinity/ShaderPayloadRepeatSkipMixin.java @@ -0,0 +1,32 @@ +package com.craisinlord.antarchy.neoforge.mixins.infinity; + +import net.codexarchonic.infinity.registry.payload.s2c.ShaderS2CPayload; +import net.codexarchonic.infinity.util.loading.ShaderLoader; +import net.minecraft.client.Minecraft; +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.nbt.CompoundTag; +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.Redirect; + +import java.lang.ref.WeakReference; + +@Mixin(value = ShaderS2CPayload.class, remap = false) +public class ShaderPayloadRepeatSkipMixin { + @Unique + private static WeakReference antarchy$lastEmptyLevel = new WeakReference<>(null); + + @Redirect(method = "lambda$receive$0", at = @At(value = "INVOKE", + target = "Lnet/codexarchonic/infinity/util/loading/ShaderLoader;reloadShaders(Lnet/minecraft/client/Minecraft;Lnet/minecraft/nbt/CompoundTag;Z)V"), + remap = false) + private void antarchy$skipRepeatedEmptyReload(Minecraft minecraft, CompoundTag shader, boolean iridescence) { + boolean empty = shader.isEmpty() && !iridescence; + ClientLevel level = minecraft.level; + if (empty && level != null && antarchy$lastEmptyLevel.get() == level) { + return; + } + antarchy$lastEmptyLevel = new WeakReference<>(empty ? level : null); + ShaderLoader.reloadShaders(minecraft, shader, iridescence); + } +} diff --git a/neoforge-1.21.1/src/main/resources/antarchy-neoforge.mixins.json b/neoforge-1.21.1/src/main/resources/antarchy-neoforge.mixins.json index 12f078df..f2c7a752 100644 --- a/neoforge-1.21.1/src/main/resources/antarchy-neoforge.mixins.json +++ b/neoforge-1.21.1/src/main/resources/antarchy-neoforge.mixins.json @@ -15,6 +15,7 @@ "infinity.WarpLogicMixin" ], "client": [ + "infinity.ShaderPayloadRepeatSkipMixin" ], "injectors": { "defaultRequire": 1