This commit is contained in:
craisinlord committed 2026-10-20 10:23:25 -05:00
1 parent 758fa58b1b
commit c00d57e4e0
63 files changed
+7541 -2626

No files matched your search

@@ -15,6 +15,7 @@ import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
public final class InfinityConfigRefresh {
private static final Map<String, Registry<?>> CHECKED_FILES = Map.of(
@@ -22,6 +23,7 @@ public final class InfinityConfigRefresh {
"items.json", BuiltInRegistries.ITEM,
"effects.json", BuiltInRegistries.MOB_EFFECT
);
private static final Set<String> DISABLED_IDS = Set.of("antarchy:paralyzed");
private InfinityConfigRefresh() {
}
@@ -43,7 +45,7 @@ public final class InfinityConfigRefresh {
try {
Files.delete(invocationLock);
Antarchy.LOGGER.info(
"[Antarchy] Infinite Dimensions config lists unregistered Antarchy content {}; its config will regenerate on next world join",
"[Antarchy] Infinite Dimensions config lists unregistered or disabled Antarchy content {}; its config will regenerate on next world join",
staleIds
);
} catch (Exception exception) {
@@ -73,6 +75,8 @@ public final class InfinityConfigRefresh {
ResourceLocation id = ResourceLocation.tryParse(key);
if (id != null && Antarchy.MODID.equals(id.getNamespace()) && !registry.containsKey(id)) {
staleIds.add(key);
} else if (DISABLED_IDS.contains(key) && (!entry.has("weight") || entry.get("weight").getAsDouble() > 0.0D)) {
staleIds.add(key);
}
}
} catch (Exception exception) {
@@ -11,6 +11,7 @@ import net.minecraft.util.Mth;
import net.minecraft.util.RandomSource;
import java.util.Comparator;
import java.util.function.Supplier;
public final class QueenMusicHandler {
private static final double START_RADIUS = 256.0D;
@@ -25,7 +26,7 @@ public final class QueenMusicHandler {
return music != null;
}
public static void tick(Minecraft minecraft, SoundEvent soundEvent) {
public static void tick(Minecraft minecraft, Supplier<SoundEvent> soundEventSupplier) {
if (minecraft.level == null || minecraft.player == null || minecraft.isPaused()
|| !minecraft.player.isAlive() || minecraft.player.isRemoved()
|| minecraft.player.isDeadOrDying() || minecraft.screen instanceof DeathScreen) {
@@ -51,6 +52,15 @@ public final class QueenMusicHandler {
}
if (music == null) {
SoundEvent soundEvent;
try {
soundEvent = soundEventSupplier.get();
} catch (RuntimeException exception) {
return;
}
if (soundEvent == null) {
return;
}
minecraft.getMusicManager().stopPlaying();
music = new QueenMusicSound(soundEvent);
minecraft.getSoundManager().play(music);
@@ -759,10 +759,6 @@ public class HerculesBeetleEntity extends TamableAnimal implements GeoEntity, Fl
}
}
private boolean isMountedFlightPoseActive() {
return this.isFlying() && (!this.onGround() || this.toLocal(this.getDeltaMovement()).y > 0.08D);
}
private void startMountedFlight() {
this.pendingChargeStart = false;
this.setFlying(true);
@@ -1169,6 +1165,19 @@ public class HerculesBeetleEntity extends TamableAnimal implements GeoEntity, Fl
&& this.getControllingPassenger() == player;
}
@Override
public boolean isControlledByLocalInstance() {
if (this.level().isClientSide && this.isChargeAnimationActive()) {
return false;
}
return super.isControlledByLocalInstance();
}
private boolean isChargeAnimationActive() {
int state = this.getAnimationState();
return state == ANIM_CHARGE_START || state == ANIM_CHARGE;
}
@Override
@Nullable
public LivingEntity getControllingPassenger() {
@@ -1184,7 +1193,7 @@ public class HerculesBeetleEntity extends TamableAnimal implements GeoEntity, Fl
if (this.hasPassenger(passenger)) {
Vec3 localForward = this.toLocalPlane(this.getLookAngle());
Vec3 forward = localForward.lengthSqr() > 1.0E-4D ? this.toWorld(localForward.normalize()) : this.toWorld(0.0D, 0.0D, 1.0D);
boolean mountedFlightPose = this.isMountedFlightPoseActive();
boolean mountedFlightPose = this.isFlying();
double yOffset = mountedFlightPose ? 5.3D : 3.0D;
double forwardOffset = mountedFlightPose ? 2.2D : 1.25D;
if (!this.isFlying()) {
@@ -63,7 +63,7 @@ public final class AntTeleportHelper {
}
}
private static List<Mob> collectCompanions(ServerPlayer player) {
public static List<Mob> collectCompanions(ServerPlayer player) {
List<Mob> companions = new ArrayList<>();
if (player.getVehicle() instanceof Mob vehicle) {
companions.add(vehicle);
@@ -95,7 +95,7 @@ public final class AntTeleportHelper {
}
@Nullable
private static Mob moveCompanion(Mob companion, ServerLevel destination, Vec3 destinationPos) {
public static Mob moveCompanion(Mob companion, ServerLevel destination, Vec3 destinationPos) {
companion.stopRiding();
companion.ejectPassengers();
companion.getNavigation().stop();
@@ -400,7 +400,7 @@ public final class AntTeleportHelper {
return null;
}
private static void loadArrivalSearchArea(ServerLevel destination, BlockPos preferredPos, int searchRadius) {
public static void loadArrivalSearchArea(ServerLevel destination, BlockPos preferredPos, int searchRadius) {
ChunkPos center = new ChunkPos(preferredPos);
int chunkRadius = (searchRadius >> 4) + 1;
for (int chunkX = center.x - chunkRadius; chunkX <= center.x + chunkRadius; chunkX++) {
@@ -131,7 +131,13 @@ public class KingEntity extends RoyalBossEntity {
private static final int COMBAT_WAYPOINT_VARIANCE = 35;
private static final int GROUND_COMBAT_WAYPOINT_MIN_TICKS = 18;
private static final int GROUND_COMBAT_WAYPOINT_VARIANCE = 12;
private static final int LANDING_DESCENT_TIMEOUT_TICKS = 120;
private static final int LANDING_RETRY_TICKS = 40;
private static final int LANDING_ROUTE_SAMPLES = 6;
private static final int LANDING_ROUTE_CHECK_LIMIT = 4;
private static final double GROUND_COMBAT_BITE_DISTANCE = 19.0D;
private static final double GROUND_COMBAT_BITE_VARIANCE = 4.0D;
private static final int ELEVATED_TARGET_TAKEOFF_TICKS = 40;
@Nullable
private Vec3 patrolCenter;
@Nullable
@@ -182,6 +188,7 @@ public class KingEntity extends RoyalBossEntity {
@Nullable
private Vec3 kingGroundCombatWaypoint;
private int kingStrafeDirection = 1;
private int kingLandingRetryTicks;
private int royalBoundaryGraceTicks;
@Nullable
private ServerPlayer exileTarget;
@@ -254,6 +261,35 @@ public class KingEntity extends RoyalBossEntity {
return this.royalChargeActive;
}
@Override
protected SoundEvent royalWingBuffetSound() {
return AntarchySoundEvents.KING_WING_FLAP.get();
}
@Override
protected boolean royalShouldFaceTarget(LivingEntity target) {
return !this.royalChargeActive;
}
@Override
protected void onRoyalStuck(LivingEntity target) {
switch (this.kingLandingStage) {
case GROUND_COMBAT -> this.beginKingTakeoff(target);
case TELEGRAPH, DESCENT -> {
this.resetKingCombatMovement();
this.kingLandingRetryTicks = LANDING_RETRY_TICKS;
this.groundAssaultCooldownTicks = Math.max(this.groundAssaultCooldownTicks, 160);
this.setRoyalFlying(true);
}
case AERIAL -> {
this.kingCombatWaypoint = null;
this.kingStrafeDirection *= -1;
}
default -> {
}
}
}
@Override
protected void onRoyalPhaseTransitionStarted(Phase previousPhase, Phase nextPhase) {
this.attackScheduler.cancel(RoyalAttackLane.MOVEMENT);
@@ -476,6 +512,14 @@ public class KingEntity extends RoyalBossEntity {
}
}
@Override
public boolean canDamageWithRoyalAttack(LivingEntity target) {
if (target instanceof QueenEntity) {
return target.isAlive() && !this.isAlliedTo(target);
}
return super.canDamageWithRoyalAttack(target);
}
private void chainLightning(ServerLevel level, Vec3 origin) {
java.util.Set<UUID> struck = new java.util.HashSet<>();
Vec3 from = origin;
@@ -485,7 +529,7 @@ public class KingEntity extends RoyalBossEntity {
double bestScore = Double.MAX_VALUE;
for (LivingEntity candidate : level.getEntitiesOfClass(LivingEntity.class,
new AABB(from, from).inflate(CHAIN_LIGHTNING_JUMP_RANGE),
e -> e.isAlive() && !(e instanceof RoyalBossEntity) && e.getType() != this.getType())) {
e -> e.isAlive() && (e instanceof QueenEntity || !(e instanceof RoyalBossEntity)) && e.getType() != this.getType())) {
if (struck.contains(candidate.getUUID())) {
continue;
}
@@ -565,17 +609,26 @@ public class KingEntity extends RoyalBossEntity {
if (this.royalChargeOpeningDelayTicks > 0) {
this.royalChargeOpeningDelayTicks--;
}
if (this.kingLandingRetryTicks > 0) {
this.kingLandingRetryTicks--;
}
if (!this.isDeadOrDying()) {
if (this.fireballCooldownTicks > 0) this.fireballCooldownTicks--;
if (this.iceballCooldownTicks > 0) this.iceballCooldownTicks--;
if (this.iceSpikeCooldownTicks > 0) this.iceSpikeCooldownTicks--;
}
if (this.isRoyalPhaseTransitionActive()) {
return;
}
LivingEntity target = this.getTarget();
if (target != null && !this.isDeadOrDying() && this.level() instanceof ServerLevel level) {
if (target != null && target.isAlive() && !this.isDeadOrDying() && this.level() instanceof ServerLevel level) {
this.trackBehavior(target);
this.tickDecree(level, target);
this.tickKingCombatMovement(target);
if (!this.blocksRoyalAttacksForMovement()) {
this.tickKingWholeBody(level, target);
}
this.applyRoyalBoundaryPull();
this.stabilizeKingRotation();
return;
}
@@ -583,11 +636,15 @@ public class KingEntity extends RoyalBossEntity {
if (this.isDeadOrDying() || !(this.level() instanceof ServerLevel)) {
return;
}
if (this.activeDecree != null) {
this.endDecree(null);
}
this.setRoyalFlying(true);
this.resetKingCombatMovement();
this.decreeRetreatPressure = false;
if (this.treePatrolBound && this.treePatrolCenter != null) {
this.tickTreePatrol();
this.applyRoyalBoundaryPull();
return;
}
if (this.patrolCenter == null || this.position().distanceToSqr(this.patrolCenter) > 64.0D * 64.0D) {
@@ -667,17 +724,6 @@ public class KingEntity extends RoyalBossEntity {
return;
}
double boundaryRadius = Math.max(32.0D, AntarchySettings.royalBoundaryRadius());
double distance = Math.sqrt(this.horizontalDistanceToTreeSqr());
if (distance > boundaryRadius) {
Vec3 towardCenter = this.treePatrolCenter.subtract(this.position()).multiply(1.0D, 0.0D, 1.0D);
if (towardCenter.lengthSqr() > 0.001D) {
towardCenter = towardCenter.normalize();
this.getMoveControl().setWantedPosition(this.getX() + towardCenter.x * 12.0D,
this.groundYBelow(this.getX() + towardCenter.x * 12.0D,
this.getZ() + towardCenter.z * 12.0D) + FLYING_PREFERRED_HOVER,
this.getZ() + towardCenter.z * 12.0D, 1.4D);
}
}
LivingEntity target = this.getTarget();
if (target == null || !target.isAlive()) {
this.royalBoundaryGraceTicks = 0;
@@ -702,6 +748,25 @@ public class KingEntity extends RoyalBossEntity {
}
}
private void applyRoyalBoundaryPull() {
if (!this.treePatrolBound || this.treePatrolCenter == null || this.royalChargeActive
|| this.kingLandingStage != KingLandingStage.AERIAL) {
return;
}
double boundaryRadius = Math.max(32.0D, AntarchySettings.royalBoundaryRadius());
if (this.horizontalDistanceToTreeSqr() <= boundaryRadius * boundaryRadius) {
return;
}
Vec3 towardCenter = this.treePatrolCenter.subtract(this.position()).multiply(1.0D, 0.0D, 1.0D);
if (towardCenter.lengthSqr() <= 0.001D) {
return;
}
towardCenter = towardCenter.normalize();
double x = this.getX() + towardCenter.x * 12.0D;
double z = this.getZ() + towardCenter.z * 12.0D;
this.getMoveControl().setWantedPosition(x, this.groundYBelow(x, z) + FLYING_PREFERRED_HOVER, z, 1.4D);
}
private double horizontalDistanceToTreeSqr() {
if (this.treePatrolCenter == null) {
return 0.0D;
@@ -793,9 +858,14 @@ public class KingEntity extends RoyalBossEntity {
return;
}
boolean closeQuarters = this.activeDecree instanceof CloseQuartersDecree;
if (closeQuarters && this.isRoyalFlying() && this.kingLandingStage == KingLandingStage.AERIAL) {
if (!this.isRoyalFlying() && this.kingLandingStage == KingLandingStage.AERIAL) {
this.setRoyalFlying(true);
}
if (closeQuarters && this.isRoyalFlying() && this.kingLandingStage == KingLandingStage.AERIAL
&& this.kingLandingRetryTicks <= 0) {
this.beginKingLanding(target);
} else if (!closeQuarters && this.isRoyalFlying() && this.kingLandingStage == KingLandingStage.AERIAL
&& this.kingLandingRetryTicks <= 0
&& this.groundAssaultCooldownTicks-- <= 0 && target.onGround()) {
this.beginKingLanding(target);
}
@@ -807,6 +877,7 @@ public class KingEntity extends RoyalBossEntity {
this.tickKingLandingIndicator();
if (--this.kingLandingStageTicks <= 0) {
this.kingLandingStage = KingLandingStage.DESCENT;
this.kingLandingStageTicks = LANDING_DESCENT_TIMEOUT_TICKS;
this.playRoyalSound(AntarchySoundEvents.KING_WING_FLAP.get(), 0.72F);
}
return;
@@ -829,7 +900,10 @@ public class KingEntity extends RoyalBossEntity {
|| Math.abs(localLandingDelta.y) <= 1.25D && horizontalDistanceSquared <= 4.0D;
if (reachedLanding) {
this.setRoyalFlying(false);
this.setPos(this.getX(), landing.y, this.getZ());
AABB snapped = this.getBoundingBox().move(0.0D, landing.y - this.getY(), 0.0D);
if (Math.abs(landing.y - this.getY()) > 1.0E-3D && this.level().noCollision(this, snapped)) {
this.setPos(this.getX(), landing.y, this.getZ());
}
this.kingLandingStage = KingLandingStage.IMPACT;
this.kingLandingStageTicks = LANDING_IMPACT_TICKS;
this.triggerAnim("body_action", "stomp");
@@ -837,6 +911,10 @@ public class KingEntity extends RoyalBossEntity {
if (this.level() instanceof ServerLevel level) {
this.performStomp(level, target);
}
} else if (--this.kingLandingStageTicks <= 0) {
this.resetKingCombatMovement();
this.kingLandingRetryTicks = LANDING_RETRY_TICKS;
this.groundAssaultCooldownTicks = Math.max(this.groundAssaultCooldownTicks, 160);
}
return;
}
@@ -864,8 +942,12 @@ public class KingEntity extends RoyalBossEntity {
this.getMoveControl().setWantedPosition(this.kingGroundCombatWaypoint.x,
this.kingGroundCombatWaypoint.y, this.kingGroundCombatWaypoint.z, 1.25D);
}
if (--this.kingLandingStageTicks <= 0 && !closeQuarters) {
boolean targetOutOfReach = this.royalElevatedTargetTicks() >= ELEVATED_TARGET_TAKEOFF_TICKS;
if (--this.kingLandingStageTicks <= 0 && !closeQuarters || targetOutOfReach) {
this.beginKingTakeoff(target);
if (targetOutOfReach) {
this.kingLandingRetryTicks = 100;
}
}
return;
}
@@ -913,6 +995,7 @@ public class KingEntity extends RoyalBossEntity {
} else {
this.kingLandingWaypoint = null;
this.groundAssaultCooldownTicks = 100;
this.kingLandingRetryTicks = LANDING_RETRY_TICKS;
}
}
@@ -954,7 +1037,9 @@ public class KingEntity extends RoyalBossEntity {
private Vec3 findKingLandingPoint(LivingEntity target) {
Vec3 away = this.position().subtract(target.position()).multiply(1.0D, 0.0D, 1.0D);
double baseAngle = away.lengthSqr() > 1.0E-4D ? Math.atan2(away.z, away.x) : this.random.nextDouble() * Mth.TWO_PI;
double[] radii = {9.0D, 13.0D, 17.0D, 6.0D, 4.0D, 2.0D, 0.0D};
double[] radii = {20.0D, 23.0D, 17.0D, 13.0D, 9.0D, 6.0D, 0.0D};
int routeChecks = 0;
Vec3 unroutedCandidate = null;
for (int angleIndex = 0; angleIndex < 16; angleIndex++) {
double angle = baseAngle + angleIndex * Mth.TWO_PI / 16.0D;
for (double radius : radii) {
@@ -962,18 +1047,27 @@ public class KingEntity extends RoyalBossEntity {
double z = target.getZ() + Math.sin(angle) * radius;
double y = GravityApi.isGravityInverted(this)
? target.position().y - 0.1D
: this.groundYBelow(x, z) + 0.1D;
: this.groundYBelow(x, z, target.getY() + 1.0D) + 0.1D;
Vec3 candidate = new Vec3(x, y, z);
AABB box = this.getBoundingBox().move(
candidate.x - this.getX(), candidate.y - this.getY(), candidate.z - this.getZ());
if (this.level().noCollision(this, box)) {
return candidate;
if (!this.level().noCollision(this, box)) {
continue;
}
if (unroutedCandidate == null) {
unroutedCandidate = candidate;
}
if (this.clearKingLandingRoute(candidate) || ++routeChecks >= LANDING_ROUTE_CHECK_LIMIT) {
return routeChecks >= LANDING_ROUTE_CHECK_LIMIT ? unroutedCandidate : candidate;
}
}
}
if (unroutedCandidate != null) {
return unroutedCandidate;
}
double fallbackY = GravityApi.isGravityInverted(this)
? target.position().y - 0.1D
: this.groundYBelow(target.getX(), target.getZ()) + 0.1D;
: this.groundYBelow(target.getX(), target.getZ(), target.getY() + 1.0D) + 0.1D;
Vec3 fallback = new Vec3(target.getX(), fallbackY, target.getZ());
AABB fallbackBox = this.getBoundingBox().move(
fallback.x - this.getX(), fallback.y - this.getY(), fallback.z - this.getZ());
@@ -989,6 +1083,26 @@ public class KingEntity extends RoyalBossEntity {
return this.level().noCollision(this, directBox) ? direct : null;
}
private boolean clearKingLandingRoute(Vec3 destination) {
Vec3 start = this.position();
AABB bounds = this.getBoundingBox();
for (int sample = 1; sample < LANDING_ROUTE_SAMPLES; sample++) {
Vec3 point = start.lerp(destination, sample / (double) LANDING_ROUTE_SAMPLES);
AABB box = bounds.move(point.x - this.getX(), point.y - this.getY(), point.z - this.getZ());
if (!this.level().noCollision(this, box)) {
return false;
}
}
return true;
}
private double kingAerialWaypointY(double x, double z, LivingEntity target, double hoverOffset) {
double groundY = this.groundYBelow(x, z, target.getY() + 1.0D);
double desired = target.getY() + hoverOffset;
return Mth.clamp(desired, groundY + FLYING_MIN_HOVER,
Math.max(groundY + FLYING_MAX_HOVER_ABOVE_GROUND, desired));
}
private void selectKingCombatWaypoint(LivingEntity target) {
Vec3 radial = this.position().subtract(target.position()).multiply(1.0D, 0.0D, 1.0D);
if (radial.lengthSqr() < 1.0E-4D) {
@@ -1009,9 +1123,7 @@ public class KingEntity extends RoyalBossEntity {
double candidateRange = range + (angleAttempt % 2 == 0 ? 0.0D : -6.0D);
double x = target.getX() + Math.cos(angle) * candidateRange;
double z = target.getZ() + Math.sin(angle) * candidateRange;
double groundY = this.groundYBelow(x, z);
double y = Mth.clamp(target.getY() + hoverOffset,
groundY + FLYING_MIN_HOVER, groundY + FLYING_MAX_HOVER_ABOVE_GROUND);
double y = this.kingAerialWaypointY(x, z, target, hoverOffset);
Vec3 candidate = new Vec3(x, y, z);
if (treeBoundCombat) {
double dx = x - this.treePatrolCenter.x;
@@ -1029,9 +1141,7 @@ public class KingEntity extends RoyalBossEntity {
double angle = currentAngle + this.kingStrafeDirection * COMBAT_ORBIT_ANGLE_STEP;
double x = target.getX() + Math.cos(angle) * range;
double z = target.getZ() + Math.sin(angle) * range;
double groundY = this.groundYBelow(x, z);
double y = Mth.clamp(target.getY() + hoverOffset,
groundY + FLYING_MIN_HOVER, groundY + FLYING_MAX_HOVER_ABOVE_GROUND);
double y = this.kingAerialWaypointY(x, z, target, hoverOffset);
Vec3 candidate = new Vec3(x, y, z);
double dx = candidate.x - this.treePatrolCenter.x;
double dz = candidate.z - this.treePatrolCenter.z;
@@ -1044,9 +1154,7 @@ public class KingEntity extends RoyalBossEntity {
double angle = currentAngle + this.kingStrafeDirection * COMBAT_ORBIT_ANGLE_STEP;
double x = target.getX() + Math.cos(angle) * range;
double z = target.getZ() + Math.sin(angle) * range;
double groundY = this.groundYBelow(x, z);
double y = Mth.clamp(target.getY() + hoverOffset,
groundY + FLYING_MIN_HOVER, groundY + FLYING_MAX_HOVER_ABOVE_GROUND);
double y = this.kingAerialWaypointY(x, z, target, hoverOffset);
orbitWaypoint = new Vec3(x, y, z);
if (treeBoundCombat) {
orbitWaypoint = this.findTreePatrolWaypoint(this.treePatrolCenter, this.treePatrolAngle);
@@ -1060,6 +1168,9 @@ public class KingEntity extends RoyalBossEntity {
}
private void stabilizeKingRotation() {
if (this.royalFacingPoint() != null) {
return;
}
Vec3 movement = this.getDeltaMovement().multiply(1.0D, 0.0D, 1.0D);
if (movement.lengthSqr() < 0.0025D) {
return;
@@ -1093,16 +1204,16 @@ public class KingEntity extends RoyalBossEntity {
radial = radial.normalize();
Vec3 tangent = new Vec3(-radial.z * this.kingStrafeDirection, 0.0D,
radial.x * this.kingStrafeDirection);
double distance = 4.0D + this.random.nextDouble() * 3.0D;
Vec3 candidate = target.position().add(radial.scale(distance)).add(tangent.scale(2.5D));
Vec3 waypoint = new Vec3(candidate.x, target.getY(), candidate.z);
double distance = GROUND_COMBAT_BITE_DISTANCE + this.random.nextDouble() * GROUND_COMBAT_BITE_VARIANCE;
Vec3 candidate = target.position().add(radial.scale(distance)).add(tangent.scale(4.0D));
Vec3 waypoint = this.kingGroundPoint(candidate.x, candidate.z, target);
AABB box = this.getBoundingBox().move(waypoint.x - this.getX(), waypoint.y - this.getY(), waypoint.z - this.getZ());
if (!this.level().noCollision(this, box)) {
waypoint = target.position().add(radial.scale(distance));
waypoint = new Vec3(waypoint.x, target.getY(), waypoint.z);
Vec3 radialOnly = target.position().add(radial.scale(distance));
waypoint = this.kingGroundPoint(radialOnly.x, radialOnly.z, target);
box = this.getBoundingBox().move(waypoint.x - this.getX(), waypoint.y - this.getY(), waypoint.z - this.getZ());
}
this.kingGroundCombatWaypoint = this.level().noCollision(this, box) ? waypoint : target.position();
this.kingGroundCombatWaypoint = this.level().noCollision(this, box) ? waypoint : this.position();
this.kingGroundCombatWaypointTicks = GROUND_COMBAT_WAYPOINT_MIN_TICKS
+ this.random.nextInt(GROUND_COMBAT_WAYPOINT_VARIANCE + 1);
if (this.random.nextInt(4) == 0) {
@@ -1110,6 +1221,13 @@ public class KingEntity extends RoyalBossEntity {
}
}
private Vec3 kingGroundPoint(double x, double z, LivingEntity target) {
double y = GravityApi.isGravityInverted(this)
? target.getY()
: this.groundYBelow(x, z, Math.max(this.getY(), target.getY()) + 1.0D);
return new Vec3(x, y, z);
}
public void requestCloseQuartersAssault(LivingEntity target) {
if (target != null && target.isAlive() && this.isRoyalFlying()
&& this.kingLandingStage == KingLandingStage.AERIAL) {
@@ -1433,10 +1551,8 @@ public class KingEntity extends RoyalBossEntity {
this.bumpBehavior(Behavior.SPRINTING, target.isSprinting() ? 1 : -1);
this.bumpBehavior(Behavior.HUGGING, distance <= 10.0D ? 1 : -1);
this.bumpBehavior(Behavior.KEEPING_FAR, distance >= 22.0D ? 1 : -1);
if (this.patrolCenter != null) {
this.bumpBehavior(Behavior.RETREATING, distance > 20.0D && target.getDeltaMovement().horizontalDistanceSqr() > 0.02D
&& target.position().subtract(this.position()).dot(target.getDeltaMovement()) > 0.0D ? 1 : -1);
}
this.bumpBehavior(Behavior.RETREATING, distance > 20.0D && target.getDeltaMovement().horizontalDistanceSqr() > 0.02D
&& target.position().subtract(this.position()).dot(target.getDeltaMovement()) > 0.0D ? 1 : -1);
}
private void bumpBehavior(Behavior behavior, int delta) {
@@ -1708,10 +1824,6 @@ public class KingEntity extends RoyalBossEntity {
}
private void tickKingElementalProjectiles(ServerLevel level, LivingEntity target) {
if (this.fireballCooldownTicks > 0) this.fireballCooldownTicks--;
if (this.iceballCooldownTicks > 0) this.iceballCooldownTicks--;
if (this.iceSpikeCooldownTicks > 0) this.iceSpikeCooldownTicks--;
double distance = this.distanceTo(target);
String[] candidates = {"fireball", "iceball", "ice_spikes"};
String selected = this.attackScheduler.chooseWeighted(candidates, id -> {
@@ -1789,14 +1901,25 @@ public class KingEntity extends RoyalBossEntity {
Vec3 start = this.position();
Vec3 end = target.position();
Vec3 line = end.subtract(start).multiply(1.0D, 0.0D, 1.0D);
if (line.lengthSqr() < 1.0E-4D) return;
line = line.normalize();
double reach = line.length();
if (reach < 6.0D) {
for (int index = 0; index < 8; index++) {
double angle = Mth.TWO_PI * index / 8.0D;
double x = end.x + Math.cos(angle) * 2.5D;
double z = end.z + Math.sin(angle) * 2.5D;
double y = this.groundYBelow(x, z, end.y + 1.0D) + 0.1D;
level.addFreshEntity(RoyalIceSpikeEntity.create(level, new Vec3(x, y, z), this));
}
return;
}
line = line.scale(1.0D / reach);
Vec3 side = new Vec3(-line.z, 0.0D, line.x);
for (int i = 1; i <= 5; i++) {
double distance = i * 4.0D;
double firstDistance = Math.max(4.0D, reach - 16.0D);
for (int i = 0; i < 5; i++) {
double distance = firstDistance + i * 4.0D;
for (int lane = -1; lane <= 1; lane++) {
Vec3 point = start.add(line.scale(distance)).add(side.scale(lane * (i % 2 == 0 ? 2.0D : 0.0D)));
double y = this.groundYBelow(point.x, point.z) + 0.1D;
Vec3 point = start.add(line.scale(distance)).add(side.scale(lane * (i % 2 == 1 ? 2.0D : 0.0D)));
double y = this.groundYBelow(point.x, point.z, end.y + 1.0D) + 0.1D;
RoyalIceSpikeEntity spike = RoyalIceSpikeEntity.create(level, new Vec3(point.x, y, point.z), this);
level.addFreshEntity(spike);
}
@@ -44,7 +44,10 @@ import net.minecraft.world.entity.ai.attributes.AttributeInstance;
import net.minecraft.world.entity.ai.attributes.AttributeModifier;
import net.minecraft.world.entity.ai.attributes.AttributeSupplier;
import net.minecraft.world.entity.ai.attributes.Attributes;
import net.minecraft.world.entity.ai.goal.target.HurtByTargetGoal;
import net.minecraft.world.entity.ai.goal.target.NearestAttackableTargetGoal;
import net.minecraft.world.entity.ambient.AmbientCreature;
import net.minecraft.world.entity.animal.WaterAnimal;
import net.minecraft.world.entity.MobSpawnType;
import net.minecraft.world.level.GameRules;
import net.minecraft.world.level.Level;
@@ -128,6 +131,12 @@ public class QueenEntity extends RoyalBossEntity {
private static final int ACCELERATION_VOLLEY_DELAY = 18;
private static final int ACCELERATION_LANDING_DELAY = 55;
private static final int DIRECTED_LANDING_RETRY_TICKS = 60;
private static final int LANDING_TIMEOUT_COOLDOWN_TICKS = 160;
private static final double GROUND_CHARGE_MIN_DISTANCE = 16.0D;
private static final double GROUND_CHARGE_HIT_INFLATE = 1.5D;
private static final double GROUND_CHARGE_DAMAGE_SCALE = 1.1D;
private static final double GROUND_CHARGE_KNOCKBACK = 2.2D;
private static final int ELEVATED_TARGET_TAKEOFF_TICKS = 40;
private int manticoreSummonCooldownTicks;
private int gravityStompCooldownTicks;
@@ -161,6 +170,7 @@ public class QueenEntity extends RoyalBossEntity {
private Vec3 queenLandingWaypoint;
@Nullable
private Vec3 queenGroundChargeDirection;
private final java.util.Set<UUID> queenGroundChargeHits = new java.util.HashSet<>();
@Nullable
private Phase pendingPhaseSignature;
private boolean pendingPhaseTwoCrushingGravity;
@@ -295,11 +305,34 @@ public class QueenEntity extends RoyalBossEntity {
@Override
protected double combatStandoffDistance() {
if (this.queenLandingStage == QueenLandingStage.GROUND_COMBAT && !this.isRoyalFlying()) {
return 0.0D;
return super.combatStandoffDistance();
}
return Math.max(super.combatStandoffDistance(), AntarchySettings.queenBeamMinimumRange() + 6.0D);
}
@Override
protected SoundEvent royalWingBuffetSound() {
return AntarchySoundEvents.QUEEN_FLY_LOOP.get();
}
@Override
protected boolean royalShouldFaceTarget(LivingEntity target) {
return this.queenGroundChargeWindupTicks <= 0 && this.queenGroundChargeTicks <= 0;
}
@Override
protected void onRoyalStuck(LivingEntity target) {
switch (this.queenLandingStage) {
case GROUND_COMBAT -> this.beginQueenTakeoff();
case TELEGRAPH, DESCENT -> {
this.resetQueenLandingSequence();
this.queenLandingCooldownTicks = LANDING_TIMEOUT_COOLDOWN_TICKS;
}
default -> {
}
}
}
@Override
protected double biteDamageMultiplier() {
return super.biteDamageMultiplier() * 1.5D;
@@ -320,9 +353,13 @@ public class QueenEntity extends RoyalBossEntity {
protected void registerRoyalTargetGoals() {
this.targetSelector.addGoal(1, new NearestAttackableTargetGoal<>(this, KingEntity.class, true));
this.targetSelector.addGoal(2, new NearestAttackableTargetGoal<>(this, Player.class, true));
this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(
this, Mob.class, 10, true, false, this::canAttack));
this.targetSelector.addGoal(2, new HurtByTargetGoal(this).setUnseenMemoryTicks(ROYAL_UNSEEN_MEMORY_TICKS));
this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, Player.class, true)
.setUnseenMemoryTicks(ROYAL_UNSEEN_MEMORY_TICKS));
this.targetSelector.addGoal(4, new NearestAttackableTargetGoal<>(
this, Mob.class, 10, true, false,
living -> !(living instanceof AmbientCreature) && !(living instanceof WaterAnimal)
&& this.canAttack(living)));
}
@Override
@@ -495,6 +532,9 @@ public class QueenEntity extends RoyalBossEntity {
}
private void tickQueenLandingSequence(LivingEntity target) {
if (this.queenLandingStage == QueenLandingStage.AERIAL && !this.isRoyalFlying()) {
this.setRoyalFlying(true);
}
if (this.queenLandingStage == QueenLandingStage.AERIAL && this.isRoyalFlying()
&& !this.isQueenAbilityTelegraphActive()
&& this.queenLandingCooldownTicks-- <= 0) {
@@ -529,7 +569,7 @@ public class QueenEntity extends RoyalBossEntity {
this.performGravityStomp((ServerLevel) this.level(), target);
} else if (--this.queenLandingStageTicks <= 0) {
this.resetQueenLandingSequence();
this.queenLandingCooldownTicks = 40;
this.queenLandingCooldownTicks = LANDING_TIMEOUT_COOLDOWN_TICKS;
}
}
case IMPACT -> {
@@ -544,7 +584,8 @@ public class QueenEntity extends RoyalBossEntity {
}
case GROUND_COMBAT -> {
this.tickQueenGroundCharge(target);
if (--this.queenLandingStageTicks <= 0) {
if (--this.queenLandingStageTicks <= 0
|| this.royalElevatedTargetTicks() >= ELEVATED_TARGET_TAKEOFF_TICKS) {
this.beginQueenTakeoff();
}
}
@@ -577,7 +618,7 @@ public class QueenEntity extends RoyalBossEntity {
double baseAngle = away.lengthSqr() > 1.0E-4D
? Math.atan2(away.z, away.x) : this.random.nextDouble() * Mth.TWO_PI;
boolean inverted = GravityApi.isGravityInverted(this);
double[] radii = {10.0D, 14.0D, 18.0D, 7.0D};
double[] radii = {20.0D, 23.0D, 17.0D, 14.0D, 10.0D};
for (int angleIndex = 0; angleIndex < 12; angleIndex++) {
double angle = baseAngle + angleIndex * Mth.TWO_PI / 12.0D;
for (double radius : radii) {
@@ -585,7 +626,7 @@ public class QueenEntity extends RoyalBossEntity {
double z = target.getZ() + Math.sin(angle) * radius;
double y = inverted
? target.position().y - 0.1D
: this.groundYBelow(x, z) + 0.1D;
: this.groundYBelow(x, z, target.getY() + 1.0D) + 0.1D;
Vec3 candidate = new Vec3(x, y, z);
AABB box = this.getBoundingBox().move(
candidate.x - this.getX(), candidate.y - this.getY(), candidate.z - this.getZ());
@@ -596,7 +637,7 @@ public class QueenEntity extends RoyalBossEntity {
}
double fallbackY = inverted
? target.position().y - 0.1D
: this.groundYBelow(target.getX(), target.getZ()) + 0.1D;
: this.groundYBelow(target.getX(), target.getZ(), target.getY() + 1.0D) + 0.1D;
Vec3 fallback = new Vec3(target.getX(), fallbackY, target.getZ());
AABB fallbackBox = this.getBoundingBox().move(
fallback.x - this.getX(), fallback.y - this.getY(), fallback.z - this.getZ());
@@ -667,6 +708,7 @@ public class QueenEntity extends RoyalBossEntity {
if (direction.lengthSqr() > 1.0E-4D) {
this.queenGroundChargeDirection = direction.normalize();
this.queenGroundChargeTicks = GROUND_CHARGE_DURATION_TICKS;
this.queenGroundChargeHits.clear();
} else {
this.finishQueenGroundCharge();
}
@@ -681,6 +723,9 @@ public class QueenEntity extends RoyalBossEntity {
this.setYRot((float) (Mth.atan2(-direction.x, direction.z) * Mth.RAD_TO_DEG));
this.yBodyRot = this.getYRot();
this.hasImpulse = true;
if (this.level() instanceof ServerLevel level) {
this.applyQueenGroundChargeHits(level, direction);
}
if (--this.queenGroundChargeTicks <= 0) {
this.finishQueenGroundCharge();
}
@@ -688,7 +733,8 @@ public class QueenEntity extends RoyalBossEntity {
}
if (this.queenGroundChargeCooldownTicks-- > 0
|| target.position().multiply(1.0D, 0.0D, 1.0D)
.distanceToSqr(this.position().multiply(1.0D, 0.0D, 1.0D)) < 36.0D) {
.distanceToSqr(this.position().multiply(1.0D, 0.0D, 1.0D))
< GROUND_CHARGE_MIN_DISTANCE * GROUND_CHARGE_MIN_DISTANCE) {
return;
}
this.queenGroundChargeDirection = target.position().subtract(this.position())
@@ -701,6 +747,34 @@ public class QueenEntity extends RoyalBossEntity {
}
}
private void applyQueenGroundChargeHits(ServerLevel level, Vec3 direction) {
float damage = (float) (this.getAttributeValue(Attributes.ATTACK_DAMAGE) * GROUND_CHARGE_DAMAGE_SCALE);
DamageSource source = this.damageSources().mobAttack(this);
AABB hitBox = this.getBoundingBox().inflate(GROUND_CHARGE_HIT_INFLATE);
for (LivingEntity living : level.getEntitiesOfClass(LivingEntity.class, hitBox,
entity -> entity.isAlive() && entity != this && this.canDamageWithRoyalAttack(entity))) {
Vec3 offset = living.position().subtract(this.position()).multiply(1.0D, 0.0D, 1.0D);
if (offset.dot(direction) < -this.getBbWidth() * 0.15D
|| !this.queenGroundChargeHits.add(living.getUUID())) {
continue;
}
living.hurt(source, damage);
if (!(living instanceof RoyalBossEntity)) {
Vec3 lift = GravityRotationUtil.vecPlayerToWorld(
0.0D, 0.55D, 0.0D, GravityApi.getGravityDirection(living));
this.addGravityAwareImpulse(living, direction.scale(GROUND_CHARGE_KNOCKBACK).add(lift));
living.hasImpulse = true;
living.hurtMarked = true;
}
level.sendParticles(ParticleTypes.CRIT, living.getX(), living.getY() + living.getBbHeight() * 0.5D,
living.getZ(), 16, 0.5D, 0.6D, 0.5D, 0.1D);
}
if (this.queenGroundChargeTicks % 4 == 0) {
Vec3 front = this.position().add(direction.scale(this.getBbWidth() * 0.5D + 1.0D));
RoyalBlockDestruction.destroySphere(level, this, front.add(0.0D, 2.0D, 0.0D), 3.5D, 24, 60.0D, 0.1F);
}
}
private void finishQueenGroundCharge() {
this.queenGroundChargeTicks = 0;
this.queenGroundChargeWindupTicks = 0;
@@ -710,6 +784,7 @@ public class QueenEntity extends RoyalBossEntity {
}
private void clearQueenGroundCharge() {
this.queenGroundChargeHits.clear();
this.queenGroundChargeCooldownTicks = 0;
this.queenGroundChargeWindupTicks = 0;
this.queenGroundChargeTicks = 0;
@@ -1048,14 +1123,21 @@ public class QueenEntity extends RoyalBossEntity {
: this.pendingPhaseSignature == Phase.THREE ? "acceleration"
: phase == Phase.TWO && this.pendingPhaseTwoCrushingGravity ? "crushing_gravity"
: phase == Phase.THREE && this.pendingPhaseThreeChronosphere ? "queen_chronosphere" : null;
String selected = forcedAttack != null
String chosen = forcedAttack != null
&& this.queenAttackReady(forcedAttack, phase, majorBusy)
? forcedAttack : this.attackScheduler.chooseWeighted(candidates, id -> {
if (!this.queenAttackReady(id, phase, majorBusy)
|| id.equals(this.lastQueenMajorAttack)) return 0;
return this.queenAttackWeight(id, phase);
}, this.random);
if (selected == null) return;
if (chosen == null && this.lastQueenMajorAttack != null
&& this.queenAttackReady(this.lastQueenMajorAttack, phase, majorBusy)
&& java.util.Arrays.asList(candidates).contains(this.lastQueenMajorAttack)
&& this.queenAttackWeight(this.lastQueenMajorAttack, phase) > 0) {
chosen = this.lastQueenMajorAttack;
}
if (chosen == null) return;
final String selected = chosen;
int cooldown = switch (selected) {
case "gravity_stomp" -> GRAVITY_STOMP_COOLDOWN;
case "black_hole" -> BLACK_HOLE_COOLDOWN;
@@ -1606,7 +1688,9 @@ public class QueenEntity extends RoyalBossEntity {
double radius = this.random.nextDouble() * GRAVITIC_UPHEAVAL_RADIUS;
double x = center.x + Math.cos(angle) * radius;
double z = center.z + Math.sin(angle) * radius;
BlockPos pos = BlockPos.containing(x, this.groundYBelow(x, z), z);
BlockPos pos = inverted
? BlockPos.containing(x, this.ceilingYAbove(x, z, this.getBoundingBox().minY + 0.5D) + 0.5D, z)
: BlockPos.containing(x, this.groundYBelow(x, z) - 0.5D, z);
BlockState state = level.getBlockState(pos);
if (!state.is(AntarchyTags.Blocks.QUEEN_GRABBABLE_BLOCKS) || level.getBlockEntity(pos) != null
|| state.getDestroySpeed(level, pos) < 0.0F || state.getCollisionShape(level, pos).isEmpty()) {
@@ -57,8 +57,13 @@ import net.minecraft.world.entity.ai.navigation.GroundPathNavigation;
import net.minecraft.world.entity.ai.navigation.PathNavigation;
import net.minecraft.world.entity.monster.Monster;
import net.minecraft.world.entity.player.Player;
import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.sounds.SoundSource;
import net.minecraft.world.level.ClipContext;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.HitResult;
import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.Nullable;
import software.bernie.geckolib.animatable.GeoEntity;
@@ -112,6 +117,9 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
private static final ResourceLocation MULTIPLAYER_DAMAGE_ID = new ResourceLocation("antarchy", "royal_multiplayer_damage");
private static final UUID MULTIPLAYER_DAMAGE_UUID = UUID.nameUUIDFromBytes(MULTIPLAYER_DAMAGE_ID.toString().getBytes());
private static final int OBSTRUCTION_CLEAR_INTERVAL_TICKS = 5;
private static final int RIVAL_OBSTRUCTION_CLEAR_INTERVAL_TICKS = 10;
private static final int BODY_CRUSH_INTERVAL_TICKS = 10;
private static final int RIVAL_BODY_CRUSH_INTERVAL_TICKS = 20;
private static final double OBSTRUCTION_CLEAR_RADIUS = 3.5D;
private static final int OBSTRUCTION_CLEAR_MAX_BLOCKS = 24;
private static final double OBSTRUCTION_CLEAR_MAX_RESISTANCE = 60.0D;
@@ -121,6 +129,36 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
private static final double BEAM_MUZZLE_SIDE_Y = 10.25D;
private static final double BEAM_MUZZLE_LATERAL = 2.625D;
private static final int HEAVY_BLOCK_CHECK_INTERVAL_TICKS = 4;
private static final String WING_BUFFET_ID = "royal_wing_buffet";
private static final int WING_BUFFET_WINDUP_TICKS = 16;
private static final double WING_BUFFET_EXTRA_RADIUS = 5.0D;
private static final double WING_BUFFET_BELOW = 14.0D;
private static final double WING_BUFFET_ABOVE = 6.0D;
private static final double WING_BUFFET_DAMAGE_SCALE = 0.85D;
private static final double WING_BUFFET_PUSH = 2.6D;
private static final double WING_BUFFET_LIFT = 0.75D;
private static final String EXCAVATION_ID = "royal_excavation";
private static final int EXCAVATION_TRIGGER_TICKS = 100;
private static final int EXCAVATION_WINDUP_TICKS = 24;
private static final int EXCAVATION_COOLDOWN_TICKS = 200;
private static final double EXCAVATION_RANGE = 72.0D;
private static final double EXCAVATION_RADIUS = 2.5D;
private static final double EXCAVATION_DEPTH = 10.0D;
private static final int EXCAVATION_MAX_BLOCKS = 20;
private static final double EXCAVATION_CONCEALED_DAMAGE = 5.0D;
private static final int STUCK_SAMPLE_TICKS = 40;
private static final double STUCK_MIN_PROGRESS_SQR = 1.0D;
private static final double STUCK_MIN_WANTED_DISTANCE_SQR = 36.0D;
private static final int STUCK_TAKEOFF_STRIKES = 2;
private static final int STUCK_TELEPORT_STRIKES = 4;
private static final int STUCK_TELEPORT_COOLDOWN_TICKS = 600;
private static final int STUCK_TELEPORT_ATTEMPTS = 16;
private static final int LANDING_FOR_COMBAT_TIMEOUT_TICKS = 120;
private static final int GROUND_SCAN_DEPTH = 48;
private static final int GROUND_SCAN_RISE = 24;
private static final double MAX_BEAM_PITCH = Math.toRadians(55.0D);
private static final int ELEVATED_TARGET_HEIGHT = 10;
private static final float FACING_TURN_RATE = 6.0F;
protected static final double FLYING_MIN_HOVER = 2.5D;
protected static final double FLYING_PREFERRED_HOVER = 6.0D;
@@ -197,6 +235,16 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
private boolean multiplayerScalingInitialized;
private final Set<UUID> encounterParticipants = new HashSet<>();
private double royalDamageMultiplier = 1.0D;
private int landingForCombatTicks;
private int royalDeadZoneTicks;
private int royalConcealedTicks;
private int royalElevatedTargetTicks;
private int royalLastMoveRequestTick = Integer.MIN_VALUE;
@Nullable
private Vec3 royalStuckSamplePosition;
private int royalStuckSampleTicks;
private int royalStuckStrikes;
private int royalTeleportCooldownTicks;
@Nullable
private Entity[] multipartParts;
@@ -207,8 +255,8 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
this.beamControllers[i] = new BeamController(this);
}
this.xpReward = 500;
this.groundMoveControl = new MoveControl(this);
this.flyingMoveControl = new FlyingMoveControl(this, 20, true);
this.groundMoveControl = new RoyalGroundMoveControl(this);
this.flyingMoveControl = new RoyalFlyingMoveControl(this, 20);
this.groundNavigation = new GroundPathNavigation(this, level);
FlyingPathNavigation flyingNavigation = new FlyingPathNavigation(this, level);
flyingNavigation.setCanFloat(true);
@@ -217,6 +265,7 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
this.moveControl = this.flyingMoveControl;
this.navigation = this.flyingNavigation;
} else {
this.moveControl = this.groundMoveControl;
this.navigation = this.groundNavigation;
}
this.bossEvent = new com.craisinlord.antarchy.content.boss.EntityLinkedServerBossEvent(
@@ -380,8 +429,42 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
}
protected void registerRoyalTargetGoals() {
this.targetSelector.addGoal(1, new HurtByTargetGoal(this));
this.targetSelector.addGoal(2, new NearestAttackableTargetGoal<>(this, Player.class, true));
this.targetSelector.addGoal(1, new HurtByTargetGoal(this).setUnseenMemoryTicks(ROYAL_UNSEEN_MEMORY_TICKS));
this.targetSelector.addGoal(2, new NearestAttackableTargetGoal<>(this, Player.class, true)
.setUnseenMemoryTicks(ROYAL_UNSEEN_MEMORY_TICKS));
}
protected static final int ROYAL_UNSEEN_MEMORY_TICKS = 400;
protected SoundEvent royalWingBuffetSound() {
return this.royalBiteSound();
}
protected int royalWingBuffetCooldownTicks(Phase phase) {
return switch (phase) {
case ONE -> 110;
case TWO -> 90;
case THREE -> 70;
};
}
protected int royalDeadZoneTriggerTicks(Phase phase) {
return switch (phase) {
case ONE -> 40;
case TWO -> 32;
case THREE -> 24;
};
}
protected boolean royalShouldFaceTarget(LivingEntity target) {
return true;
}
protected void onRoyalStuck(LivingEntity target) {
}
protected final int royalElevatedTargetTicks() {
return this.royalElevatedTargetTicks;
}
public boolean canDamageWithRoyalAttack(LivingEntity target) {
@@ -512,7 +595,7 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
if (this.shouldClearObstruction()
&& !this.level().isClientSide
&& !this.isDeadOrDying()
&& this.tickCount % OBSTRUCTION_CLEAR_INTERVAL_TICKS == 0
&& this.tickCount % (this.isFightingRoyalRival() ? RIVAL_OBSTRUCTION_CLEAR_INTERVAL_TICKS : OBSTRUCTION_CLEAR_INTERVAL_TICKS) == 0
&& this.isInWall()) {
RoyalBlockDestruction.destroySphere(
(ServerLevel) this.level(),
@@ -575,13 +658,16 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
for (RoyalHead head : this.heads) {
head.setTarget(null);
}
this.resetRoyalCombatTracking();
return;
}
this.initializeMultiplayerScaling(primaryTarget);
this.assignHeadTargets(primaryTarget);
this.tickRoyalTargetElevation(primaryTarget);
this.tickCombatLocomotionMode(primaryTarget);
this.tickRoyalStuckRecovery(primaryTarget);
boolean movementBlocksAttacks = this.blocksRoyalAttacksForMovement();
if (!movementBlocksAttacks) {
boolean aeriallyStabilized = this.steerTowardTarget(primaryTarget);
@@ -595,6 +681,8 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
return;
}
this.tickBodyCrush();
this.tickRoyalCloseRangeAnswer(primaryTarget);
this.tickRoyalConcealedTarget(primaryTarget);
Phase phase = this.phase();
int activeHeadAttacks = 0;
@@ -917,15 +1005,18 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
double verticalDistance = Math.abs(target.getY() + target.getBbHeight() * 0.5D
- (this.getY() + this.getBbHeight() * 0.5D));
boolean inverted = GravityApi.isGravityInverted(this);
double groundBelowTarget = this.groundYBelow(wanted.x, wanted.z, target.getY() + 1.0D);
double wantedY = this.landingForCombat
? (inverted ? target.getY() + target.getBbHeight() - this.getBbHeight() : groundBelowTarget)
: inverted
? target.getY() - 3.0D
: Mth.clamp(target.getY() + 3.0D,
groundBelowTarget + FLYING_MIN_HOVER,
Math.max(groundBelowTarget + FLYING_MAX_HOVER_ABOVE_GROUND, target.getY() + 4.0D));
if (!this.landingForCombat && verticalDistance > 6.0D) {
if (this.aerialCombatAnchor == null || this.aerialCombatAnchorTicks-- <= 0) {
double anchorX = this.getX();
double anchorZ = this.getZ();
if (horizontalDistance > 10.0D) {
anchorX = wanted.x;
anchorZ = wanted.z;
}
this.aerialCombatAnchor = new Vec3(anchorX, this.getY(), anchorZ);
this.aerialCombatAnchor = new Vec3(wanted.x, wantedY, wanted.z);
this.aerialCombatAnchorTicks = 20;
}
Vec3 anchor = this.aerialCombatAnchor;
@@ -935,15 +1026,6 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
this.aerialCombatAnchor = null;
this.aerialCombatAnchorTicks = 0;
boolean inverted = GravityApi.isGravityInverted(this);
double groundBelowTarget = this.groundYBelow(wanted.x, wanted.z);
double wantedY = this.landingForCombat
? (inverted ? target.getY() + target.getBbHeight() - this.getBbHeight() : groundBelowTarget)
: inverted
? target.getY() - 3.0D
: Mth.clamp(target.getY() + 3.0D,
groundBelowTarget + FLYING_MIN_HOVER,
groundBelowTarget + FLYING_MAX_HOVER_ABOVE_GROUND);
boolean closeVertically = Math.abs(wantedY - this.getY()) <= 2.5D;
if (atStandoff && closeVertically) {
this.getNavigation().stop();
@@ -953,6 +1035,536 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
return false;
}
private void resetRoyalCombatTracking() {
this.royalDeadZoneTicks = 0;
this.royalConcealedTicks = 0;
this.royalElevatedTargetTicks = 0;
this.royalStuckSamplePosition = null;
this.royalStuckSampleTicks = 0;
this.royalStuckStrikes = 0;
}
private void tickRoyalTargetElevation(LivingEntity target) {
Vec3 local = GravityRotationUtil.vecWorldToPlayer(target.position().subtract(this.position()),
GravityApi.getGravityDirection(this));
if (local.y > ELEVATED_TARGET_HEIGHT) {
this.royalElevatedTargetTicks++;
} else {
this.royalElevatedTargetTicks = 0;
}
}
final void markRoyalMoveRequested() {
this.royalLastMoveRequestTick = this.tickCount;
}
private void tickRoyalStuckRecovery(LivingEntity target) {
if (this.royalTeleportCooldownTicks > 0) {
this.royalTeleportCooldownTicks--;
}
MoveControl control = this.getMoveControl();
boolean requested = (long) this.tickCount - (long) this.royalLastMoveRequestTick <= 2L
&& this.distanceToSqr(control.getWantedX(), control.getWantedY(), control.getWantedZ())
> STUCK_MIN_WANTED_DISTANCE_SQR;
if (!requested) {
this.royalStuckSamplePosition = null;
this.royalStuckSampleTicks = 0;
this.royalStuckStrikes = 0;
return;
}
if (this.royalStuckSamplePosition == null) {
this.royalStuckSamplePosition = this.position();
this.royalStuckSampleTicks = 0;
return;
}
if (++this.royalStuckSampleTicks < STUCK_SAMPLE_TICKS) {
return;
}
boolean progressed = this.position().distanceToSqr(this.royalStuckSamplePosition) >= STUCK_MIN_PROGRESS_SQR;
this.royalStuckSamplePosition = this.position();
this.royalStuckSampleTicks = 0;
if (progressed) {
this.royalStuckStrikes = 0;
return;
}
this.royalStuckStrikes++;
this.onRoyalMovementObstructed(target);
}
private void onRoyalMovementObstructed(LivingEntity target) {
if (this.level() instanceof ServerLevel serverLevel) {
this.clearRoyalPathTowardWanted(serverLevel);
}
if (this.royalStuckStrikes >= STUCK_TAKEOFF_STRIKES) {
this.onRoyalStuck(target);
if (this.isFlyingBoss()) {
this.addGravityAwareImpulse(this, GravityRotationUtil.vecPlayerToWorld(
0.0D, 0.6D, 0.0D, GravityApi.getGravityDirection(this)));
this.hasImpulse = true;
}
}
if (this.royalStuckStrikes >= STUCK_TELEPORT_STRIKES && this.royalTeleportCooldownTicks <= 0
&& this.tryRoyalRecoveryTeleport(target)) {
this.royalStuckStrikes = 0;
this.royalStuckSamplePosition = null;
this.royalTeleportCooldownTicks = STUCK_TELEPORT_COOLDOWN_TICKS;
}
}
private void clearRoyalPathTowardWanted(ServerLevel level) {
MoveControl control = this.getMoveControl();
Vec3 toWanted = new Vec3(control.getWantedX() - this.getX(), control.getWantedY() - this.getY(),
control.getWantedZ() - this.getZ());
Vec3 horizontal = toWanted.multiply(1.0D, 0.0D, 1.0D);
Vec3 direction;
if (horizontal.lengthSqr() > 1.0E-4D) {
direction = horizontal.normalize();
} else {
double yaw = this.yBodyRot * Mth.DEG_TO_RAD;
direction = new Vec3(-Math.sin(yaw), 0.0D, Math.cos(yaw));
}
Vec3 side = new Vec3(-direction.z, 0.0D, direction.x);
var gravity = GravityApi.getGravityDirection(this);
double reach = this.getBbWidth() * 0.5D + 1.0D;
double[] heights = {1.5D, this.getBbHeight() * 0.4D, this.getBbHeight() * 0.75D};
double[] laterals = {-this.getBbWidth() * 0.3D, 0.0D, this.getBbWidth() * 0.3D};
for (double height : heights) {
for (double lateral : laterals) {
Vec3 center = this.position().add(direction.scale(reach)).add(side.scale(lateral))
.add(GravityRotationUtil.vecPlayerToWorld(0.0D, height, 0.0D, gravity));
RoyalBlockDestruction.destroySphere(level, this, center, OBSTRUCTION_CLEAR_RADIUS,
OBSTRUCTION_CLEAR_MAX_BLOCKS, OBSTRUCTION_CLEAR_MAX_RESISTANCE, OBSTRUCTION_CLEAR_DROP_CHANCE);
}
}
if (toWanted.y > 3.0D) {
RoyalBlockDestruction.destroySphere(level, this,
this.position().add(GravityRotationUtil.vecPlayerToWorld(0.0D, this.getBbHeight() + 1.0D, 0.0D, gravity)),
OBSTRUCTION_CLEAR_RADIUS, OBSTRUCTION_CLEAR_MAX_BLOCKS, OBSTRUCTION_CLEAR_MAX_RESISTANCE,
OBSTRUCTION_CLEAR_DROP_CHANCE);
}
}
private boolean tryRoyalRecoveryTeleport(LivingEntity target) {
if (!(this.level() instanceof ServerLevel level)) {
return false;
}
boolean inverted = GravityApi.isGravityInverted(this);
for (int attempt = 0; attempt < STUCK_TELEPORT_ATTEMPTS; attempt++) {
double angle = this.random.nextDouble() * Mth.TWO_PI;
double radius = 22.0D + this.random.nextDouble() * 14.0D;
double x = target.getX() + Math.cos(angle) * radius;
double z = target.getZ() + Math.sin(angle) * radius;
double y = inverted
? target.getY() + target.getBbHeight() - this.getBbHeight()
: this.groundYBelow(x, z, target.getY() + 1.0D)
+ (this.isRoyalFlying() ? FLYING_PREFERRED_HOVER : 0.1D);
AABB box = this.getBoundingBox().move(x - this.getX(), y - this.getY(), z - this.getZ());
if (!level.noCollision(this, box) || level.containsAnyLiquid(box)) {
continue;
}
this.sendRoyalRelocationBurst(level);
this.getNavigation().stop();
this.teleportTo(x, y, z);
this.setDeltaMovement(Vec3.ZERO);
this.sendRoyalRelocationBurst(level);
level.playSound(null, x, y, z, SoundEvents.ENDERMAN_TELEPORT, SoundSource.HOSTILE, 3.0F, 0.55F);
return true;
}
return false;
}
private void sendRoyalRelocationBurst(ServerLevel level) {
level.sendParticles(ParticleTypes.REVERSE_PORTAL,
this.getX(), this.getY() + this.getBbHeight() * 0.5D, this.getZ(),
80, this.getBbWidth() * 0.3D, this.getBbHeight() * 0.3D, this.getBbWidth() * 0.3D, 0.2D);
}
private double royalDeadZoneRadius() {
return this.getBbWidth() * 0.5D + WING_BUFFET_EXTRA_RADIUS;
}
private boolean isInRoyalDeadZone(LivingEntity living) {
Vec3 local = GravityRotationUtil.vecWorldToPlayer(living.position().subtract(this.position()),
GravityApi.getGravityDirection(this));
double radius = this.royalDeadZoneRadius();
if (local.x * local.x + local.z * local.z > radius * radius
|| local.y < -WING_BUFFET_BELOW
|| local.y > this.getBbHeight() + WING_BUFFET_ABOVE) {
return false;
}
for (RoyalHead head : this.heads) {
if (this.headWithinBiteReach(head, living)) {
return false;
}
}
return true;
}
private boolean hasRoyalDeadZoneIntruder(ServerLevel level, LivingEntity primaryTarget) {
if (!(primaryTarget instanceof Player) && this.isInRoyalDeadZone(primaryTarget)) {
return true;
}
AABB area = this.getBoundingBox().inflate(WING_BUFFET_EXTRA_RADIUS + 1.0D, WING_BUFFET_BELOW,
WING_BUFFET_EXTRA_RADIUS + 1.0D);
for (Player player : level.getEntitiesOfClass(Player.class, area,
candidate -> candidate.isAlive() && !candidate.isSpectator() && !candidate.isCreative())) {
if (this.canDamageWithRoyalAttack(player) && this.isInRoyalDeadZone(player)) {
return true;
}
}
return false;
}
private void tickRoyalCloseRangeAnswer(LivingEntity primaryTarget) {
if (!(this.level() instanceof ServerLevel level)) {
return;
}
boolean intruder = this.hasRoyalDeadZoneIntruder(level, primaryTarget);
this.royalDeadZoneTicks = intruder ? this.royalDeadZoneTicks + 1 : Math.max(0, this.royalDeadZoneTicks - 2);
Phase phase = this.phase();
if (this.royalDeadZoneTicks < this.royalDeadZoneTriggerTicks(phase)
|| this.recoveryWindowTicks > 0
|| !this.attackScheduler.ready(WING_BUFFET_ID, RoyalAttackLane.BODY)) {
return;
}
if (this.beginRoyalAttack(WING_BUFFET_ID, RoyalAttackLane.BODY, this.royalWingBuffetCooldownTicks(phase),
WING_BUFFET_WINDUP_TICKS, 1, this.animationRecovery(55, WING_BUFFET_WINDUP_TICKS, 1, 12),
new RoyalAttackScheduler.Action() {
@Override
public void onStart() {
RoyalBossEntity.this.triggerAnim("wing_action", "wing_gust");
RoyalBossEntity.this.playRoyalSound(RoyalBossEntity.this.royalWingBuffetSound(), 0.75F);
}
@Override
public void onWindup(int elapsedTicks, int remainingTicks) {
RoyalBossEntity.this.sendRoyalWingBuffetTelegraph(level, elapsedTicks);
}
@Override
public void onActive(int elapsedTicks) {
RoyalBossEntity.this.performRoyalWingBuffet(level);
}
})) {
this.royalDeadZoneTicks = 0;
}
}
private void sendRoyalWingBuffetTelegraph(ServerLevel level, int elapsedTicks) {
if (elapsedTicks % 2 != 0) {
return;
}
double progress = Math.min(1.0D, elapsedTicks / (double) WING_BUFFET_WINDUP_TICKS);
double radius = Mth.lerp(progress, this.getBbWidth() * 0.3D, this.royalDeadZoneRadius());
var gravity = GravityApi.getGravityDirection(this);
for (int index = 0; index < 40; index++) {
double angle = Mth.TWO_PI * index / 40.0D;
Vec3 point = this.position().add(GravityRotationUtil.vecPlayerToWorld(
Math.cos(angle) * radius, 0.3D, Math.sin(angle) * radius, gravity));
level.sendParticles(ParticleTypes.CLOUD, point.x, point.y, point.z, 1, 0.0D, 0.02D, 0.0D, 0.0D);
}
}
private void performRoyalWingBuffet(ServerLevel level) {
float damage = (float) (this.getAttributeValue(Attributes.ATTACK_DAMAGE) * WING_BUFFET_DAMAGE_SCALE);
DamageSource source = this.damageSources().mobAttack(this);
var gravity = GravityApi.getGravityDirection(this);
double radius = this.royalDeadZoneRadius() + 2.0D;
AABB area = this.getBoundingBox().inflate(WING_BUFFET_EXTRA_RADIUS + 3.0D, WING_BUFFET_BELOW + 2.0D,
WING_BUFFET_EXTRA_RADIUS + 3.0D);
for (LivingEntity living : level.getEntitiesOfClass(LivingEntity.class, area,
entity -> entity.isAlive() && entity != this && this.canDamageWithRoyalAttack(entity))) {
if (living instanceof Player player && (player.isCreative() || player.isSpectator())) {
continue;
}
Vec3 local = GravityRotationUtil.vecWorldToPlayer(living.position().subtract(this.position()), gravity);
double horizontalSqr = local.x * local.x + local.z * local.z;
if (horizontalSqr > radius * radius
|| local.y < -WING_BUFFET_BELOW - 2.0D
|| local.y > this.getBbHeight() + WING_BUFFET_ABOVE + 2.0D) {
continue;
}
living.hurt(source, damage);
if (living instanceof RoyalBossEntity) {
continue;
}
Vec3 outwardLocal = horizontalSqr > 1.0E-4D
? new Vec3(local.x, 0.0D, local.z).normalize()
: new Vec3(1.0D, 0.0D, 0.0D);
Vec3 outward = GravityRotationUtil.vecPlayerToWorld(outwardLocal, gravity);
Vec3 lift = GravityRotationUtil.vecPlayerToWorld(0.0D, WING_BUFFET_LIFT, 0.0D,
GravityApi.getGravityDirection(living));
this.addGravityAwareImpulse(living, outward.scale(WING_BUFFET_PUSH).add(lift));
living.hasImpulse = true;
living.hurtMarked = true;
}
for (int ring = 1; ring <= 3; ring++) {
double ringRadius = this.getBbWidth() * 0.22D * ring;
int samples = 16 + ring * 8;
for (int sample = 0; sample < samples; sample++) {
double angle = Mth.TWO_PI * sample / samples;
Vec3 point = this.position().add(GravityRotationUtil.vecPlayerToWorld(
Math.cos(angle) * ringRadius, 1.0D, Math.sin(angle) * ringRadius, gravity));
Vec3 velocity = GravityRotationUtil.vecPlayerToWorld(
Math.cos(angle) * 0.45D, 0.04D, Math.sin(angle) * 0.45D, gravity);
level.sendParticles(ParticleTypes.CLOUD, point.x, point.y, point.z,
0, velocity.x, velocity.y, velocity.z, 1.0D);
}
}
level.sendParticles(ParticleTypes.SWEEP_ATTACK, this.getX(), this.getY() + 2.0D, this.getZ(),
16, this.getBbWidth() * 0.3D, 1.0D, this.getBbWidth() * 0.3D, 0.0D);
this.playRoyalSound(this.royalWingBuffetSound(), 0.6F);
}
private void tickRoyalConcealedTarget(LivingEntity target) {
if (!(this.level() instanceof ServerLevel level) || this.tickCount % 10 != 0) {
return;
}
if (this.distanceToSqr(target) > EXCAVATION_RANGE * EXCAVATION_RANGE || this.royalCanSeeTarget(target)) {
this.royalConcealedTicks = 0;
return;
}
this.royalConcealedTicks += 10;
if (this.royalConcealedTicks < EXCAVATION_TRIGGER_TICKS
|| this.recoveryWindowTicks > 0
|| !this.attackScheduler.ready(EXCAVATION_ID, RoyalAttackLane.SUMMON)) {
return;
}
RoyalHead head = this.heads[RoyalHead.Slot.CENTER.ordinal()];
LivingEntity committedTarget = target;
if (this.beginRoyalAttack(EXCAVATION_ID, RoyalAttackLane.SUMMON, EXCAVATION_COOLDOWN_TICKS,
EXCAVATION_WINDUP_TICKS, 1, 10, new RoyalAttackScheduler.Action() {
@Override
public void onStart() {
if (head.readyToAttack()) {
RoyalBossEntity.this.triggerAnim(head.slot().controllerName(), "shoot");
}
RoyalBossEntity.this.playRoyalSound(RoyalBossEntity.this.royalBeamStartSound(head), 0.8F);
}
@Override
public void onWindup(int elapsedTicks, int remainingTicks) {
if (committedTarget.isAlive() && committedTarget.level() == level && elapsedTicks % 2 == 0) {
RoyalBossEntity.this.sendRoyalExcavationTelegraph(level, committedTarget);
}
}
@Override
public void onActive(int elapsedTicks) {
RoyalBossEntity.this.performRoyalExcavation(level, committedTarget);
}
})) {
this.royalConcealedTicks = 0;
}
}
private boolean royalCanSeeTarget(LivingEntity target) {
Vec3 targetEye = target.getEyePosition();
if (this.clearRoyalSightLine(this.getEyePosition(), targetEye)) {
return true;
}
for (RoyalHead head : this.heads) {
if (this.clearRoyalSightLine(this.headAnchor(head), targetEye)) {
return true;
}
}
return false;
}
private boolean clearRoyalSightLine(Vec3 from, Vec3 to) {
return this.level().clip(new ClipContext(from, to, ClipContext.Block.COLLIDER, ClipContext.Fluid.NONE, this))
.getType() == HitResult.Type.MISS;
}
private void sendRoyalExcavationTelegraph(ServerLevel level, LivingEntity target) {
Vec3 origin = this.headAnchor(this.heads[RoyalHead.Slot.CENTER.ordinal()]);
Vec3 targetCenter = target.position().add(0.0D, target.getBbHeight() * 0.5D, 0.0D);
Vec3 line = targetCenter.subtract(origin);
double length = line.length();
if (length > 1.0E-3D) {
Vec3 direction = line.scale(1.0D / length);
for (double walked = 0.0D; walked <= length; walked += 2.0D) {
Vec3 point = origin.add(direction.scale(walked));
level.sendParticles(ParticleTypes.ELECTRIC_SPARK, point.x, point.y, point.z, 1, 0.05D, 0.05D, 0.05D, 0.0D);
}
}
for (int index = 0; index < 20; index++) {
double angle = Mth.TWO_PI * index / 20.0D;
level.sendParticles(ParticleTypes.END_ROD,
targetCenter.x + Math.cos(angle) * EXCAVATION_RADIUS, targetCenter.y,
targetCenter.z + Math.sin(angle) * EXCAVATION_RADIUS,
1, 0.0D, 0.0D, 0.0D, 0.0D);
}
}
private void performRoyalExcavation(ServerLevel level, LivingEntity target) {
if (!target.isAlive() || target.level() != level) {
return;
}
Vec3 targetCenter = target.position().add(0.0D, target.getBbHeight() * 0.5D, 0.0D);
Vec3 origin = this.headAnchor(this.heads[RoyalHead.Slot.CENTER.ordinal()]);
Vec3 toward = origin.subtract(targetCenter);
double length = toward.length();
Vec3 direction = length > 1.0E-4D ? toward.scale(1.0D / length) : Vec3.ZERO;
double depth = Math.min(length, EXCAVATION_DEPTH);
for (double step = 1.5D; step <= depth; step += EXCAVATION_RADIUS) {
Vec3 center = targetCenter.add(direction.scale(step));
RoyalBlockDestruction.destroySphere(level, this, center, EXCAVATION_RADIUS, EXCAVATION_MAX_BLOCKS,
OBSTRUCTION_CLEAR_MAX_RESISTANCE, OBSTRUCTION_CLEAR_DROP_CHANCE);
level.sendParticles(ParticleTypes.EXPLOSION, center.x, center.y, center.z, 1, 0.4D, 0.4D, 0.4D, 0.0D);
}
level.playSound(null, targetCenter.x, targetCenter.y, targetCenter.z,
SoundEvents.GENERIC_EXPLODE, SoundSource.HOSTILE, 2.0F, 0.7F);
if (!this.royalCanSeeTarget(target)) {
target.hurt(this.damageSources().indirectMagic(this, this),
this.scaleRoyalDamage(EXCAVATION_CONCEALED_DAMAGE));
}
}
@Nullable
final Vec3 royalFacingPoint() {
if (this.level().isClientSide || this.isDeadOrDying() || this.phaseTransitionTicks > 0
|| GravityApi.isGravityInverted(this)) {
return null;
}
LivingEntity target = this.getTarget();
if (target == null || !target.isAlive() || !this.canAttack(target) || !this.royalShouldFaceTarget(target)) {
return null;
}
return target.position();
}
final void turnRoyalBodyToward(Vec3 point) {
double dx = point.x - this.getX();
double dz = point.z - this.getZ();
if (dx * dx + dz * dz < 1.0E-4D) {
return;
}
float desiredYaw = (float) (Mth.atan2(dz, dx) * Mth.RAD_TO_DEG) - 90.0F;
float turn = Mth.clamp(Mth.wrapDegrees(desiredYaw - this.getYRot()), -FACING_TURN_RATE, FACING_TURN_RATE);
this.setYRot(this.getYRot() + turn);
this.yBodyRot = this.getYRot();
}
private static void applyRelativeInput(Mob mob, double dx, double dz, float speed) {
double length = Math.sqrt(dx * dx + dz * dz);
double yaw = mob.getYRot() * Mth.DEG_TO_RAD;
double nx = dx / length;
double nz = dz / length;
float forward = (float) (-nx * Math.sin(yaw) + nz * Math.cos(yaw));
float strafe = (float) (nx * Math.cos(yaw) + nz * Math.sin(yaw));
mob.setSpeed(speed);
mob.setZza(forward * speed);
mob.setXxa(strafe * speed);
}
private static final class RoyalGroundMoveControl extends MoveControl {
private final RoyalBossEntity boss;
private RoyalGroundMoveControl(RoyalBossEntity boss) {
super(boss);
this.boss = boss;
}
@Override
public void setWantedPosition(double x, double y, double z, double speed) {
super.setWantedPosition(x, y, z, speed);
this.boss.markRoyalMoveRequested();
}
@Override
public void tick() {
Vec3 facing = this.boss.royalFacingPoint();
if (facing == null) {
super.tick();
return;
}
if (this.operation != Operation.MOVE_TO) {
boolean waiting = this.operation == Operation.WAIT;
super.tick();
if (waiting) {
this.mob.setXxa(0.0F);
}
this.boss.turnRoyalBodyToward(facing);
return;
}
this.operation = Operation.WAIT;
double dx = this.wantedX - this.mob.getX();
double dy = this.wantedY - this.mob.getY();
double dz = this.wantedZ - this.mob.getZ();
double horizontalSqr = dx * dx + dz * dz;
this.boss.turnRoyalBodyToward(facing);
if (horizontalSqr < 2.5E-7D) {
this.mob.setZza(0.0F);
this.mob.setXxa(0.0F);
return;
}
float speed = (float) (this.speedModifier * this.mob.getAttributeValue(Attributes.MOVEMENT_SPEED));
applyRelativeInput(this.mob, dx, dz, speed);
if (dy > this.mob.maxUpStep() && horizontalSqr < Math.max(1.0F, this.mob.getBbWidth())) {
this.mob.getJumpControl().jump();
this.operation = Operation.JUMPING;
}
}
}
private static final class RoyalFlyingMoveControl extends FlyingMoveControl {
private final RoyalBossEntity boss;
private final int maxPitchTurn;
private RoyalFlyingMoveControl(RoyalBossEntity boss, int maxTurn) {
super(boss, maxTurn, true);
this.boss = boss;
this.maxPitchTurn = maxTurn;
}
@Override
public void setWantedPosition(double x, double y, double z, double speed) {
super.setWantedPosition(x, y, z, speed);
this.boss.markRoyalMoveRequested();
}
@Override
public void tick() {
Vec3 facing = this.boss.royalFacingPoint();
if (facing == null) {
super.tick();
return;
}
if (this.operation != Operation.MOVE_TO) {
super.tick();
this.mob.setXxa(0.0F);
this.boss.turnRoyalBodyToward(facing);
return;
}
this.operation = Operation.WAIT;
this.mob.setNoGravity(true);
double dx = this.wantedX - this.mob.getX();
double dy = this.wantedY - this.mob.getY();
double dz = this.wantedZ - this.mob.getZ();
this.boss.turnRoyalBodyToward(facing);
if (dx * dx + dy * dy + dz * dz < 2.5E-7D) {
this.mob.setYya(0.0F);
this.mob.setZza(0.0F);
this.mob.setXxa(0.0F);
return;
}
float speed = (float) (this.speedModifier * this.mob.getAttributeValue(
this.mob.onGround() ? Attributes.MOVEMENT_SPEED : Attributes.FLYING_SPEED));
double horizontal = Math.sqrt(dx * dx + dz * dz);
if (horizontal > 1.0E-5D) {
applyRelativeInput(this.mob, dx, dz, speed);
} else {
this.mob.setSpeed(speed);
this.mob.setZza(0.0F);
this.mob.setXxa(0.0F);
}
float pitch = (float) (-(Mth.atan2(dy, horizontal) * Mth.RAD_TO_DEG));
this.mob.setXRot(this.rotlerp(this.mob.getXRot(), pitch, this.maxPitchTurn));
this.mob.setYya(dy > 0.0D ? speed : -speed);
}
}
protected double combatStandoffDistance() {
return Math.max(this.getBbWidth() * 0.55D,
BEAM_MUZZLE_FORWARD - this.biteReach() * 0.65D + 1.0D);
@@ -965,8 +1577,9 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
boolean inverted = GravityApi.isGravityInverted(this);
double groundY = this.groundYBelow(this.getX(), this.getZ());
if (this.landingForCombat) {
if (!target.onGround()) {
if (!target.onGround() || ++this.landingForCombatTicks > LANDING_FOR_COMBAT_TIMEOUT_TICKS) {
this.landingForCombat = false;
this.landingForCombatTicks = 0;
this.locomotionDecisionTicks = 60;
} else if (this.onGround() || !inverted && this.getY() <= groundY + 1.0D) {
this.landingForCombat = false;
@@ -983,6 +1596,7 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
this.locomotionDecisionTicks = 60 + this.random.nextInt(80);
if (target.onGround() && this.random.nextDouble() < this.groundCombatBias()) {
this.landingForCombat = true;
this.landingForCombatTicks = 0;
}
} else if (!target.onGround() || this.random.nextDouble() >= this.groundCombatBias()) {
this.setRoyalFlying(true);
@@ -1009,8 +1623,61 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
}
protected double groundYBelow(double x, double z) {
return this.level().getHeight(net.minecraft.world.level.levelgen.Heightmap.Types.MOTION_BLOCKING_NO_LEAVES,
Mth.floor(x), Mth.floor(z));
return this.groundYBelow(x, z, this.getY() + 1.0D);
}
protected double groundYBelow(double x, double z, double referenceY) {
Level level = this.level();
int blockX = Mth.floor(x);
int blockZ = Mth.floor(z);
int surface = level.getHeight(net.minecraft.world.level.levelgen.Heightmap.Types.MOTION_BLOCKING_NO_LEAVES,
blockX, blockZ);
int startY = Mth.floor(referenceY);
if (startY >= surface) {
return surface;
}
net.minecraft.core.BlockPos.MutableBlockPos cursor = new net.minecraft.core.BlockPos.MutableBlockPos(blockX, startY, blockZ);
if (this.isRoyalGroundBlock(level, cursor)) {
int top = Math.min(surface, startY + GROUND_SCAN_RISE);
for (int y = startY + 1; y <= top; y++) {
cursor.setY(y);
if (!this.isRoyalGroundBlock(level, cursor)) {
return y;
}
}
return surface;
}
int bottom = Math.max(level.getMinBuildHeight(), startY - GROUND_SCAN_DEPTH);
for (int y = startY - 1; y >= bottom; y--) {
cursor.setY(y);
if (this.isRoyalGroundBlock(level, cursor)) {
return y + 1;
}
}
return bottom;
}
protected double ceilingYAbove(double x, double z, double referenceY) {
Level level = this.level();
net.minecraft.core.BlockPos.MutableBlockPos cursor = new net.minecraft.core.BlockPos.MutableBlockPos(
Mth.floor(x), Mth.floor(referenceY), Mth.floor(z));
int startY = cursor.getY();
int top = Math.min(level.getMaxBuildHeight() - 1, startY + GROUND_SCAN_DEPTH);
for (int y = startY; y <= top; y++) {
cursor.setY(y);
if (this.isRoyalGroundBlock(level, cursor)) {
return y;
}
}
return top;
}
private boolean isRoyalGroundBlock(Level level, net.minecraft.core.BlockPos pos) {
net.minecraft.world.level.block.state.BlockState state = level.getBlockState(pos);
if (state.isAir() || state.is(net.minecraft.tags.BlockTags.LEAVES)) {
return false;
}
return !state.getFluidState().isEmpty() || !state.getCollisionShape(level, pos).isEmpty();
}
private void tickFlyingAltitude() {
@@ -1022,6 +1689,10 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
}
double groundY = this.groundYBelow(this.getX(), this.getZ());
double ceiling = groundY + FLYING_MAX_HOVER_ABOVE_GROUND;
LivingEntity target = this.getTarget();
if (target != null && target.isAlive()) {
ceiling = Math.max(ceiling, target.getY() + 8.0D);
}
if (this.getY() > ceiling) {
double desiredY = groundY + FLYING_PREFERRED_HOVER;
this.getMoveControl().setWantedPosition(this.getX(), desiredY, this.getZ(), 1.0D);
@@ -1032,15 +1703,33 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
}
}
private boolean isFightingRoyalRival() {
return this.getTarget() instanceof RoyalBossEntity;
}
private void tickBodyCrush() {
if (!(this.level() instanceof ServerLevel serverLevel) || this.tickCount % 10 != 0) {
if (!(this.level() instanceof ServerLevel serverLevel)
|| this.tickCount % (this.isFightingRoyalRival() ? RIVAL_BODY_CRUSH_INTERVAL_TICKS : BODY_CRUSH_INTERVAL_TICKS) != 0) {
return;
}
if (this.getDeltaMovement().horizontalDistanceSqr() < 0.0016D) {
Vec3 horizontalMovement = this.getDeltaMovement().multiply(1.0D, 0.0D, 1.0D);
boolean moving = horizontalMovement.lengthSqr() >= 0.0016D;
if (!moving && !this.horizontalCollision) {
return;
}
double yaw = this.yBodyRot * Mth.DEG_TO_RAD;
Vec3 forward = new Vec3(-Math.sin(yaw), 0.0D, Math.cos(yaw));
Vec3 forward;
if (moving) {
forward = horizontalMovement.normalize();
} else {
Vec3 toWanted = new Vec3(this.getMoveControl().getWantedX() - this.getX(), 0.0D,
this.getMoveControl().getWantedZ() - this.getZ());
if (toWanted.lengthSqr() > 1.0E-4D) {
forward = toWanted.normalize();
} else {
double yaw = this.yBodyRot * Mth.DEG_TO_RAD;
forward = new Vec3(-Math.sin(yaw), 0.0D, Math.cos(yaw));
}
}
Vec3 center = new Vec3(
this.getX() + forward.x * (this.getBbWidth() * 0.45D),
this.getY() + (this.isRoyalFlying() ? 0.0D : 1.0D),
@@ -1163,11 +1852,30 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
protected Vec3 beamDirection(RoyalHead head) {
float yaw = this.getYRot() * Mth.DEG_TO_RAD;
Vec3 localDirection = new Vec3(-Math.sin(yaw), 0.0D, Math.cos(yaw));
double pitch = this.royalBeamPitch(head);
double horizontal = Math.cos(pitch);
Vec3 localDirection = new Vec3(-Math.sin(yaw) * horizontal, Math.sin(pitch), Math.cos(yaw) * horizontal);
return GravityRotationUtil.vecPlayerToWorld(localDirection,
GravityApi.getGravityDirection(this)).normalize();
}
private double royalBeamPitch(RoyalHead head) {
if (this.level().isClientSide) {
return 0.0D;
}
LivingEntity target = head.target(this.level());
if (target == null) {
return 0.0D;
}
Vec3 local = GravityRotationUtil.vecWorldToPlayer(target.getEyePosition().subtract(this.beamAnchor(head)),
GravityApi.getGravityDirection(this));
double horizontal = Math.sqrt(local.x * local.x + local.z * local.z);
if (horizontal < 1.0E-3D) {
return 0.0D;
}
return Mth.clamp(Math.atan2(local.y, horizontal), -MAX_BEAM_PITCH, MAX_BEAM_PITCH);
}
protected boolean targetInBeamLine(RoyalHead head, LivingEntity target) {
Vec3 origin = this.beamAnchor(head);
Vec3 direction = this.beamDirection(head);
@@ -131,6 +131,25 @@ public class RoyalElementalProjectileEntity extends ThrowableProjectile implemen
}
}
@Override
protected boolean canHitEntity(Entity target) {
Entity owner = this.getOwner();
if (owner != null && target == owner) {
return false;
}
if (owner instanceof com.craisinlord.anthology.multipart.MultipartEntityOwner multipart) {
Entity[] parts = multipart.anthology$getMultipartParts();
if (parts != null) {
for (Entity part : parts) {
if (part == target) {
return false;
}
}
}
}
return super.canHitEntity(target);
}
@Override
protected void onHitEntity(EntityHitResult result) {
if (!this.level().isClientSide && this.level() instanceof ServerLevel level) {
@@ -100,7 +100,8 @@ public final class RoyalAttackScheduler {
int total = 0;
int[] weights = new int[ids.length];
for (int i = 0; i < ids.length; i++) {
weights[i] = Math.max(0, weightFunction.applyAsInt(ids[i]) - repetitionPenalty(ids[i]));
int baseWeight = weightFunction.applyAsInt(ids[i]);
weights[i] = baseWeight > 0 ? Math.max(1, baseWeight - repetitionPenalty(ids[i])) : 0;
total += weights[i];
}
if (total <= 0) {
@@ -1,6 +1,7 @@
package com.craisinlord.antarchy.content.portal;
import com.craisinlord.antarchy.config.AntarchySettings;
import com.craisinlord.antarchy.content.entity.ant.AntTeleportHelper;
import java.util.Comparator;
import java.util.LinkedHashSet;
import java.util.List;
@@ -14,6 +15,7 @@ import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.util.Mth;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.Mob;
import net.minecraft.world.entity.ai.village.poi.PoiManager;
import net.minecraft.world.entity.ai.village.poi.PoiRecord;
import net.minecraft.world.entity.vehicle.DismountHelper;
@@ -48,12 +50,23 @@ public final class PermanentPortalTeleporter {
entity.setPortalCooldown();
Transition transition = createTransition(sourceLevel, entity, entity.blockPosition(), type);
List<Mob> companions = entity instanceof ServerPlayer player && transition.newLevel() != sourceLevel
? AntTeleportHelper.collectCompanions(player)
: List.of();
Entity movedEntity = moveEntity(entity, transition);
if (movedEntity != null) {
movedEntity.setPortalCooldown();
if (transition.playPortalSound() && movedEntity instanceof ServerPlayer player) {
player.connection.send(new ClientboundLevelEventPacket(LevelEvent.SOUND_PORTAL_TRAVEL, BlockPos.ZERO, 0, false));
}
for (Mob companion : companions) {
if (companion.isAlive()) {
Mob movedCompanion = AntTeleportHelper.moveCompanion(companion, transition.newLevel(), transition.pos());
if (movedCompanion != null) {
movedCompanion.setPortalCooldown();
}
}
}
}
}
@@ -91,6 +104,7 @@ public final class PermanentPortalTeleporter {
return safePortalPosition != null ? safePortalPosition : existing.center();
}
AntTeleportHelper.loadArrivalSearchArea(destination, preferredPos, SEARCH_RADIUS);
Vec3 safe = findSafeArrivalPosition(entity, destination, preferredPos, type);
if (safe != null) {
PermanentPortalShape created = createReturnPortal(destination, safe, type);
@@ -100,7 +114,19 @@ public final class PermanentPortalTeleporter {
return createFallbackPortal(destination, BlockPos.containing(safe), type);
}
return null;
return createFallbackPortal(destination, fallbackArrivalPos(destination, preferredPos, type), type);
}
private static BlockPos fallbackArrivalPos(ServerLevel destination, BlockPos preferredPos, PermanentPortalType type) {
int[] yRange = getDimensionYRange(destination, type);
if (yRange != null) {
return new BlockPos(preferredPos.getX(), Mth.clamp(preferredPos.getY(), yRange[0] + 1, yRange[1] - 4), preferredPos.getZ());
}
BlockPos surface = destination.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, preferredPos);
if (surface.getY() > destination.getMinBuildHeight() + 1) {
return surface;
}
return preferredPos;
}
@Nullable
@@ -3,11 +3,14 @@ package com.craisinlord.antarchy.mixins.client;
import com.craisinlord.antarchy.content.entity.HerculesBeetleEntity;
import net.minecraft.client.Camera;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.phys.Vec3;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.ModifyArg;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(Camera.class)
public abstract class MountedVehicleCameraMixin {
@@ -17,31 +20,31 @@ public abstract class MountedVehicleCameraMixin {
@Shadow
private boolean detached;
@ModifyArg(
method = "setup",
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Camera;move(DDD)V"),
index = 0
)
private double antarchy$adjustMountedCameraX(double value) {
return this.antarchy$ridingHerculesBeetle() ? value * 1.45D : value;
}
@Shadow
protected abstract void setPosition(Vec3 position);
@ModifyArg(
method = "setup",
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Camera;move(DDD)V"),
index = 1
)
private double antarchy$adjustMountedCameraY(double value) {
return this.antarchy$ridingHerculesBeetle() ? value + 0.2D : value;
}
@Inject(method = "setup", at = @At("RETURN"))
private void antarchy$adjustMountedCamera(
BlockGetter level,
Entity entity,
boolean detached,
boolean thirdPersonReverse,
float partialTick,
CallbackInfo ci
) {
if (!this.antarchy$ridingHerculesBeetle()) {
return;
}
@ModifyArg(
method = "setup",
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Camera;move(DDD)V"),
index = 2
)
private double antarchy$adjustMountedCameraZ(double value) {
return this.antarchy$ridingHerculesBeetle() ? value + 0.45D : value;
Camera camera = (Camera) (Object) this;
Vec3 look = new Vec3(camera.getLookVector());
Vec3 cameraOffset = camera.getPosition().subtract(entity.getEyePosition(partialTick));
double zoomOffset = cameraOffset.dot(look) * 0.45D;
Vec3 adjustment = look.scale(zoomOffset)
.add(new Vec3(camera.getUpVector()).scale(0.2D))
.add(new Vec3(camera.getLeftVector()).scale(0.45D));
this.setPosition(camera.getPosition().add(adjustment));
}
@Unique
@@ -1,18 +1,17 @@
package com.craisinlord.antarchy.mixins.client;
import com.craisinlord.antarchy.content.client.PortalGunZoomClientState;
import com.llamalad7.mixinextras.injector.ModifyReturnValue;
import net.minecraft.client.Camera;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.GameRenderer;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(GameRenderer.class)
public abstract class PortalGunZoomFovMixin {
@Inject(method = "getFov", at = @At("RETURN"), cancellable = true)
private void antarchy$applyPortalGunZoom(Camera camera, float partialTick, boolean useFovSetting, CallbackInfoReturnable<Double> cir) {
cir.setReturnValue(PortalGunZoomClientState.applyFov(cir.getReturnValue(), partialTick, Minecraft.getInstance().options));
@ModifyReturnValue(method = "getFov", at = @At("RETURN"))
private double antarchy$applyPortalGunZoom(double fov, Camera camera, float partialTick, boolean useFovSetting) {
return PortalGunZoomClientState.applyFov(fov, partialTick, Minecraft.getInstance().options);
}
}
@@ -57,7 +57,7 @@
{"origin": [2.5, 3.7, -9.47], "size": [0, 5, 3], "uv": [26, 27]},
{"origin": [-2.5, 3.7, -9.47], "size": [5, 0, 3], "uv": [22, 15]},
{"origin": [-2.5, 3.7, -9.47], "size": [0, 5, 3], "uv": [20, 27]},
{"origin": [-2.5, 3.7, -6.47], "size": [5, 5, 0], "uv": [20, 22]}
{"origin": [-2.5, 3.7, -6.55], "size": [5, 5, 0], "uv": [20, 22]}
]
}
]
File diff suppressed because it is too large. Load diff
@@ -27,7 +27,7 @@
{
"type": "minecraft:item",
"name": "antarchy:cornea_ear",
"weight": 8,
"weight": 5,
"functions": [
{
"function": "minecraft:set_count",
@@ -42,7 +42,7 @@
{
"type": "minecraft:item",
"name": "antarchy:bluestone",
"weight": 10,
"weight": 6,
"functions": [
{
"function": "minecraft:set_count",
@@ -54,6 +54,36 @@
}
]
},
{
"type": "minecraft:item",
"name": "antarchy:uranium_nugget",
"weight": 4,
"functions": [
{
"function": "minecraft:set_count",
"count": {
"type": "minecraft:uniform",
"min": 2,
"max": 5
}
}
]
},
{
"type": "minecraft:item",
"name": "antarchy:titanium_nugget",
"weight": 4,
"functions": [
{
"function": "minecraft:set_count",
"count": {
"type": "minecraft:uniform",
"min": 2,
"max": 5
}
}
]
},
{
"type": "minecraft:item",
"name": "minecraft:diamond",
@@ -4,11 +4,11 @@
"pattern": [
" B",
" B ",
"N "
"I "
],
"key": {
"N": {
"item": "minecraft:netherite_ingot"
"I": {
"item": "minecraft:iron_ingot"
},
"B": {
"item": "antarchy:blood_crystal_shard"