2.2.6
This commit is contained in:
1 parent
758fa58b1b
commit
c00d57e4e0
63 files changed
+7541
-2626
No files matched your search
+5
-1
@@ -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
-1
@@ -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);
|
||||
|
||||
+14
-5
@@ -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()) {
|
||||
|
||||
+3
-3
@@ -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++) {
|
||||
|
||||
+174
-51
@@ -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);
|
||||
}
|
||||
|
||||
+97
-13
@@ -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()) {
|
||||
|
||||
+737
-29
@@ -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);
|
||||
|
||||
+19
@@ -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) {
|
||||
|
||||
+2
-1
@@ -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) {
|
||||
|
||||
+27
-1
@@ -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
|
||||
|
||||
+27
-24
@@ -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
|
||||
|
||||
+4
-5
@@ -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
+32
-2
@@ -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"
|
||||
|
||||
Reference in new issue
Block a user