63 files changed
+6992
-2077
No files matched your search
+5
-1
@@ -15,6 +15,7 @@ import java.nio.file.Path;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
public final class InfinityConfigRefresh {
|
public final class InfinityConfigRefresh {
|
||||||
private static final Map<String, Registry<?>> CHECKED_FILES = Map.of(
|
private static final Map<String, Registry<?>> CHECKED_FILES = Map.of(
|
||||||
@@ -22,6 +23,7 @@ public final class InfinityConfigRefresh {
|
|||||||
"items.json", BuiltInRegistries.ITEM,
|
"items.json", BuiltInRegistries.ITEM,
|
||||||
"effects.json", BuiltInRegistries.MOB_EFFECT
|
"effects.json", BuiltInRegistries.MOB_EFFECT
|
||||||
);
|
);
|
||||||
|
private static final Set<String> DISABLED_IDS = Set.of("antarchy:paralyzed");
|
||||||
|
|
||||||
private InfinityConfigRefresh() {
|
private InfinityConfigRefresh() {
|
||||||
}
|
}
|
||||||
@@ -43,7 +45,7 @@ public final class InfinityConfigRefresh {
|
|||||||
try {
|
try {
|
||||||
Files.delete(invocationLock);
|
Files.delete(invocationLock);
|
||||||
Antarchy.LOGGER.info(
|
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
|
staleIds
|
||||||
);
|
);
|
||||||
} catch (Exception exception) {
|
} catch (Exception exception) {
|
||||||
@@ -73,6 +75,8 @@ public final class InfinityConfigRefresh {
|
|||||||
ResourceLocation id = ResourceLocation.tryParse(key);
|
ResourceLocation id = ResourceLocation.tryParse(key);
|
||||||
if (id != null && Antarchy.MODID.equals(id.getNamespace()) && !registry.containsKey(id)) {
|
if (id != null && Antarchy.MODID.equals(id.getNamespace()) && !registry.containsKey(id)) {
|
||||||
staleIds.add(key);
|
staleIds.add(key);
|
||||||
|
} else if (DISABLED_IDS.contains(key) && (!entry.has("weight") || entry.get("weight").getAsDouble() > 0.0D)) {
|
||||||
|
staleIds.add(key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (Exception exception) {
|
} catch (Exception exception) {
|
||||||
|
|||||||
+11
-1
@@ -11,6 +11,7 @@ import net.minecraft.util.Mth;
|
|||||||
import net.minecraft.util.RandomSource;
|
import net.minecraft.util.RandomSource;
|
||||||
|
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
public final class QueenMusicHandler {
|
public final class QueenMusicHandler {
|
||||||
private static final double START_RADIUS = 256.0D;
|
private static final double START_RADIUS = 256.0D;
|
||||||
@@ -25,7 +26,7 @@ public final class QueenMusicHandler {
|
|||||||
return music != null;
|
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()
|
if (minecraft.level == null || minecraft.player == null || minecraft.isPaused()
|
||||||
|| !minecraft.player.isAlive() || minecraft.player.isRemoved()
|
|| !minecraft.player.isAlive() || minecraft.player.isRemoved()
|
||||||
|| minecraft.player.isDeadOrDying() || minecraft.screen instanceof DeathScreen) {
|
|| minecraft.player.isDeadOrDying() || minecraft.screen instanceof DeathScreen) {
|
||||||
@@ -51,6 +52,15 @@ public final class QueenMusicHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (music == null) {
|
if (music == null) {
|
||||||
|
SoundEvent soundEvent;
|
||||||
|
try {
|
||||||
|
soundEvent = soundEventSupplier.get();
|
||||||
|
} catch (RuntimeException exception) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (soundEvent == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
minecraft.getMusicManager().stopPlaying();
|
minecraft.getMusicManager().stopPlaying();
|
||||||
music = new QueenMusicSound(soundEvent);
|
music = new QueenMusicSound(soundEvent);
|
||||||
minecraft.getSoundManager().play(music);
|
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() {
|
private void startMountedFlight() {
|
||||||
this.pendingChargeStart = false;
|
this.pendingChargeStart = false;
|
||||||
this.setFlying(true);
|
this.setFlying(true);
|
||||||
@@ -1169,6 +1165,19 @@ public class HerculesBeetleEntity extends TamableAnimal implements GeoEntity, Fl
|
|||||||
&& this.getControllingPassenger() == player;
|
&& 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
|
@Override
|
||||||
@Nullable
|
@Nullable
|
||||||
public LivingEntity getControllingPassenger() {
|
public LivingEntity getControllingPassenger() {
|
||||||
@@ -1184,7 +1193,7 @@ public class HerculesBeetleEntity extends TamableAnimal implements GeoEntity, Fl
|
|||||||
if (this.hasPassenger(passenger)) {
|
if (this.hasPassenger(passenger)) {
|
||||||
Vec3 localForward = this.toLocalPlane(this.getLookAngle());
|
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);
|
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 yOffset = mountedFlightPose ? 5.3D : 3.0D;
|
||||||
double forwardOffset = mountedFlightPose ? 2.2D : 1.25D;
|
double forwardOffset = mountedFlightPose ? 2.2D : 1.25D;
|
||||||
if (!this.isFlying()) {
|
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<>();
|
List<Mob> companions = new ArrayList<>();
|
||||||
if (player.getVehicle() instanceof Mob vehicle) {
|
if (player.getVehicle() instanceof Mob vehicle) {
|
||||||
companions.add(vehicle);
|
companions.add(vehicle);
|
||||||
@@ -95,7 +95,7 @@ public final class AntTeleportHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private static Mob moveCompanion(Mob companion, ServerLevel destination, Vec3 destinationPos) {
|
public static Mob moveCompanion(Mob companion, ServerLevel destination, Vec3 destinationPos) {
|
||||||
companion.stopRiding();
|
companion.stopRiding();
|
||||||
companion.ejectPassengers();
|
companion.ejectPassengers();
|
||||||
companion.getNavigation().stop();
|
companion.getNavigation().stop();
|
||||||
@@ -400,7 +400,7 @@ public final class AntTeleportHelper {
|
|||||||
return null;
|
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);
|
ChunkPos center = new ChunkPos(preferredPos);
|
||||||
int chunkRadius = (searchRadius >> 4) + 1;
|
int chunkRadius = (searchRadius >> 4) + 1;
|
||||||
for (int chunkX = center.x - chunkRadius; chunkX <= center.x + chunkRadius; chunkX++) {
|
for (int chunkX = center.x - chunkRadius; chunkX <= center.x + chunkRadius; chunkX++) {
|
||||||
|
|||||||
+171
-48
@@ -131,7 +131,13 @@ public class KingEntity extends RoyalBossEntity {
|
|||||||
private static final int COMBAT_WAYPOINT_VARIANCE = 35;
|
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_MIN_TICKS = 18;
|
||||||
private static final int GROUND_COMBAT_WAYPOINT_VARIANCE = 12;
|
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
|
@Nullable
|
||||||
private Vec3 patrolCenter;
|
private Vec3 patrolCenter;
|
||||||
@Nullable
|
@Nullable
|
||||||
@@ -182,6 +188,7 @@ public class KingEntity extends RoyalBossEntity {
|
|||||||
@Nullable
|
@Nullable
|
||||||
private Vec3 kingGroundCombatWaypoint;
|
private Vec3 kingGroundCombatWaypoint;
|
||||||
private int kingStrafeDirection = 1;
|
private int kingStrafeDirection = 1;
|
||||||
|
private int kingLandingRetryTicks;
|
||||||
private int royalBoundaryGraceTicks;
|
private int royalBoundaryGraceTicks;
|
||||||
@Nullable
|
@Nullable
|
||||||
private ServerPlayer exileTarget;
|
private ServerPlayer exileTarget;
|
||||||
@@ -254,6 +261,35 @@ public class KingEntity extends RoyalBossEntity {
|
|||||||
return this.royalChargeActive;
|
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
|
@Override
|
||||||
protected void onRoyalPhaseTransitionStarted(Phase previousPhase, Phase nextPhase) {
|
protected void onRoyalPhaseTransitionStarted(Phase previousPhase, Phase nextPhase) {
|
||||||
this.attackScheduler.cancel(RoyalAttackLane.MOVEMENT);
|
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) {
|
private void chainLightning(ServerLevel level, Vec3 origin) {
|
||||||
java.util.Set<UUID> struck = new java.util.HashSet<>();
|
java.util.Set<UUID> struck = new java.util.HashSet<>();
|
||||||
Vec3 from = origin;
|
Vec3 from = origin;
|
||||||
@@ -485,7 +529,7 @@ public class KingEntity extends RoyalBossEntity {
|
|||||||
double bestScore = Double.MAX_VALUE;
|
double bestScore = Double.MAX_VALUE;
|
||||||
for (LivingEntity candidate : level.getEntitiesOfClass(LivingEntity.class,
|
for (LivingEntity candidate : level.getEntitiesOfClass(LivingEntity.class,
|
||||||
new AABB(from, from).inflate(CHAIN_LIGHTNING_JUMP_RANGE),
|
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())) {
|
if (struck.contains(candidate.getUUID())) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -565,17 +609,26 @@ public class KingEntity extends RoyalBossEntity {
|
|||||||
if (this.royalChargeOpeningDelayTicks > 0) {
|
if (this.royalChargeOpeningDelayTicks > 0) {
|
||||||
this.royalChargeOpeningDelayTicks--;
|
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()) {
|
if (this.isRoyalPhaseTransitionActive()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
LivingEntity target = this.getTarget();
|
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.trackBehavior(target);
|
||||||
this.tickDecree(level, target);
|
this.tickDecree(level, target);
|
||||||
this.tickKingCombatMovement(target);
|
this.tickKingCombatMovement(target);
|
||||||
if (!this.blocksRoyalAttacksForMovement()) {
|
if (!this.blocksRoyalAttacksForMovement()) {
|
||||||
this.tickKingWholeBody(level, target);
|
this.tickKingWholeBody(level, target);
|
||||||
}
|
}
|
||||||
|
this.applyRoyalBoundaryPull();
|
||||||
this.stabilizeKingRotation();
|
this.stabilizeKingRotation();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -583,11 +636,15 @@ public class KingEntity extends RoyalBossEntity {
|
|||||||
if (this.isDeadOrDying() || !(this.level() instanceof ServerLevel)) {
|
if (this.isDeadOrDying() || !(this.level() instanceof ServerLevel)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (this.activeDecree != null) {
|
||||||
|
this.endDecree(null);
|
||||||
|
}
|
||||||
this.setRoyalFlying(true);
|
this.setRoyalFlying(true);
|
||||||
this.resetKingCombatMovement();
|
this.resetKingCombatMovement();
|
||||||
this.decreeRetreatPressure = false;
|
this.decreeRetreatPressure = false;
|
||||||
if (this.treePatrolBound && this.treePatrolCenter != null) {
|
if (this.treePatrolBound && this.treePatrolCenter != null) {
|
||||||
this.tickTreePatrol();
|
this.tickTreePatrol();
|
||||||
|
this.applyRoyalBoundaryPull();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (this.patrolCenter == null || this.position().distanceToSqr(this.patrolCenter) > 64.0D * 64.0D) {
|
if (this.patrolCenter == null || this.position().distanceToSqr(this.patrolCenter) > 64.0D * 64.0D) {
|
||||||
@@ -667,17 +724,6 @@ public class KingEntity extends RoyalBossEntity {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
double boundaryRadius = Math.max(32.0D, AntarchySettings.royalBoundaryRadius());
|
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();
|
LivingEntity target = this.getTarget();
|
||||||
if (target == null || !target.isAlive()) {
|
if (target == null || !target.isAlive()) {
|
||||||
this.royalBoundaryGraceTicks = 0;
|
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() {
|
private double horizontalDistanceToTreeSqr() {
|
||||||
if (this.treePatrolCenter == null) {
|
if (this.treePatrolCenter == null) {
|
||||||
return 0.0D;
|
return 0.0D;
|
||||||
@@ -793,9 +858,14 @@ public class KingEntity extends RoyalBossEntity {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
boolean closeQuarters = this.activeDecree instanceof CloseQuartersDecree;
|
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);
|
this.beginKingLanding(target);
|
||||||
} else if (!closeQuarters && this.isRoyalFlying() && this.kingLandingStage == KingLandingStage.AERIAL
|
} else if (!closeQuarters && this.isRoyalFlying() && this.kingLandingStage == KingLandingStage.AERIAL
|
||||||
|
&& this.kingLandingRetryTicks <= 0
|
||||||
&& this.groundAssaultCooldownTicks-- <= 0 && target.onGround()) {
|
&& this.groundAssaultCooldownTicks-- <= 0 && target.onGround()) {
|
||||||
this.beginKingLanding(target);
|
this.beginKingLanding(target);
|
||||||
}
|
}
|
||||||
@@ -807,6 +877,7 @@ public class KingEntity extends RoyalBossEntity {
|
|||||||
this.tickKingLandingIndicator();
|
this.tickKingLandingIndicator();
|
||||||
if (--this.kingLandingStageTicks <= 0) {
|
if (--this.kingLandingStageTicks <= 0) {
|
||||||
this.kingLandingStage = KingLandingStage.DESCENT;
|
this.kingLandingStage = KingLandingStage.DESCENT;
|
||||||
|
this.kingLandingStageTicks = LANDING_DESCENT_TIMEOUT_TICKS;
|
||||||
this.playRoyalSound(AntarchySoundEvents.KING_WING_FLAP.get(), 0.72F);
|
this.playRoyalSound(AntarchySoundEvents.KING_WING_FLAP.get(), 0.72F);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
@@ -829,7 +900,10 @@ public class KingEntity extends RoyalBossEntity {
|
|||||||
|| Math.abs(localLandingDelta.y) <= 1.25D && horizontalDistanceSquared <= 4.0D;
|
|| Math.abs(localLandingDelta.y) <= 1.25D && horizontalDistanceSquared <= 4.0D;
|
||||||
if (reachedLanding) {
|
if (reachedLanding) {
|
||||||
this.setRoyalFlying(false);
|
this.setRoyalFlying(false);
|
||||||
|
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.setPos(this.getX(), landing.y, this.getZ());
|
||||||
|
}
|
||||||
this.kingLandingStage = KingLandingStage.IMPACT;
|
this.kingLandingStage = KingLandingStage.IMPACT;
|
||||||
this.kingLandingStageTicks = LANDING_IMPACT_TICKS;
|
this.kingLandingStageTicks = LANDING_IMPACT_TICKS;
|
||||||
this.triggerAnim("body_action", "stomp");
|
this.triggerAnim("body_action", "stomp");
|
||||||
@@ -837,6 +911,10 @@ public class KingEntity extends RoyalBossEntity {
|
|||||||
if (this.level() instanceof ServerLevel level) {
|
if (this.level() instanceof ServerLevel level) {
|
||||||
this.performStomp(level, target);
|
this.performStomp(level, target);
|
||||||
}
|
}
|
||||||
|
} else if (--this.kingLandingStageTicks <= 0) {
|
||||||
|
this.resetKingCombatMovement();
|
||||||
|
this.kingLandingRetryTicks = LANDING_RETRY_TICKS;
|
||||||
|
this.groundAssaultCooldownTicks = Math.max(this.groundAssaultCooldownTicks, 160);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -864,8 +942,12 @@ public class KingEntity extends RoyalBossEntity {
|
|||||||
this.getMoveControl().setWantedPosition(this.kingGroundCombatWaypoint.x,
|
this.getMoveControl().setWantedPosition(this.kingGroundCombatWaypoint.x,
|
||||||
this.kingGroundCombatWaypoint.y, this.kingGroundCombatWaypoint.z, 1.25D);
|
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);
|
this.beginKingTakeoff(target);
|
||||||
|
if (targetOutOfReach) {
|
||||||
|
this.kingLandingRetryTicks = 100;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -913,6 +995,7 @@ public class KingEntity extends RoyalBossEntity {
|
|||||||
} else {
|
} else {
|
||||||
this.kingLandingWaypoint = null;
|
this.kingLandingWaypoint = null;
|
||||||
this.groundAssaultCooldownTicks = 100;
|
this.groundAssaultCooldownTicks = 100;
|
||||||
|
this.kingLandingRetryTicks = LANDING_RETRY_TICKS;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -954,7 +1037,9 @@ public class KingEntity extends RoyalBossEntity {
|
|||||||
private Vec3 findKingLandingPoint(LivingEntity target) {
|
private Vec3 findKingLandingPoint(LivingEntity target) {
|
||||||
Vec3 away = this.position().subtract(target.position()).multiply(1.0D, 0.0D, 1.0D);
|
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 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++) {
|
for (int angleIndex = 0; angleIndex < 16; angleIndex++) {
|
||||||
double angle = baseAngle + angleIndex * Mth.TWO_PI / 16.0D;
|
double angle = baseAngle + angleIndex * Mth.TWO_PI / 16.0D;
|
||||||
for (double radius : radii) {
|
for (double radius : radii) {
|
||||||
@@ -962,18 +1047,27 @@ public class KingEntity extends RoyalBossEntity {
|
|||||||
double z = target.getZ() + Math.sin(angle) * radius;
|
double z = target.getZ() + Math.sin(angle) * radius;
|
||||||
double y = GravityApi.isGravityInverted(this)
|
double y = GravityApi.isGravityInverted(this)
|
||||||
? target.position().y - 0.1D
|
? 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);
|
Vec3 candidate = new Vec3(x, y, z);
|
||||||
AABB box = this.getBoundingBox().move(
|
AABB box = this.getBoundingBox().move(
|
||||||
candidate.x - this.getX(), candidate.y - this.getY(), candidate.z - this.getZ());
|
candidate.x - this.getX(), candidate.y - this.getY(), candidate.z - this.getZ());
|
||||||
if (this.level().noCollision(this, box)) {
|
if (!this.level().noCollision(this, box)) {
|
||||||
return candidate;
|
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)
|
double fallbackY = GravityApi.isGravityInverted(this)
|
||||||
? target.position().y - 0.1D
|
? 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());
|
Vec3 fallback = new Vec3(target.getX(), fallbackY, target.getZ());
|
||||||
AABB fallbackBox = this.getBoundingBox().move(
|
AABB fallbackBox = this.getBoundingBox().move(
|
||||||
fallback.x - this.getX(), fallback.y - this.getY(), fallback.z - this.getZ());
|
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;
|
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) {
|
private void selectKingCombatWaypoint(LivingEntity target) {
|
||||||
Vec3 radial = this.position().subtract(target.position()).multiply(1.0D, 0.0D, 1.0D);
|
Vec3 radial = this.position().subtract(target.position()).multiply(1.0D, 0.0D, 1.0D);
|
||||||
if (radial.lengthSqr() < 1.0E-4D) {
|
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 candidateRange = range + (angleAttempt % 2 == 0 ? 0.0D : -6.0D);
|
||||||
double x = target.getX() + Math.cos(angle) * candidateRange;
|
double x = target.getX() + Math.cos(angle) * candidateRange;
|
||||||
double z = target.getZ() + Math.sin(angle) * candidateRange;
|
double z = target.getZ() + Math.sin(angle) * candidateRange;
|
||||||
double groundY = this.groundYBelow(x, z);
|
double y = this.kingAerialWaypointY(x, z, target, hoverOffset);
|
||||||
double y = Mth.clamp(target.getY() + hoverOffset,
|
|
||||||
groundY + FLYING_MIN_HOVER, groundY + FLYING_MAX_HOVER_ABOVE_GROUND);
|
|
||||||
Vec3 candidate = new Vec3(x, y, z);
|
Vec3 candidate = new Vec3(x, y, z);
|
||||||
if (treeBoundCombat) {
|
if (treeBoundCombat) {
|
||||||
double dx = x - this.treePatrolCenter.x;
|
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 angle = currentAngle + this.kingStrafeDirection * COMBAT_ORBIT_ANGLE_STEP;
|
||||||
double x = target.getX() + Math.cos(angle) * range;
|
double x = target.getX() + Math.cos(angle) * range;
|
||||||
double z = target.getZ() + Math.sin(angle) * range;
|
double z = target.getZ() + Math.sin(angle) * range;
|
||||||
double groundY = this.groundYBelow(x, z);
|
double y = this.kingAerialWaypointY(x, z, target, hoverOffset);
|
||||||
double y = Mth.clamp(target.getY() + hoverOffset,
|
|
||||||
groundY + FLYING_MIN_HOVER, groundY + FLYING_MAX_HOVER_ABOVE_GROUND);
|
|
||||||
Vec3 candidate = new Vec3(x, y, z);
|
Vec3 candidate = new Vec3(x, y, z);
|
||||||
double dx = candidate.x - this.treePatrolCenter.x;
|
double dx = candidate.x - this.treePatrolCenter.x;
|
||||||
double dz = candidate.z - this.treePatrolCenter.z;
|
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 angle = currentAngle + this.kingStrafeDirection * COMBAT_ORBIT_ANGLE_STEP;
|
||||||
double x = target.getX() + Math.cos(angle) * range;
|
double x = target.getX() + Math.cos(angle) * range;
|
||||||
double z = target.getZ() + Math.sin(angle) * range;
|
double z = target.getZ() + Math.sin(angle) * range;
|
||||||
double groundY = this.groundYBelow(x, z);
|
double y = this.kingAerialWaypointY(x, z, target, hoverOffset);
|
||||||
double y = Mth.clamp(target.getY() + hoverOffset,
|
|
||||||
groundY + FLYING_MIN_HOVER, groundY + FLYING_MAX_HOVER_ABOVE_GROUND);
|
|
||||||
orbitWaypoint = new Vec3(x, y, z);
|
orbitWaypoint = new Vec3(x, y, z);
|
||||||
if (treeBoundCombat) {
|
if (treeBoundCombat) {
|
||||||
orbitWaypoint = this.findTreePatrolWaypoint(this.treePatrolCenter, this.treePatrolAngle);
|
orbitWaypoint = this.findTreePatrolWaypoint(this.treePatrolCenter, this.treePatrolAngle);
|
||||||
@@ -1060,6 +1168,9 @@ public class KingEntity extends RoyalBossEntity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void stabilizeKingRotation() {
|
private void stabilizeKingRotation() {
|
||||||
|
if (this.royalFacingPoint() != null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
Vec3 movement = this.getDeltaMovement().multiply(1.0D, 0.0D, 1.0D);
|
Vec3 movement = this.getDeltaMovement().multiply(1.0D, 0.0D, 1.0D);
|
||||||
if (movement.lengthSqr() < 0.0025D) {
|
if (movement.lengthSqr() < 0.0025D) {
|
||||||
return;
|
return;
|
||||||
@@ -1093,16 +1204,16 @@ public class KingEntity extends RoyalBossEntity {
|
|||||||
radial = radial.normalize();
|
radial = radial.normalize();
|
||||||
Vec3 tangent = new Vec3(-radial.z * this.kingStrafeDirection, 0.0D,
|
Vec3 tangent = new Vec3(-radial.z * this.kingStrafeDirection, 0.0D,
|
||||||
radial.x * this.kingStrafeDirection);
|
radial.x * this.kingStrafeDirection);
|
||||||
double distance = 4.0D + this.random.nextDouble() * 3.0D;
|
double distance = GROUND_COMBAT_BITE_DISTANCE + this.random.nextDouble() * GROUND_COMBAT_BITE_VARIANCE;
|
||||||
Vec3 candidate = target.position().add(radial.scale(distance)).add(tangent.scale(2.5D));
|
Vec3 candidate = target.position().add(radial.scale(distance)).add(tangent.scale(4.0D));
|
||||||
Vec3 waypoint = new Vec3(candidate.x, target.getY(), candidate.z);
|
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());
|
AABB box = this.getBoundingBox().move(waypoint.x - this.getX(), waypoint.y - this.getY(), waypoint.z - this.getZ());
|
||||||
if (!this.level().noCollision(this, box)) {
|
if (!this.level().noCollision(this, box)) {
|
||||||
waypoint = target.position().add(radial.scale(distance));
|
Vec3 radialOnly = target.position().add(radial.scale(distance));
|
||||||
waypoint = new Vec3(waypoint.x, target.getY(), waypoint.z);
|
waypoint = this.kingGroundPoint(radialOnly.x, radialOnly.z, target);
|
||||||
box = this.getBoundingBox().move(waypoint.x - this.getX(), waypoint.y - this.getY(), waypoint.z - this.getZ());
|
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.kingGroundCombatWaypointTicks = GROUND_COMBAT_WAYPOINT_MIN_TICKS
|
||||||
+ this.random.nextInt(GROUND_COMBAT_WAYPOINT_VARIANCE + 1);
|
+ this.random.nextInt(GROUND_COMBAT_WAYPOINT_VARIANCE + 1);
|
||||||
if (this.random.nextInt(4) == 0) {
|
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) {
|
public void requestCloseQuartersAssault(LivingEntity target) {
|
||||||
if (target != null && target.isAlive() && this.isRoyalFlying()
|
if (target != null && target.isAlive() && this.isRoyalFlying()
|
||||||
&& this.kingLandingStage == KingLandingStage.AERIAL) {
|
&& this.kingLandingStage == KingLandingStage.AERIAL) {
|
||||||
@@ -1433,11 +1551,9 @@ public class KingEntity extends RoyalBossEntity {
|
|||||||
this.bumpBehavior(Behavior.SPRINTING, target.isSprinting() ? 1 : -1);
|
this.bumpBehavior(Behavior.SPRINTING, target.isSprinting() ? 1 : -1);
|
||||||
this.bumpBehavior(Behavior.HUGGING, distance <= 10.0D ? 1 : -1);
|
this.bumpBehavior(Behavior.HUGGING, distance <= 10.0D ? 1 : -1);
|
||||||
this.bumpBehavior(Behavior.KEEPING_FAR, distance >= 22.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
|
this.bumpBehavior(Behavior.RETREATING, distance > 20.0D && target.getDeltaMovement().horizontalDistanceSqr() > 0.02D
|
||||||
&& target.position().subtract(this.position()).dot(target.getDeltaMovement()) > 0.0D ? 1 : -1);
|
&& target.position().subtract(this.position()).dot(target.getDeltaMovement()) > 0.0D ? 1 : -1);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private void bumpBehavior(Behavior behavior, int delta) {
|
private void bumpBehavior(Behavior behavior, int delta) {
|
||||||
int updated = Math.max(0, Math.min(BEHAVIOR_SCORE_CAP, this.behaviorScores.getOrDefault(behavior, 0) + delta));
|
int updated = Math.max(0, Math.min(BEHAVIOR_SCORE_CAP, this.behaviorScores.getOrDefault(behavior, 0) + delta));
|
||||||
@@ -1708,10 +1824,6 @@ public class KingEntity extends RoyalBossEntity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void tickKingElementalProjectiles(ServerLevel level, LivingEntity target) {
|
private void tickKingElementalProjectiles(ServerLevel level, LivingEntity target) {
|
||||||
if (this.fireballCooldownTicks > 0) this.fireballCooldownTicks--;
|
|
||||||
if (this.iceballCooldownTicks > 0) this.iceballCooldownTicks--;
|
|
||||||
if (this.iceSpikeCooldownTicks > 0) this.iceSpikeCooldownTicks--;
|
|
||||||
|
|
||||||
double distance = this.distanceTo(target);
|
double distance = this.distanceTo(target);
|
||||||
String[] candidates = {"fireball", "iceball", "ice_spikes"};
|
String[] candidates = {"fireball", "iceball", "ice_spikes"};
|
||||||
String selected = this.attackScheduler.chooseWeighted(candidates, id -> {
|
String selected = this.attackScheduler.chooseWeighted(candidates, id -> {
|
||||||
@@ -1789,14 +1901,25 @@ public class KingEntity extends RoyalBossEntity {
|
|||||||
Vec3 start = this.position();
|
Vec3 start = this.position();
|
||||||
Vec3 end = target.position();
|
Vec3 end = target.position();
|
||||||
Vec3 line = end.subtract(start).multiply(1.0D, 0.0D, 1.0D);
|
Vec3 line = end.subtract(start).multiply(1.0D, 0.0D, 1.0D);
|
||||||
if (line.lengthSqr() < 1.0E-4D) return;
|
double reach = line.length();
|
||||||
line = line.normalize();
|
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);
|
Vec3 side = new Vec3(-line.z, 0.0D, line.x);
|
||||||
for (int i = 1; i <= 5; i++) {
|
double firstDistance = Math.max(4.0D, reach - 16.0D);
|
||||||
double distance = i * 4.0D;
|
for (int i = 0; i < 5; i++) {
|
||||||
|
double distance = firstDistance + i * 4.0D;
|
||||||
for (int lane = -1; lane <= 1; lane++) {
|
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)));
|
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) + 0.1D;
|
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);
|
RoyalIceSpikeEntity spike = RoyalIceSpikeEntity.create(level, new Vec3(point.x, y, point.z), this);
|
||||||
level.addFreshEntity(spike);
|
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.AttributeModifier;
|
||||||
import net.minecraft.world.entity.ai.attributes.AttributeSupplier;
|
import net.minecraft.world.entity.ai.attributes.AttributeSupplier;
|
||||||
import net.minecraft.world.entity.ai.attributes.Attributes;
|
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.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.entity.MobSpawnType;
|
||||||
import net.minecraft.world.level.GameRules;
|
import net.minecraft.world.level.GameRules;
|
||||||
import net.minecraft.world.level.Level;
|
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_VOLLEY_DELAY = 18;
|
||||||
private static final int ACCELERATION_LANDING_DELAY = 55;
|
private static final int ACCELERATION_LANDING_DELAY = 55;
|
||||||
private static final int DIRECTED_LANDING_RETRY_TICKS = 60;
|
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 manticoreSummonCooldownTicks;
|
||||||
private int gravityStompCooldownTicks;
|
private int gravityStompCooldownTicks;
|
||||||
@@ -161,6 +170,7 @@ public class QueenEntity extends RoyalBossEntity {
|
|||||||
private Vec3 queenLandingWaypoint;
|
private Vec3 queenLandingWaypoint;
|
||||||
@Nullable
|
@Nullable
|
||||||
private Vec3 queenGroundChargeDirection;
|
private Vec3 queenGroundChargeDirection;
|
||||||
|
private final java.util.Set<UUID> queenGroundChargeHits = new java.util.HashSet<>();
|
||||||
@Nullable
|
@Nullable
|
||||||
private Phase pendingPhaseSignature;
|
private Phase pendingPhaseSignature;
|
||||||
private boolean pendingPhaseTwoCrushingGravity;
|
private boolean pendingPhaseTwoCrushingGravity;
|
||||||
@@ -295,11 +305,34 @@ public class QueenEntity extends RoyalBossEntity {
|
|||||||
@Override
|
@Override
|
||||||
protected double combatStandoffDistance() {
|
protected double combatStandoffDistance() {
|
||||||
if (this.queenLandingStage == QueenLandingStage.GROUND_COMBAT && !this.isRoyalFlying()) {
|
if (this.queenLandingStage == QueenLandingStage.GROUND_COMBAT && !this.isRoyalFlying()) {
|
||||||
return 0.0D;
|
return super.combatStandoffDistance();
|
||||||
}
|
}
|
||||||
return Math.max(super.combatStandoffDistance(), AntarchySettings.queenBeamMinimumRange() + 6.0D);
|
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
|
@Override
|
||||||
protected double biteDamageMultiplier() {
|
protected double biteDamageMultiplier() {
|
||||||
return super.biteDamageMultiplier() * 1.5D;
|
return super.biteDamageMultiplier() * 1.5D;
|
||||||
@@ -320,9 +353,13 @@ public class QueenEntity extends RoyalBossEntity {
|
|||||||
|
|
||||||
protected void registerRoyalTargetGoals() {
|
protected void registerRoyalTargetGoals() {
|
||||||
this.targetSelector.addGoal(1, new NearestAttackableTargetGoal<>(this, KingEntity.class, true));
|
this.targetSelector.addGoal(1, new NearestAttackableTargetGoal<>(this, KingEntity.class, true));
|
||||||
this.targetSelector.addGoal(2, new NearestAttackableTargetGoal<>(this, Player.class, true));
|
this.targetSelector.addGoal(2, new HurtByTargetGoal(this).setUnseenMemoryTicks(ROYAL_UNSEEN_MEMORY_TICKS));
|
||||||
this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(
|
this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, Player.class, true)
|
||||||
this, Mob.class, 10, true, false, this::canAttack));
|
.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
|
@Override
|
||||||
@@ -495,6 +532,9 @@ public class QueenEntity extends RoyalBossEntity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void tickQueenLandingSequence(LivingEntity target) {
|
private void tickQueenLandingSequence(LivingEntity target) {
|
||||||
|
if (this.queenLandingStage == QueenLandingStage.AERIAL && !this.isRoyalFlying()) {
|
||||||
|
this.setRoyalFlying(true);
|
||||||
|
}
|
||||||
if (this.queenLandingStage == QueenLandingStage.AERIAL && this.isRoyalFlying()
|
if (this.queenLandingStage == QueenLandingStage.AERIAL && this.isRoyalFlying()
|
||||||
&& !this.isQueenAbilityTelegraphActive()
|
&& !this.isQueenAbilityTelegraphActive()
|
||||||
&& this.queenLandingCooldownTicks-- <= 0) {
|
&& this.queenLandingCooldownTicks-- <= 0) {
|
||||||
@@ -529,7 +569,7 @@ public class QueenEntity extends RoyalBossEntity {
|
|||||||
this.performGravityStomp((ServerLevel) this.level(), target);
|
this.performGravityStomp((ServerLevel) this.level(), target);
|
||||||
} else if (--this.queenLandingStageTicks <= 0) {
|
} else if (--this.queenLandingStageTicks <= 0) {
|
||||||
this.resetQueenLandingSequence();
|
this.resetQueenLandingSequence();
|
||||||
this.queenLandingCooldownTicks = 40;
|
this.queenLandingCooldownTicks = LANDING_TIMEOUT_COOLDOWN_TICKS;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case IMPACT -> {
|
case IMPACT -> {
|
||||||
@@ -544,7 +584,8 @@ public class QueenEntity extends RoyalBossEntity {
|
|||||||
}
|
}
|
||||||
case GROUND_COMBAT -> {
|
case GROUND_COMBAT -> {
|
||||||
this.tickQueenGroundCharge(target);
|
this.tickQueenGroundCharge(target);
|
||||||
if (--this.queenLandingStageTicks <= 0) {
|
if (--this.queenLandingStageTicks <= 0
|
||||||
|
|| this.royalElevatedTargetTicks() >= ELEVATED_TARGET_TAKEOFF_TICKS) {
|
||||||
this.beginQueenTakeoff();
|
this.beginQueenTakeoff();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -577,7 +618,7 @@ public class QueenEntity extends RoyalBossEntity {
|
|||||||
double baseAngle = away.lengthSqr() > 1.0E-4D
|
double baseAngle = away.lengthSqr() > 1.0E-4D
|
||||||
? Math.atan2(away.z, away.x) : this.random.nextDouble() * Mth.TWO_PI;
|
? Math.atan2(away.z, away.x) : this.random.nextDouble() * Mth.TWO_PI;
|
||||||
boolean inverted = GravityApi.isGravityInverted(this);
|
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++) {
|
for (int angleIndex = 0; angleIndex < 12; angleIndex++) {
|
||||||
double angle = baseAngle + angleIndex * Mth.TWO_PI / 12.0D;
|
double angle = baseAngle + angleIndex * Mth.TWO_PI / 12.0D;
|
||||||
for (double radius : radii) {
|
for (double radius : radii) {
|
||||||
@@ -585,7 +626,7 @@ public class QueenEntity extends RoyalBossEntity {
|
|||||||
double z = target.getZ() + Math.sin(angle) * radius;
|
double z = target.getZ() + Math.sin(angle) * radius;
|
||||||
double y = inverted
|
double y = inverted
|
||||||
? target.position().y - 0.1D
|
? 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);
|
Vec3 candidate = new Vec3(x, y, z);
|
||||||
AABB box = this.getBoundingBox().move(
|
AABB box = this.getBoundingBox().move(
|
||||||
candidate.x - this.getX(), candidate.y - this.getY(), candidate.z - this.getZ());
|
candidate.x - this.getX(), candidate.y - this.getY(), candidate.z - this.getZ());
|
||||||
@@ -596,7 +637,7 @@ public class QueenEntity extends RoyalBossEntity {
|
|||||||
}
|
}
|
||||||
double fallbackY = inverted
|
double fallbackY = inverted
|
||||||
? target.position().y - 0.1D
|
? 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());
|
Vec3 fallback = new Vec3(target.getX(), fallbackY, target.getZ());
|
||||||
AABB fallbackBox = this.getBoundingBox().move(
|
AABB fallbackBox = this.getBoundingBox().move(
|
||||||
fallback.x - this.getX(), fallback.y - this.getY(), fallback.z - this.getZ());
|
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) {
|
if (direction.lengthSqr() > 1.0E-4D) {
|
||||||
this.queenGroundChargeDirection = direction.normalize();
|
this.queenGroundChargeDirection = direction.normalize();
|
||||||
this.queenGroundChargeTicks = GROUND_CHARGE_DURATION_TICKS;
|
this.queenGroundChargeTicks = GROUND_CHARGE_DURATION_TICKS;
|
||||||
|
this.queenGroundChargeHits.clear();
|
||||||
} else {
|
} else {
|
||||||
this.finishQueenGroundCharge();
|
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.setYRot((float) (Mth.atan2(-direction.x, direction.z) * Mth.RAD_TO_DEG));
|
||||||
this.yBodyRot = this.getYRot();
|
this.yBodyRot = this.getYRot();
|
||||||
this.hasImpulse = true;
|
this.hasImpulse = true;
|
||||||
|
if (this.level() instanceof ServerLevel level) {
|
||||||
|
this.applyQueenGroundChargeHits(level, direction);
|
||||||
|
}
|
||||||
if (--this.queenGroundChargeTicks <= 0) {
|
if (--this.queenGroundChargeTicks <= 0) {
|
||||||
this.finishQueenGroundCharge();
|
this.finishQueenGroundCharge();
|
||||||
}
|
}
|
||||||
@@ -688,7 +733,8 @@ public class QueenEntity extends RoyalBossEntity {
|
|||||||
}
|
}
|
||||||
if (this.queenGroundChargeCooldownTicks-- > 0
|
if (this.queenGroundChargeCooldownTicks-- > 0
|
||||||
|| target.position().multiply(1.0D, 0.0D, 1.0D)
|
|| 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;
|
return;
|
||||||
}
|
}
|
||||||
this.queenGroundChargeDirection = target.position().subtract(this.position())
|
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() {
|
private void finishQueenGroundCharge() {
|
||||||
this.queenGroundChargeTicks = 0;
|
this.queenGroundChargeTicks = 0;
|
||||||
this.queenGroundChargeWindupTicks = 0;
|
this.queenGroundChargeWindupTicks = 0;
|
||||||
@@ -710,6 +784,7 @@ public class QueenEntity extends RoyalBossEntity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void clearQueenGroundCharge() {
|
private void clearQueenGroundCharge() {
|
||||||
|
this.queenGroundChargeHits.clear();
|
||||||
this.queenGroundChargeCooldownTicks = 0;
|
this.queenGroundChargeCooldownTicks = 0;
|
||||||
this.queenGroundChargeWindupTicks = 0;
|
this.queenGroundChargeWindupTicks = 0;
|
||||||
this.queenGroundChargeTicks = 0;
|
this.queenGroundChargeTicks = 0;
|
||||||
@@ -1048,14 +1123,21 @@ public class QueenEntity extends RoyalBossEntity {
|
|||||||
: this.pendingPhaseSignature == Phase.THREE ? "acceleration"
|
: this.pendingPhaseSignature == Phase.THREE ? "acceleration"
|
||||||
: phase == Phase.TWO && this.pendingPhaseTwoCrushingGravity ? "crushing_gravity"
|
: phase == Phase.TWO && this.pendingPhaseTwoCrushingGravity ? "crushing_gravity"
|
||||||
: phase == Phase.THREE && this.pendingPhaseThreeChronosphere ? "queen_chronosphere" : null;
|
: phase == Phase.THREE && this.pendingPhaseThreeChronosphere ? "queen_chronosphere" : null;
|
||||||
String selected = forcedAttack != null
|
String chosen = forcedAttack != null
|
||||||
&& this.queenAttackReady(forcedAttack, phase, majorBusy)
|
&& this.queenAttackReady(forcedAttack, phase, majorBusy)
|
||||||
? forcedAttack : this.attackScheduler.chooseWeighted(candidates, id -> {
|
? forcedAttack : this.attackScheduler.chooseWeighted(candidates, id -> {
|
||||||
if (!this.queenAttackReady(id, phase, majorBusy)
|
if (!this.queenAttackReady(id, phase, majorBusy)
|
||||||
|| id.equals(this.lastQueenMajorAttack)) return 0;
|
|| id.equals(this.lastQueenMajorAttack)) return 0;
|
||||||
return this.queenAttackWeight(id, phase);
|
return this.queenAttackWeight(id, phase);
|
||||||
}, this.random);
|
}, 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) {
|
int cooldown = switch (selected) {
|
||||||
case "gravity_stomp" -> GRAVITY_STOMP_COOLDOWN;
|
case "gravity_stomp" -> GRAVITY_STOMP_COOLDOWN;
|
||||||
case "black_hole" -> BLACK_HOLE_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 radius = this.random.nextDouble() * GRAVITIC_UPHEAVAL_RADIUS;
|
||||||
double x = center.x + Math.cos(angle) * radius;
|
double x = center.x + Math.cos(angle) * radius;
|
||||||
double z = center.z + Math.sin(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);
|
BlockState state = level.getBlockState(pos);
|
||||||
if (!state.is(AntarchyTags.Blocks.QUEEN_GRABBABLE_BLOCKS) || level.getBlockEntity(pos) != null
|
if (!state.is(AntarchyTags.Blocks.QUEEN_GRABBABLE_BLOCKS) || level.getBlockEntity(pos) != null
|
||||||
|| state.getDestroySpeed(level, pos) < 0.0F || state.getCollisionShape(level, pos).isEmpty()) {
|
|| state.getDestroySpeed(level, pos) < 0.0F || state.getCollisionShape(level, pos).isEmpty()) {
|
||||||
|
|||||||
+736
-28
@@ -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.ai.navigation.PathNavigation;
|
||||||
import net.minecraft.world.entity.monster.Monster;
|
import net.minecraft.world.entity.monster.Monster;
|
||||||
import net.minecraft.world.entity.player.Player;
|
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.level.Level;
|
||||||
import net.minecraft.world.phys.AABB;
|
import net.minecraft.world.phys.AABB;
|
||||||
|
import net.minecraft.world.phys.HitResult;
|
||||||
import net.minecraft.world.phys.Vec3;
|
import net.minecraft.world.phys.Vec3;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
import software.bernie.geckolib.animatable.GeoEntity;
|
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 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 UUID MULTIPLAYER_DAMAGE_UUID = UUID.nameUUIDFromBytes(MULTIPLAYER_DAMAGE_ID.toString().getBytes());
|
||||||
private static final int OBSTRUCTION_CLEAR_INTERVAL_TICKS = 5;
|
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 double OBSTRUCTION_CLEAR_RADIUS = 3.5D;
|
||||||
private static final int OBSTRUCTION_CLEAR_MAX_BLOCKS = 24;
|
private static final int OBSTRUCTION_CLEAR_MAX_BLOCKS = 24;
|
||||||
private static final double OBSTRUCTION_CLEAR_MAX_RESISTANCE = 60.0D;
|
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_SIDE_Y = 10.25D;
|
||||||
private static final double BEAM_MUZZLE_LATERAL = 2.625D;
|
private static final double BEAM_MUZZLE_LATERAL = 2.625D;
|
||||||
private static final int HEAVY_BLOCK_CHECK_INTERVAL_TICKS = 4;
|
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_MIN_HOVER = 2.5D;
|
||||||
protected static final double FLYING_PREFERRED_HOVER = 6.0D;
|
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 boolean multiplayerScalingInitialized;
|
||||||
private final Set<UUID> encounterParticipants = new HashSet<>();
|
private final Set<UUID> encounterParticipants = new HashSet<>();
|
||||||
private double royalDamageMultiplier = 1.0D;
|
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
|
@Nullable
|
||||||
private Entity[] multipartParts;
|
private Entity[] multipartParts;
|
||||||
@@ -207,8 +255,8 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
|
|||||||
this.beamControllers[i] = new BeamController(this);
|
this.beamControllers[i] = new BeamController(this);
|
||||||
}
|
}
|
||||||
this.xpReward = 500;
|
this.xpReward = 500;
|
||||||
this.groundMoveControl = new MoveControl(this);
|
this.groundMoveControl = new RoyalGroundMoveControl(this);
|
||||||
this.flyingMoveControl = new FlyingMoveControl(this, 20, true);
|
this.flyingMoveControl = new RoyalFlyingMoveControl(this, 20);
|
||||||
this.groundNavigation = new GroundPathNavigation(this, level);
|
this.groundNavigation = new GroundPathNavigation(this, level);
|
||||||
FlyingPathNavigation flyingNavigation = new FlyingPathNavigation(this, level);
|
FlyingPathNavigation flyingNavigation = new FlyingPathNavigation(this, level);
|
||||||
flyingNavigation.setCanFloat(true);
|
flyingNavigation.setCanFloat(true);
|
||||||
@@ -217,6 +265,7 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
|
|||||||
this.moveControl = this.flyingMoveControl;
|
this.moveControl = this.flyingMoveControl;
|
||||||
this.navigation = this.flyingNavigation;
|
this.navigation = this.flyingNavigation;
|
||||||
} else {
|
} else {
|
||||||
|
this.moveControl = this.groundMoveControl;
|
||||||
this.navigation = this.groundNavigation;
|
this.navigation = this.groundNavigation;
|
||||||
}
|
}
|
||||||
this.bossEvent = new com.craisinlord.antarchy.content.boss.EntityLinkedServerBossEvent(
|
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() {
|
protected void registerRoyalTargetGoals() {
|
||||||
this.targetSelector.addGoal(1, new HurtByTargetGoal(this));
|
this.targetSelector.addGoal(1, new HurtByTargetGoal(this).setUnseenMemoryTicks(ROYAL_UNSEEN_MEMORY_TICKS));
|
||||||
this.targetSelector.addGoal(2, new NearestAttackableTargetGoal<>(this, Player.class, true));
|
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) {
|
public boolean canDamageWithRoyalAttack(LivingEntity target) {
|
||||||
@@ -512,7 +595,7 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
|
|||||||
if (this.shouldClearObstruction()
|
if (this.shouldClearObstruction()
|
||||||
&& !this.level().isClientSide
|
&& !this.level().isClientSide
|
||||||
&& !this.isDeadOrDying()
|
&& !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()) {
|
&& this.isInWall()) {
|
||||||
RoyalBlockDestruction.destroySphere(
|
RoyalBlockDestruction.destroySphere(
|
||||||
(ServerLevel) this.level(),
|
(ServerLevel) this.level(),
|
||||||
@@ -575,13 +658,16 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
|
|||||||
for (RoyalHead head : this.heads) {
|
for (RoyalHead head : this.heads) {
|
||||||
head.setTarget(null);
|
head.setTarget(null);
|
||||||
}
|
}
|
||||||
|
this.resetRoyalCombatTracking();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.initializeMultiplayerScaling(primaryTarget);
|
this.initializeMultiplayerScaling(primaryTarget);
|
||||||
this.assignHeadTargets(primaryTarget);
|
this.assignHeadTargets(primaryTarget);
|
||||||
|
this.tickRoyalTargetElevation(primaryTarget);
|
||||||
|
|
||||||
this.tickCombatLocomotionMode(primaryTarget);
|
this.tickCombatLocomotionMode(primaryTarget);
|
||||||
|
this.tickRoyalStuckRecovery(primaryTarget);
|
||||||
boolean movementBlocksAttacks = this.blocksRoyalAttacksForMovement();
|
boolean movementBlocksAttacks = this.blocksRoyalAttacksForMovement();
|
||||||
if (!movementBlocksAttacks) {
|
if (!movementBlocksAttacks) {
|
||||||
boolean aeriallyStabilized = this.steerTowardTarget(primaryTarget);
|
boolean aeriallyStabilized = this.steerTowardTarget(primaryTarget);
|
||||||
@@ -595,6 +681,8 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.tickBodyCrush();
|
this.tickBodyCrush();
|
||||||
|
this.tickRoyalCloseRangeAnswer(primaryTarget);
|
||||||
|
this.tickRoyalConcealedTarget(primaryTarget);
|
||||||
|
|
||||||
Phase phase = this.phase();
|
Phase phase = this.phase();
|
||||||
int activeHeadAttacks = 0;
|
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
|
double verticalDistance = Math.abs(target.getY() + target.getBbHeight() * 0.5D
|
||||||
- (this.getY() + this.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.landingForCombat && verticalDistance > 6.0D) {
|
||||||
if (this.aerialCombatAnchor == null || this.aerialCombatAnchorTicks-- <= 0) {
|
if (this.aerialCombatAnchor == null || this.aerialCombatAnchorTicks-- <= 0) {
|
||||||
double anchorX = this.getX();
|
this.aerialCombatAnchor = new Vec3(wanted.x, wantedY, wanted.z);
|
||||||
double anchorZ = this.getZ();
|
|
||||||
if (horizontalDistance > 10.0D) {
|
|
||||||
anchorX = wanted.x;
|
|
||||||
anchorZ = wanted.z;
|
|
||||||
}
|
|
||||||
this.aerialCombatAnchor = new Vec3(anchorX, this.getY(), anchorZ);
|
|
||||||
this.aerialCombatAnchorTicks = 20;
|
this.aerialCombatAnchorTicks = 20;
|
||||||
}
|
}
|
||||||
Vec3 anchor = this.aerialCombatAnchor;
|
Vec3 anchor = this.aerialCombatAnchor;
|
||||||
@@ -935,15 +1026,6 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
|
|||||||
this.aerialCombatAnchor = null;
|
this.aerialCombatAnchor = null;
|
||||||
this.aerialCombatAnchorTicks = 0;
|
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;
|
boolean closeVertically = Math.abs(wantedY - this.getY()) <= 2.5D;
|
||||||
if (atStandoff && closeVertically) {
|
if (atStandoff && closeVertically) {
|
||||||
this.getNavigation().stop();
|
this.getNavigation().stop();
|
||||||
@@ -953,6 +1035,536 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
|
|||||||
return false;
|
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() {
|
protected double combatStandoffDistance() {
|
||||||
return Math.max(this.getBbWidth() * 0.55D,
|
return Math.max(this.getBbWidth() * 0.55D,
|
||||||
BEAM_MUZZLE_FORWARD - this.biteReach() * 0.65D + 1.0D);
|
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);
|
boolean inverted = GravityApi.isGravityInverted(this);
|
||||||
double groundY = this.groundYBelow(this.getX(), this.getZ());
|
double groundY = this.groundYBelow(this.getX(), this.getZ());
|
||||||
if (this.landingForCombat) {
|
if (this.landingForCombat) {
|
||||||
if (!target.onGround()) {
|
if (!target.onGround() || ++this.landingForCombatTicks > LANDING_FOR_COMBAT_TIMEOUT_TICKS) {
|
||||||
this.landingForCombat = false;
|
this.landingForCombat = false;
|
||||||
|
this.landingForCombatTicks = 0;
|
||||||
this.locomotionDecisionTicks = 60;
|
this.locomotionDecisionTicks = 60;
|
||||||
} else if (this.onGround() || !inverted && this.getY() <= groundY + 1.0D) {
|
} else if (this.onGround() || !inverted && this.getY() <= groundY + 1.0D) {
|
||||||
this.landingForCombat = false;
|
this.landingForCombat = false;
|
||||||
@@ -983,6 +1596,7 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
|
|||||||
this.locomotionDecisionTicks = 60 + this.random.nextInt(80);
|
this.locomotionDecisionTicks = 60 + this.random.nextInt(80);
|
||||||
if (target.onGround() && this.random.nextDouble() < this.groundCombatBias()) {
|
if (target.onGround() && this.random.nextDouble() < this.groundCombatBias()) {
|
||||||
this.landingForCombat = true;
|
this.landingForCombat = true;
|
||||||
|
this.landingForCombatTicks = 0;
|
||||||
}
|
}
|
||||||
} else if (!target.onGround() || this.random.nextDouble() >= this.groundCombatBias()) {
|
} else if (!target.onGround() || this.random.nextDouble() >= this.groundCombatBias()) {
|
||||||
this.setRoyalFlying(true);
|
this.setRoyalFlying(true);
|
||||||
@@ -1009,8 +1623,61 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected double groundYBelow(double x, double z) {
|
protected double groundYBelow(double x, double z) {
|
||||||
return this.level().getHeight(net.minecraft.world.level.levelgen.Heightmap.Types.MOTION_BLOCKING_NO_LEAVES,
|
return this.groundYBelow(x, z, this.getY() + 1.0D);
|
||||||
Mth.floor(x), Mth.floor(z));
|
}
|
||||||
|
|
||||||
|
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() {
|
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 groundY = this.groundYBelow(this.getX(), this.getZ());
|
||||||
double ceiling = groundY + FLYING_MAX_HOVER_ABOVE_GROUND;
|
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) {
|
if (this.getY() > ceiling) {
|
||||||
double desiredY = groundY + FLYING_PREFERRED_HOVER;
|
double desiredY = groundY + FLYING_PREFERRED_HOVER;
|
||||||
this.getMoveControl().setWantedPosition(this.getX(), desiredY, this.getZ(), 1.0D);
|
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() {
|
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;
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
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;
|
double yaw = this.yBodyRot * Mth.DEG_TO_RAD;
|
||||||
Vec3 forward = new Vec3(-Math.sin(yaw), 0.0D, Math.cos(yaw));
|
forward = new Vec3(-Math.sin(yaw), 0.0D, Math.cos(yaw));
|
||||||
|
}
|
||||||
|
}
|
||||||
Vec3 center = new Vec3(
|
Vec3 center = new Vec3(
|
||||||
this.getX() + forward.x * (this.getBbWidth() * 0.45D),
|
this.getX() + forward.x * (this.getBbWidth() * 0.45D),
|
||||||
this.getY() + (this.isRoyalFlying() ? 0.0D : 1.0D),
|
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) {
|
protected Vec3 beamDirection(RoyalHead head) {
|
||||||
float yaw = this.getYRot() * Mth.DEG_TO_RAD;
|
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,
|
return GravityRotationUtil.vecPlayerToWorld(localDirection,
|
||||||
GravityApi.getGravityDirection(this)).normalize();
|
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) {
|
protected boolean targetInBeamLine(RoyalHead head, LivingEntity target) {
|
||||||
Vec3 origin = this.beamAnchor(head);
|
Vec3 origin = this.beamAnchor(head);
|
||||||
Vec3 direction = this.beamDirection(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
|
@Override
|
||||||
protected void onHitEntity(EntityHitResult result) {
|
protected void onHitEntity(EntityHitResult result) {
|
||||||
if (!this.level().isClientSide && this.level() instanceof ServerLevel level) {
|
if (!this.level().isClientSide && this.level() instanceof ServerLevel level) {
|
||||||
|
|||||||
+2
-1
@@ -100,7 +100,8 @@ public final class RoyalAttackScheduler {
|
|||||||
int total = 0;
|
int total = 0;
|
||||||
int[] weights = new int[ids.length];
|
int[] weights = new int[ids.length];
|
||||||
for (int i = 0; i < ids.length; i++) {
|
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];
|
total += weights[i];
|
||||||
}
|
}
|
||||||
if (total <= 0) {
|
if (total <= 0) {
|
||||||
|
|||||||
+27
-1
@@ -1,6 +1,7 @@
|
|||||||
package com.craisinlord.antarchy.content.portal;
|
package com.craisinlord.antarchy.content.portal;
|
||||||
|
|
||||||
import com.craisinlord.antarchy.config.AntarchySettings;
|
import com.craisinlord.antarchy.config.AntarchySettings;
|
||||||
|
import com.craisinlord.antarchy.content.entity.ant.AntTeleportHelper;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -14,6 +15,7 @@ import net.minecraft.server.level.ServerLevel;
|
|||||||
import net.minecraft.server.level.ServerPlayer;
|
import net.minecraft.server.level.ServerPlayer;
|
||||||
import net.minecraft.util.Mth;
|
import net.minecraft.util.Mth;
|
||||||
import net.minecraft.world.entity.Entity;
|
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.PoiManager;
|
||||||
import net.minecraft.world.entity.ai.village.poi.PoiRecord;
|
import net.minecraft.world.entity.ai.village.poi.PoiRecord;
|
||||||
import net.minecraft.world.entity.vehicle.DismountHelper;
|
import net.minecraft.world.entity.vehicle.DismountHelper;
|
||||||
@@ -48,12 +50,23 @@ public final class PermanentPortalTeleporter {
|
|||||||
|
|
||||||
entity.setPortalCooldown();
|
entity.setPortalCooldown();
|
||||||
Transition transition = createTransition(sourceLevel, entity, entity.blockPosition(), type);
|
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);
|
Entity movedEntity = moveEntity(entity, transition);
|
||||||
if (movedEntity != null) {
|
if (movedEntity != null) {
|
||||||
movedEntity.setPortalCooldown();
|
movedEntity.setPortalCooldown();
|
||||||
if (transition.playPortalSound() && movedEntity instanceof ServerPlayer player) {
|
if (transition.playPortalSound() && movedEntity instanceof ServerPlayer player) {
|
||||||
player.connection.send(new ClientboundLevelEventPacket(LevelEvent.SOUND_PORTAL_TRAVEL, BlockPos.ZERO, 0, false));
|
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();
|
return safePortalPosition != null ? safePortalPosition : existing.center();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AntTeleportHelper.loadArrivalSearchArea(destination, preferredPos, SEARCH_RADIUS);
|
||||||
Vec3 safe = findSafeArrivalPosition(entity, destination, preferredPos, type);
|
Vec3 safe = findSafeArrivalPosition(entity, destination, preferredPos, type);
|
||||||
if (safe != null) {
|
if (safe != null) {
|
||||||
PermanentPortalShape created = createReturnPortal(destination, safe, type);
|
PermanentPortalShape created = createReturnPortal(destination, safe, type);
|
||||||
@@ -100,7 +114,19 @@ public final class PermanentPortalTeleporter {
|
|||||||
return createFallbackPortal(destination, BlockPos.containing(safe), type);
|
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
|
@Nullable
|
||||||
|
|||||||
+26
-23
@@ -3,11 +3,14 @@ package com.craisinlord.antarchy.mixins.client;
|
|||||||
import com.craisinlord.antarchy.content.entity.HerculesBeetleEntity;
|
import com.craisinlord.antarchy.content.entity.HerculesBeetleEntity;
|
||||||
import net.minecraft.client.Camera;
|
import net.minecraft.client.Camera;
|
||||||
import net.minecraft.world.entity.Entity;
|
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.Mixin;
|
||||||
import org.spongepowered.asm.mixin.Shadow;
|
import org.spongepowered.asm.mixin.Shadow;
|
||||||
import org.spongepowered.asm.mixin.Unique;
|
import org.spongepowered.asm.mixin.Unique;
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
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)
|
@Mixin(Camera.class)
|
||||||
public abstract class MountedVehicleCameraMixin {
|
public abstract class MountedVehicleCameraMixin {
|
||||||
@@ -17,31 +20,31 @@ public abstract class MountedVehicleCameraMixin {
|
|||||||
@Shadow
|
@Shadow
|
||||||
private boolean detached;
|
private boolean detached;
|
||||||
|
|
||||||
@ModifyArg(
|
@Shadow
|
||||||
method = "setup",
|
protected abstract void setPosition(Vec3 position);
|
||||||
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Camera;move(DDD)V"),
|
|
||||||
index = 0
|
@Inject(method = "setup", at = @At("RETURN"))
|
||||||
)
|
private void antarchy$adjustMountedCamera(
|
||||||
private double antarchy$adjustMountedCameraX(double value) {
|
BlockGetter level,
|
||||||
return this.antarchy$ridingHerculesBeetle() ? value * 1.45D : value;
|
Entity entity,
|
||||||
|
boolean detached,
|
||||||
|
boolean thirdPersonReverse,
|
||||||
|
float partialTick,
|
||||||
|
CallbackInfo ci
|
||||||
|
) {
|
||||||
|
if (!this.antarchy$ridingHerculesBeetle()) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ModifyArg(
|
Camera camera = (Camera) (Object) this;
|
||||||
method = "setup",
|
Vec3 look = new Vec3(camera.getLookVector());
|
||||||
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Camera;move(DDD)V"),
|
Vec3 cameraOffset = camera.getPosition().subtract(entity.getEyePosition(partialTick));
|
||||||
index = 1
|
double zoomOffset = cameraOffset.dot(look) * 0.45D;
|
||||||
)
|
|
||||||
private double antarchy$adjustMountedCameraY(double value) {
|
|
||||||
return this.antarchy$ridingHerculesBeetle() ? value + 0.2D : value;
|
|
||||||
}
|
|
||||||
|
|
||||||
@ModifyArg(
|
Vec3 adjustment = look.scale(zoomOffset)
|
||||||
method = "setup",
|
.add(new Vec3(camera.getUpVector()).scale(0.2D))
|
||||||
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Camera;move(DDD)V"),
|
.add(new Vec3(camera.getLeftVector()).scale(0.45D));
|
||||||
index = 2
|
this.setPosition(camera.getPosition().add(adjustment));
|
||||||
)
|
|
||||||
private double antarchy$adjustMountedCameraZ(double value) {
|
|
||||||
return this.antarchy$ridingHerculesBeetle() ? value + 0.45D : value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Unique
|
@Unique
|
||||||
|
|||||||
+4
-5
@@ -1,18 +1,17 @@
|
|||||||
package com.craisinlord.antarchy.mixins.client;
|
package com.craisinlord.antarchy.mixins.client;
|
||||||
|
|
||||||
import com.craisinlord.antarchy.content.client.PortalGunZoomClientState;
|
import com.craisinlord.antarchy.content.client.PortalGunZoomClientState;
|
||||||
|
import com.llamalad7.mixinextras.injector.ModifyReturnValue;
|
||||||
import net.minecraft.client.Camera;
|
import net.minecraft.client.Camera;
|
||||||
import net.minecraft.client.Minecraft;
|
import net.minecraft.client.Minecraft;
|
||||||
import net.minecraft.client.renderer.GameRenderer;
|
import net.minecraft.client.renderer.GameRenderer;
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
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)
|
@Mixin(GameRenderer.class)
|
||||||
public abstract class PortalGunZoomFovMixin {
|
public abstract class PortalGunZoomFovMixin {
|
||||||
@Inject(method = "getFov", at = @At("RETURN"), cancellable = true)
|
@ModifyReturnValue(method = "getFov", at = @At("RETURN"))
|
||||||
private void antarchy$applyPortalGunZoom(Camera camera, float partialTick, boolean useFovSetting, CallbackInfoReturnable<Double> cir) {
|
private double antarchy$applyPortalGunZoom(double fov, Camera camera, float partialTick, boolean useFovSetting) {
|
||||||
cir.setReturnValue(PortalGunZoomClientState.applyFov(cir.getReturnValue(), partialTick, Minecraft.getInstance().options));
|
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": [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": [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, -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",
|
"type": "minecraft:item",
|
||||||
"name": "antarchy:cornea_ear",
|
"name": "antarchy:cornea_ear",
|
||||||
"weight": 8,
|
"weight": 5,
|
||||||
"functions": [
|
"functions": [
|
||||||
{
|
{
|
||||||
"function": "minecraft:set_count",
|
"function": "minecraft:set_count",
|
||||||
@@ -42,7 +42,7 @@
|
|||||||
{
|
{
|
||||||
"type": "minecraft:item",
|
"type": "minecraft:item",
|
||||||
"name": "antarchy:bluestone",
|
"name": "antarchy:bluestone",
|
||||||
"weight": 10,
|
"weight": 6,
|
||||||
"functions": [
|
"functions": [
|
||||||
{
|
{
|
||||||
"function": "minecraft:set_count",
|
"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",
|
"type": "minecraft:item",
|
||||||
"name": "minecraft:diamond",
|
"name": "minecraft:diamond",
|
||||||
|
|||||||
@@ -4,11 +4,11 @@
|
|||||||
"pattern": [
|
"pattern": [
|
||||||
" B",
|
" B",
|
||||||
" B ",
|
" B ",
|
||||||
"N "
|
"I "
|
||||||
],
|
],
|
||||||
"key": {
|
"key": {
|
||||||
"N": {
|
"I": {
|
||||||
"item": "minecraft:netherite_ingot"
|
"item": "minecraft:iron_ingot"
|
||||||
},
|
},
|
||||||
"B": {
|
"B": {
|
||||||
"item": "antarchy:blood_crystal_shard"
|
"item": "antarchy:blood_crystal_shard"
|
||||||
|
|||||||
+5
-1
@@ -15,6 +15,7 @@ import java.nio.file.Path;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
public final class InfinityConfigRefresh {
|
public final class InfinityConfigRefresh {
|
||||||
private static final Map<String, Registry<?>> CHECKED_FILES = Map.of(
|
private static final Map<String, Registry<?>> CHECKED_FILES = Map.of(
|
||||||
@@ -22,6 +23,7 @@ public final class InfinityConfigRefresh {
|
|||||||
"items.json", BuiltInRegistries.ITEM,
|
"items.json", BuiltInRegistries.ITEM,
|
||||||
"effects.json", BuiltInRegistries.MOB_EFFECT
|
"effects.json", BuiltInRegistries.MOB_EFFECT
|
||||||
);
|
);
|
||||||
|
private static final Set<String> DISABLED_IDS = Set.of("antarchy:paralyzed");
|
||||||
|
|
||||||
private InfinityConfigRefresh() {
|
private InfinityConfigRefresh() {
|
||||||
}
|
}
|
||||||
@@ -43,7 +45,7 @@ public final class InfinityConfigRefresh {
|
|||||||
try {
|
try {
|
||||||
Files.delete(invocationLock);
|
Files.delete(invocationLock);
|
||||||
Antarchy.LOGGER.info(
|
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
|
staleIds
|
||||||
);
|
);
|
||||||
} catch (Exception exception) {
|
} catch (Exception exception) {
|
||||||
@@ -73,6 +75,8 @@ public final class InfinityConfigRefresh {
|
|||||||
ResourceLocation id = ResourceLocation.tryParse(key);
|
ResourceLocation id = ResourceLocation.tryParse(key);
|
||||||
if (id != null && Antarchy.MODID.equals(id.getNamespace()) && !registry.containsKey(id)) {
|
if (id != null && Antarchy.MODID.equals(id.getNamespace()) && !registry.containsKey(id)) {
|
||||||
staleIds.add(key);
|
staleIds.add(key);
|
||||||
|
} else if (DISABLED_IDS.contains(key) && (!entry.has("weight") || entry.get("weight").getAsDouble() > 0.0D)) {
|
||||||
|
staleIds.add(key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (Exception exception) {
|
} catch (Exception exception) {
|
||||||
|
|||||||
+11
-1
@@ -11,6 +11,7 @@ import net.minecraft.util.Mth;
|
|||||||
import net.minecraft.util.RandomSource;
|
import net.minecraft.util.RandomSource;
|
||||||
|
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
public final class QueenMusicHandler {
|
public final class QueenMusicHandler {
|
||||||
private static final double START_RADIUS = 256.0D;
|
private static final double START_RADIUS = 256.0D;
|
||||||
@@ -25,7 +26,7 @@ public final class QueenMusicHandler {
|
|||||||
return music != null;
|
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()
|
if (minecraft.level == null || minecraft.player == null || minecraft.isPaused()
|
||||||
|| !minecraft.player.isAlive() || minecraft.player.isRemoved()
|
|| !minecraft.player.isAlive() || minecraft.player.isRemoved()
|
||||||
|| minecraft.player.isDeadOrDying() || minecraft.screen instanceof DeathScreen) {
|
|| minecraft.player.isDeadOrDying() || minecraft.screen instanceof DeathScreen) {
|
||||||
@@ -51,6 +52,15 @@ public final class QueenMusicHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (music == null) {
|
if (music == null) {
|
||||||
|
SoundEvent soundEvent;
|
||||||
|
try {
|
||||||
|
soundEvent = soundEventSupplier.get();
|
||||||
|
} catch (RuntimeException exception) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (soundEvent == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
minecraft.getMusicManager().stopPlaying();
|
minecraft.getMusicManager().stopPlaying();
|
||||||
music = new QueenMusicSound(soundEvent);
|
music = new QueenMusicSound(soundEvent);
|
||||||
minecraft.getSoundManager().play(music);
|
minecraft.getSoundManager().play(music);
|
||||||
|
|||||||
+11
@@ -11,6 +11,12 @@ import software.bernie.geckolib.model.GeoModel;
|
|||||||
|
|
||||||
public class RoyalMountModel extends GeoModel<RoyalMountEntity> {
|
public class RoyalMountModel extends GeoModel<RoyalMountEntity> {
|
||||||
private static final String[] HEADS = {"head_left", "head", "head_right"};
|
private static final String[] HEADS = {"head_left", "head", "head_right"};
|
||||||
|
private static final String[] RIDDEN_NECK_CHAIN = {
|
||||||
|
"neck", "neck2", "neck3", "head",
|
||||||
|
"neck_left", "neck_left2", "neck_left3", "head_left",
|
||||||
|
"neck_right", "neck_right2", "neck_right3", "head_right"
|
||||||
|
};
|
||||||
|
private static final float RIDDEN_NECK_RAISE = 0.35F;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ResourceLocation getModelResource(RoyalMountEntity animatable) {
|
public ResourceLocation getModelResource(RoyalMountEntity animatable) {
|
||||||
@@ -31,6 +37,11 @@ public class RoyalMountModel extends GeoModel<RoyalMountEntity> {
|
|||||||
public void setCustomAnimations(RoyalMountEntity animatable, long instanceId,
|
public void setCustomAnimations(RoyalMountEntity animatable, long instanceId,
|
||||||
AnimationState<RoyalMountEntity> animationState) {
|
AnimationState<RoyalMountEntity> animationState) {
|
||||||
super.setCustomAnimations(animatable, instanceId, animationState);
|
super.setCustomAnimations(animatable, instanceId, animationState);
|
||||||
|
if (animatable.isVehicle()) {
|
||||||
|
for (String name : RIDDEN_NECK_CHAIN) {
|
||||||
|
this.getBone(name).ifPresent(bone -> bone.setRotX(bone.getRotX() * RIDDEN_NECK_RAISE));
|
||||||
|
}
|
||||||
|
}
|
||||||
for (String head : HEADS) {
|
for (String head : HEADS) {
|
||||||
this.getBone(head).ifPresent(bone -> bone.setTrackingMatrices(true));
|
this.getBone(head).ifPresent(bone -> bone.setTrackingMatrices(true));
|
||||||
}
|
}
|
||||||
|
|||||||
+14
-5
@@ -758,10 +758,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() {
|
private void startMountedFlight() {
|
||||||
this.pendingChargeStart = false;
|
this.pendingChargeStart = false;
|
||||||
this.setFlying(true);
|
this.setFlying(true);
|
||||||
@@ -1168,6 +1164,19 @@ public class HerculesBeetleEntity extends TamableAnimal implements GeoEntity, Fl
|
|||||||
&& this.getControllingPassenger() == player;
|
&& 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
|
@Override
|
||||||
@Nullable
|
@Nullable
|
||||||
public LivingEntity getControllingPassenger() {
|
public LivingEntity getControllingPassenger() {
|
||||||
@@ -1183,7 +1192,7 @@ public class HerculesBeetleEntity extends TamableAnimal implements GeoEntity, Fl
|
|||||||
if (this.hasPassenger(passenger)) {
|
if (this.hasPassenger(passenger)) {
|
||||||
Vec3 localForward = this.toLocalPlane(this.getLookAngle());
|
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);
|
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 yOffset = mountedFlightPose ? 5.3D : 3.0D;
|
||||||
double forwardOffset = mountedFlightPose ? 2.2D : 1.25D;
|
double forwardOffset = mountedFlightPose ? 2.2D : 1.25D;
|
||||||
if (!this.isFlying()) {
|
if (!this.isFlying()) {
|
||||||
|
|||||||
+3
-3
@@ -64,7 +64,7 @@ public final class AntTeleportHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static List<Mob> collectCompanions(ServerPlayer player) {
|
public static List<Mob> collectCompanions(ServerPlayer player) {
|
||||||
List<Mob> companions = new ArrayList<>();
|
List<Mob> companions = new ArrayList<>();
|
||||||
if (player.getVehicle() instanceof Mob vehicle) {
|
if (player.getVehicle() instanceof Mob vehicle) {
|
||||||
companions.add(vehicle);
|
companions.add(vehicle);
|
||||||
@@ -96,7 +96,7 @@ public final class AntTeleportHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private static Mob moveCompanion(Mob companion, ServerLevel destination, Vec3 destinationPos) {
|
public static Mob moveCompanion(Mob companion, ServerLevel destination, Vec3 destinationPos) {
|
||||||
companion.stopRiding();
|
companion.stopRiding();
|
||||||
companion.ejectPassengers();
|
companion.ejectPassengers();
|
||||||
companion.getNavigation().stop();
|
companion.getNavigation().stop();
|
||||||
@@ -398,7 +398,7 @@ public final class AntTeleportHelper {
|
|||||||
* bounded area up front so unloaded terrain is not mistaken for an unsafe
|
* bounded area up front so unloaded terrain is not mistaken for an unsafe
|
||||||
* destination and rejected prematurely.
|
* destination and rejected prematurely.
|
||||||
*/
|
*/
|
||||||
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);
|
ChunkPos center = new ChunkPos(preferredPos);
|
||||||
int chunkRadius = (searchRadius >> 4) + 1;
|
int chunkRadius = (searchRadius >> 4) + 1;
|
||||||
for (int chunkX = center.x - chunkRadius; chunkX <= center.x + chunkRadius; chunkX++) {
|
for (int chunkX = center.x - chunkRadius; chunkX <= center.x + chunkRadius; chunkX++) {
|
||||||
|
|||||||
+19
-2
@@ -40,6 +40,8 @@ import net.minecraft.world.entity.ai.goal.target.NearestAttackableTargetGoal;
|
|||||||
import net.minecraft.world.entity.ai.navigation.FlyingPathNavigation;
|
import net.minecraft.world.entity.ai.navigation.FlyingPathNavigation;
|
||||||
import net.minecraft.world.entity.ai.navigation.PathNavigation;
|
import net.minecraft.world.entity.ai.navigation.PathNavigation;
|
||||||
import net.minecraft.world.entity.monster.Monster;
|
import net.minecraft.world.entity.monster.Monster;
|
||||||
|
import net.minecraft.world.effect.MobEffectInstance;
|
||||||
|
import net.minecraft.world.effect.MobEffects;
|
||||||
import net.minecraft.world.entity.player.Player;
|
import net.minecraft.world.entity.player.Player;
|
||||||
import net.minecraft.world.level.ServerLevelAccessor;
|
import net.minecraft.world.level.ServerLevelAccessor;
|
||||||
import net.minecraft.world.level.Level;
|
import net.minecraft.world.level.Level;
|
||||||
@@ -85,7 +87,7 @@ public class BrutalflyEntity extends Monster implements GeoEntity {
|
|||||||
private static final int SPIT_WINDUP_TICKS = 16;
|
private static final int SPIT_WINDUP_TICKS = 16;
|
||||||
private static final int SWIPE_TICKS = 18;
|
private static final int SWIPE_TICKS = 18;
|
||||||
private static final float SWIPE_KNOCKBACK = 2.8F;
|
private static final float SWIPE_KNOCKBACK = 2.8F;
|
||||||
private static final int COCOON_HIT_THRESHOLD = 3;
|
private static final int COCOON_HIT_THRESHOLD = 5;
|
||||||
private static final int COCOON_HIT_COOLDOWN_TICKS = 60;
|
private static final int COCOON_HIT_COOLDOWN_TICKS = 60;
|
||||||
private static final int COCOON_HIT_ANIM_TICKS = 30;
|
private static final int COCOON_HIT_ANIM_TICKS = 30;
|
||||||
private static final int COCOON_HATCH_ANIM_TICKS = 40;
|
private static final int COCOON_HATCH_ANIM_TICKS = 40;
|
||||||
@@ -339,6 +341,12 @@ public class BrutalflyEntity extends Monster implements GeoEntity {
|
|||||||
if (nearbyPlayer != null && !nearbyPlayer.isCreative() && !nearbyPlayer.isSpectator()) {
|
if (nearbyPlayer != null && !nearbyPlayer.isCreative() && !nearbyPlayer.isSpectator()) {
|
||||||
this.setTarget(nearbyPlayer);
|
this.setTarget(nearbyPlayer);
|
||||||
}
|
}
|
||||||
|
this.spitCooldown = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getDamageScale() {
|
||||||
|
double base = AntarchySettings.brutalflySwipeDamage();
|
||||||
|
return base <= 0.0D ? 1.0F : (float) (this.getAttributeValue(Attributes.ATTACK_DAMAGE) / base);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isCocooned() {
|
public boolean isCocooned() {
|
||||||
@@ -516,6 +524,11 @@ public class BrutalflyEntity extends Monster implements GeoEntity {
|
|||||||
this.playSound(AntarchySoundEvents.BRUTALFLY_SPIT.get(), 1.0F, 0.9F + this.random.nextFloat() * 0.2F);
|
this.playSound(AntarchySoundEvents.BRUTALFLY_SPIT.get(), 1.0F, 0.9F + this.random.nextFloat() * 0.2F);
|
||||||
switch (this.getFightPhase()) {
|
switch (this.getFightPhase()) {
|
||||||
case PHASE_THREE -> {
|
case PHASE_THREE -> {
|
||||||
|
this.launchOrb(serverLevel, origin, target, BrutalflyOrbEntity.OrbVariant.POISON, -14.0F);
|
||||||
|
this.launchOrb(serverLevel, origin, target, BrutalflyOrbEntity.OrbVariant.STICKY, 0.0F);
|
||||||
|
this.launchOrb(serverLevel, origin, target, BrutalflyOrbEntity.OrbVariant.POISON, 14.0F);
|
||||||
|
}
|
||||||
|
case PHASE_TWO -> {
|
||||||
this.launchOrb(serverLevel, origin, target, BrutalflyOrbEntity.OrbVariant.POISON, -8.0F);
|
this.launchOrb(serverLevel, origin, target, BrutalflyOrbEntity.OrbVariant.POISON, -8.0F);
|
||||||
this.launchOrb(serverLevel, origin, target, BrutalflyOrbEntity.OrbVariant.STICKY, 8.0F);
|
this.launchOrb(serverLevel, origin, target, BrutalflyOrbEntity.OrbVariant.STICKY, 8.0F);
|
||||||
}
|
}
|
||||||
@@ -588,7 +601,8 @@ public class BrutalflyEntity extends Monster implements GeoEntity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void performSwipeHit() {
|
private void performSwipeHit() {
|
||||||
float swipeDamage = (float) AntarchySettings.brutalflySwipeDamage();
|
float swipeDamage = (float) this.getAttributeValue(Attributes.ATTACK_DAMAGE);
|
||||||
|
boolean finalPhase = this.getFightPhase() == FightPhase.PHASE_THREE;
|
||||||
AABB attackBox = this.getBoundingBox().inflate(4.8D, 2.4D, 4.8D).expandTowards(0.0D, -2.5D, 0.0D);
|
AABB attackBox = this.getBoundingBox().inflate(4.8D, 2.4D, 4.8D).expandTowards(0.0D, -2.5D, 0.0D);
|
||||||
for (LivingEntity target : this.level().getEntitiesOfClass(
|
for (LivingEntity target : this.level().getEntitiesOfClass(
|
||||||
LivingEntity.class,
|
LivingEntity.class,
|
||||||
@@ -600,6 +614,9 @@ public class BrutalflyEntity extends Monster implements GeoEntity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
target.hurt(this.damageSources().mobAttack(this), swipeDamage);
|
target.hurt(this.damageSources().mobAttack(this), swipeDamage);
|
||||||
|
if (finalPhase) {
|
||||||
|
target.addEffect(new MobEffectInstance(MobEffects.MOVEMENT_SLOWDOWN, 60, 1, false, true));
|
||||||
|
}
|
||||||
|
|
||||||
double dx = target.getX() - this.getX();
|
double dx = target.getX() - this.getX();
|
||||||
double dz = target.getZ() - this.getZ();
|
double dz = target.getZ() - this.getZ();
|
||||||
|
|||||||
+3
@@ -162,6 +162,9 @@ public class BrutalflyOrbEntity extends ThrowableItemProjectile {
|
|||||||
if (!this.level().isClientSide && result.getEntity() instanceof LivingEntity livingTarget) {
|
if (!this.level().isClientSide && result.getEntity() instanceof LivingEntity livingTarget) {
|
||||||
float directHitDamage = (float) AntarchySettings.brutalflySpitDamage();
|
float directHitDamage = (float) AntarchySettings.brutalflySpitDamage();
|
||||||
Entity owner = this.getOwner();
|
Entity owner = this.getOwner();
|
||||||
|
if (owner instanceof BrutalflyEntity brutalfly) {
|
||||||
|
directHitDamage *= brutalfly.getDamageScale();
|
||||||
|
}
|
||||||
if (owner instanceof LivingEntity livingOwner) {
|
if (owner instanceof LivingEntity livingOwner) {
|
||||||
livingTarget.hurt(this.damageSources().mobProjectile(this, livingOwner), directHitDamage);
|
livingTarget.hurt(this.damageSources().mobProjectile(this, livingOwner), directHitDamage);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
+171
-48
@@ -132,7 +132,13 @@ public class KingEntity extends RoyalBossEntity {
|
|||||||
private static final int COMBAT_WAYPOINT_VARIANCE = 35;
|
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_MIN_TICKS = 18;
|
||||||
private static final int GROUND_COMBAT_WAYPOINT_VARIANCE = 12;
|
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
|
@Nullable
|
||||||
private Vec3 patrolCenter;
|
private Vec3 patrolCenter;
|
||||||
@Nullable
|
@Nullable
|
||||||
@@ -183,6 +189,7 @@ public class KingEntity extends RoyalBossEntity {
|
|||||||
@Nullable
|
@Nullable
|
||||||
private Vec3 kingGroundCombatWaypoint;
|
private Vec3 kingGroundCombatWaypoint;
|
||||||
private int kingStrafeDirection = 1;
|
private int kingStrafeDirection = 1;
|
||||||
|
private int kingLandingRetryTicks;
|
||||||
private int royalBoundaryGraceTicks;
|
private int royalBoundaryGraceTicks;
|
||||||
@Nullable
|
@Nullable
|
||||||
private ServerPlayer exileTarget;
|
private ServerPlayer exileTarget;
|
||||||
@@ -255,6 +262,35 @@ public class KingEntity extends RoyalBossEntity {
|
|||||||
return this.royalChargeActive;
|
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
|
@Override
|
||||||
protected void onRoyalPhaseTransitionStarted(Phase previousPhase, Phase nextPhase) {
|
protected void onRoyalPhaseTransitionStarted(Phase previousPhase, Phase nextPhase) {
|
||||||
this.attackScheduler.cancel(RoyalAttackLane.MOVEMENT);
|
this.attackScheduler.cancel(RoyalAttackLane.MOVEMENT);
|
||||||
@@ -477,6 +513,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) {
|
private void chainLightning(ServerLevel level, Vec3 origin) {
|
||||||
java.util.Set<UUID> struck = new java.util.HashSet<>();
|
java.util.Set<UUID> struck = new java.util.HashSet<>();
|
||||||
Vec3 from = origin;
|
Vec3 from = origin;
|
||||||
@@ -486,7 +530,7 @@ public class KingEntity extends RoyalBossEntity {
|
|||||||
double bestScore = Double.MAX_VALUE;
|
double bestScore = Double.MAX_VALUE;
|
||||||
for (LivingEntity candidate : level.getEntitiesOfClass(LivingEntity.class,
|
for (LivingEntity candidate : level.getEntitiesOfClass(LivingEntity.class,
|
||||||
new AABB(from, from).inflate(CHAIN_LIGHTNING_JUMP_RANGE),
|
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())) {
|
if (struck.contains(candidate.getUUID())) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -566,17 +610,26 @@ public class KingEntity extends RoyalBossEntity {
|
|||||||
if (this.royalChargeOpeningDelayTicks > 0) {
|
if (this.royalChargeOpeningDelayTicks > 0) {
|
||||||
this.royalChargeOpeningDelayTicks--;
|
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()) {
|
if (this.isRoyalPhaseTransitionActive()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
LivingEntity target = this.getTarget();
|
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.trackBehavior(target);
|
||||||
this.tickDecree(level, target);
|
this.tickDecree(level, target);
|
||||||
this.tickKingCombatMovement(target);
|
this.tickKingCombatMovement(target);
|
||||||
if (!this.blocksRoyalAttacksForMovement()) {
|
if (!this.blocksRoyalAttacksForMovement()) {
|
||||||
this.tickKingWholeBody(level, target);
|
this.tickKingWholeBody(level, target);
|
||||||
}
|
}
|
||||||
|
this.applyRoyalBoundaryPull();
|
||||||
this.stabilizeKingRotation();
|
this.stabilizeKingRotation();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -584,11 +637,15 @@ public class KingEntity extends RoyalBossEntity {
|
|||||||
if (this.isDeadOrDying() || !(this.level() instanceof ServerLevel)) {
|
if (this.isDeadOrDying() || !(this.level() instanceof ServerLevel)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (this.activeDecree != null) {
|
||||||
|
this.endDecree(null);
|
||||||
|
}
|
||||||
this.setRoyalFlying(true);
|
this.setRoyalFlying(true);
|
||||||
this.resetKingCombatMovement();
|
this.resetKingCombatMovement();
|
||||||
this.decreeRetreatPressure = false;
|
this.decreeRetreatPressure = false;
|
||||||
if (this.treePatrolBound && this.treePatrolCenter != null) {
|
if (this.treePatrolBound && this.treePatrolCenter != null) {
|
||||||
this.tickTreePatrol();
|
this.tickTreePatrol();
|
||||||
|
this.applyRoyalBoundaryPull();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (this.patrolCenter == null || this.position().distanceToSqr(this.patrolCenter) > 64.0D * 64.0D) {
|
if (this.patrolCenter == null || this.position().distanceToSqr(this.patrolCenter) > 64.0D * 64.0D) {
|
||||||
@@ -668,17 +725,6 @@ public class KingEntity extends RoyalBossEntity {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
double boundaryRadius = Math.max(32.0D, AntarchySettings.royalBoundaryRadius());
|
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();
|
LivingEntity target = this.getTarget();
|
||||||
if (target == null || !target.isAlive()) {
|
if (target == null || !target.isAlive()) {
|
||||||
this.royalBoundaryGraceTicks = 0;
|
this.royalBoundaryGraceTicks = 0;
|
||||||
@@ -703,6 +749,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() {
|
private double horizontalDistanceToTreeSqr() {
|
||||||
if (this.treePatrolCenter == null) {
|
if (this.treePatrolCenter == null) {
|
||||||
return 0.0D;
|
return 0.0D;
|
||||||
@@ -794,9 +859,14 @@ public class KingEntity extends RoyalBossEntity {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
boolean closeQuarters = this.activeDecree instanceof CloseQuartersDecree;
|
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);
|
this.beginKingLanding(target);
|
||||||
} else if (!closeQuarters && this.isRoyalFlying() && this.kingLandingStage == KingLandingStage.AERIAL
|
} else if (!closeQuarters && this.isRoyalFlying() && this.kingLandingStage == KingLandingStage.AERIAL
|
||||||
|
&& this.kingLandingRetryTicks <= 0
|
||||||
&& this.groundAssaultCooldownTicks-- <= 0 && target.onGround()) {
|
&& this.groundAssaultCooldownTicks-- <= 0 && target.onGround()) {
|
||||||
this.beginKingLanding(target);
|
this.beginKingLanding(target);
|
||||||
}
|
}
|
||||||
@@ -808,6 +878,7 @@ public class KingEntity extends RoyalBossEntity {
|
|||||||
this.tickKingLandingIndicator();
|
this.tickKingLandingIndicator();
|
||||||
if (--this.kingLandingStageTicks <= 0) {
|
if (--this.kingLandingStageTicks <= 0) {
|
||||||
this.kingLandingStage = KingLandingStage.DESCENT;
|
this.kingLandingStage = KingLandingStage.DESCENT;
|
||||||
|
this.kingLandingStageTicks = LANDING_DESCENT_TIMEOUT_TICKS;
|
||||||
this.playRoyalSound(AntarchySoundEvents.KING_WING_FLAP.get(), 0.72F);
|
this.playRoyalSound(AntarchySoundEvents.KING_WING_FLAP.get(), 0.72F);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
@@ -830,7 +901,10 @@ public class KingEntity extends RoyalBossEntity {
|
|||||||
|| Math.abs(localLandingDelta.y) <= 1.25D && horizontalDistanceSquared <= 4.0D;
|
|| Math.abs(localLandingDelta.y) <= 1.25D && horizontalDistanceSquared <= 4.0D;
|
||||||
if (reachedLanding) {
|
if (reachedLanding) {
|
||||||
this.setRoyalFlying(false);
|
this.setRoyalFlying(false);
|
||||||
|
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.setPos(this.getX(), landing.y, this.getZ());
|
||||||
|
}
|
||||||
this.kingLandingStage = KingLandingStage.IMPACT;
|
this.kingLandingStage = KingLandingStage.IMPACT;
|
||||||
this.kingLandingStageTicks = LANDING_IMPACT_TICKS;
|
this.kingLandingStageTicks = LANDING_IMPACT_TICKS;
|
||||||
this.triggerAnim("body_action", "stomp");
|
this.triggerAnim("body_action", "stomp");
|
||||||
@@ -838,6 +912,10 @@ public class KingEntity extends RoyalBossEntity {
|
|||||||
if (this.level() instanceof ServerLevel level) {
|
if (this.level() instanceof ServerLevel level) {
|
||||||
this.performStomp(level, target);
|
this.performStomp(level, target);
|
||||||
}
|
}
|
||||||
|
} else if (--this.kingLandingStageTicks <= 0) {
|
||||||
|
this.resetKingCombatMovement();
|
||||||
|
this.kingLandingRetryTicks = LANDING_RETRY_TICKS;
|
||||||
|
this.groundAssaultCooldownTicks = Math.max(this.groundAssaultCooldownTicks, 160);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -865,8 +943,12 @@ public class KingEntity extends RoyalBossEntity {
|
|||||||
this.getMoveControl().setWantedPosition(this.kingGroundCombatWaypoint.x,
|
this.getMoveControl().setWantedPosition(this.kingGroundCombatWaypoint.x,
|
||||||
this.kingGroundCombatWaypoint.y, this.kingGroundCombatWaypoint.z, 1.25D);
|
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);
|
this.beginKingTakeoff(target);
|
||||||
|
if (targetOutOfReach) {
|
||||||
|
this.kingLandingRetryTicks = 100;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -916,6 +998,7 @@ public class KingEntity extends RoyalBossEntity {
|
|||||||
// The next aerial tick will retry the expanded landing search.
|
// The next aerial tick will retry the expanded landing search.
|
||||||
this.kingLandingWaypoint = null;
|
this.kingLandingWaypoint = null;
|
||||||
this.groundAssaultCooldownTicks = 100;
|
this.groundAssaultCooldownTicks = 100;
|
||||||
|
this.kingLandingRetryTicks = LANDING_RETRY_TICKS;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -957,7 +1040,9 @@ public class KingEntity extends RoyalBossEntity {
|
|||||||
private Vec3 findKingLandingPoint(LivingEntity target) {
|
private Vec3 findKingLandingPoint(LivingEntity target) {
|
||||||
Vec3 away = this.position().subtract(target.position()).multiply(1.0D, 0.0D, 1.0D);
|
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 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++) {
|
for (int angleIndex = 0; angleIndex < 16; angleIndex++) {
|
||||||
double angle = baseAngle + angleIndex * Mth.TWO_PI / 16.0D;
|
double angle = baseAngle + angleIndex * Mth.TWO_PI / 16.0D;
|
||||||
for (double radius : radii) {
|
for (double radius : radii) {
|
||||||
@@ -965,18 +1050,27 @@ public class KingEntity extends RoyalBossEntity {
|
|||||||
double z = target.getZ() + Math.sin(angle) * radius;
|
double z = target.getZ() + Math.sin(angle) * radius;
|
||||||
double y = GravityApi.isGravityInverted(this)
|
double y = GravityApi.isGravityInverted(this)
|
||||||
? target.position().y - 0.1D
|
? 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);
|
Vec3 candidate = new Vec3(x, y, z);
|
||||||
AABB box = this.getBoundingBox().move(
|
AABB box = this.getBoundingBox().move(
|
||||||
candidate.x - this.getX(), candidate.y - this.getY(), candidate.z - this.getZ());
|
candidate.x - this.getX(), candidate.y - this.getY(), candidate.z - this.getZ());
|
||||||
if (this.level().noCollision(this, box)) {
|
if (!this.level().noCollision(this, box)) {
|
||||||
return candidate;
|
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)
|
double fallbackY = GravityApi.isGravityInverted(this)
|
||||||
? target.position().y - 0.1D
|
? 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());
|
Vec3 fallback = new Vec3(target.getX(), fallbackY, target.getZ());
|
||||||
AABB fallbackBox = this.getBoundingBox().move(
|
AABB fallbackBox = this.getBoundingBox().move(
|
||||||
fallback.x - this.getX(), fallback.y - this.getY(), fallback.z - this.getZ());
|
fallback.x - this.getX(), fallback.y - this.getY(), fallback.z - this.getZ());
|
||||||
@@ -995,6 +1089,26 @@ public class KingEntity extends RoyalBossEntity {
|
|||||||
return this.level().noCollision(this, directBox) ? direct : null;
|
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) {
|
private void selectKingCombatWaypoint(LivingEntity target) {
|
||||||
Vec3 radial = this.position().subtract(target.position()).multiply(1.0D, 0.0D, 1.0D);
|
Vec3 radial = this.position().subtract(target.position()).multiply(1.0D, 0.0D, 1.0D);
|
||||||
if (radial.lengthSqr() < 1.0E-4D) {
|
if (radial.lengthSqr() < 1.0E-4D) {
|
||||||
@@ -1015,9 +1129,7 @@ public class KingEntity extends RoyalBossEntity {
|
|||||||
double candidateRange = range + (angleAttempt % 2 == 0 ? 0.0D : -6.0D);
|
double candidateRange = range + (angleAttempt % 2 == 0 ? 0.0D : -6.0D);
|
||||||
double x = target.getX() + Math.cos(angle) * candidateRange;
|
double x = target.getX() + Math.cos(angle) * candidateRange;
|
||||||
double z = target.getZ() + Math.sin(angle) * candidateRange;
|
double z = target.getZ() + Math.sin(angle) * candidateRange;
|
||||||
double groundY = this.groundYBelow(x, z);
|
double y = this.kingAerialWaypointY(x, z, target, hoverOffset);
|
||||||
double y = Mth.clamp(target.getY() + hoverOffset,
|
|
||||||
groundY + FLYING_MIN_HOVER, groundY + FLYING_MAX_HOVER_ABOVE_GROUND);
|
|
||||||
Vec3 candidate = new Vec3(x, y, z);
|
Vec3 candidate = new Vec3(x, y, z);
|
||||||
if (treeBoundCombat) {
|
if (treeBoundCombat) {
|
||||||
double dx = x - this.treePatrolCenter.x;
|
double dx = x - this.treePatrolCenter.x;
|
||||||
@@ -1037,9 +1149,7 @@ public class KingEntity extends RoyalBossEntity {
|
|||||||
double angle = currentAngle + this.kingStrafeDirection * COMBAT_ORBIT_ANGLE_STEP;
|
double angle = currentAngle + this.kingStrafeDirection * COMBAT_ORBIT_ANGLE_STEP;
|
||||||
double x = target.getX() + Math.cos(angle) * range;
|
double x = target.getX() + Math.cos(angle) * range;
|
||||||
double z = target.getZ() + Math.sin(angle) * range;
|
double z = target.getZ() + Math.sin(angle) * range;
|
||||||
double groundY = this.groundYBelow(x, z);
|
double y = this.kingAerialWaypointY(x, z, target, hoverOffset);
|
||||||
double y = Mth.clamp(target.getY() + hoverOffset,
|
|
||||||
groundY + FLYING_MIN_HOVER, groundY + FLYING_MAX_HOVER_ABOVE_GROUND);
|
|
||||||
Vec3 candidate = new Vec3(x, y, z);
|
Vec3 candidate = new Vec3(x, y, z);
|
||||||
double dx = candidate.x - this.treePatrolCenter.x;
|
double dx = candidate.x - this.treePatrolCenter.x;
|
||||||
double dz = candidate.z - this.treePatrolCenter.z;
|
double dz = candidate.z - this.treePatrolCenter.z;
|
||||||
@@ -1052,9 +1162,7 @@ public class KingEntity extends RoyalBossEntity {
|
|||||||
double angle = currentAngle + this.kingStrafeDirection * COMBAT_ORBIT_ANGLE_STEP;
|
double angle = currentAngle + this.kingStrafeDirection * COMBAT_ORBIT_ANGLE_STEP;
|
||||||
double x = target.getX() + Math.cos(angle) * range;
|
double x = target.getX() + Math.cos(angle) * range;
|
||||||
double z = target.getZ() + Math.sin(angle) * range;
|
double z = target.getZ() + Math.sin(angle) * range;
|
||||||
double groundY = this.groundYBelow(x, z);
|
double y = this.kingAerialWaypointY(x, z, target, hoverOffset);
|
||||||
double y = Mth.clamp(target.getY() + hoverOffset,
|
|
||||||
groundY + FLYING_MIN_HOVER, groundY + FLYING_MAX_HOVER_ABOVE_GROUND);
|
|
||||||
orbitWaypoint = new Vec3(x, y, z);
|
orbitWaypoint = new Vec3(x, y, z);
|
||||||
if (treeBoundCombat) {
|
if (treeBoundCombat) {
|
||||||
orbitWaypoint = this.findTreePatrolWaypoint(this.treePatrolCenter, this.treePatrolAngle);
|
orbitWaypoint = this.findTreePatrolWaypoint(this.treePatrolCenter, this.treePatrolAngle);
|
||||||
@@ -1069,6 +1177,9 @@ public class KingEntity extends RoyalBossEntity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void stabilizeKingRotation() {
|
private void stabilizeKingRotation() {
|
||||||
|
if (this.royalFacingPoint() != null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
Vec3 movement = this.getDeltaMovement().multiply(1.0D, 0.0D, 1.0D);
|
Vec3 movement = this.getDeltaMovement().multiply(1.0D, 0.0D, 1.0D);
|
||||||
if (movement.lengthSqr() < 0.0025D) {
|
if (movement.lengthSqr() < 0.0025D) {
|
||||||
return;
|
return;
|
||||||
@@ -1102,16 +1213,16 @@ public class KingEntity extends RoyalBossEntity {
|
|||||||
radial = radial.normalize();
|
radial = radial.normalize();
|
||||||
Vec3 tangent = new Vec3(-radial.z * this.kingStrafeDirection, 0.0D,
|
Vec3 tangent = new Vec3(-radial.z * this.kingStrafeDirection, 0.0D,
|
||||||
radial.x * this.kingStrafeDirection);
|
radial.x * this.kingStrafeDirection);
|
||||||
double distance = 4.0D + this.random.nextDouble() * 3.0D;
|
double distance = GROUND_COMBAT_BITE_DISTANCE + this.random.nextDouble() * GROUND_COMBAT_BITE_VARIANCE;
|
||||||
Vec3 candidate = target.position().add(radial.scale(distance)).add(tangent.scale(2.5D));
|
Vec3 candidate = target.position().add(radial.scale(distance)).add(tangent.scale(4.0D));
|
||||||
Vec3 waypoint = new Vec3(candidate.x, target.getY(), candidate.z);
|
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());
|
AABB box = this.getBoundingBox().move(waypoint.x - this.getX(), waypoint.y - this.getY(), waypoint.z - this.getZ());
|
||||||
if (!this.level().noCollision(this, box)) {
|
if (!this.level().noCollision(this, box)) {
|
||||||
waypoint = target.position().add(radial.scale(distance));
|
Vec3 radialOnly = target.position().add(radial.scale(distance));
|
||||||
waypoint = new Vec3(waypoint.x, target.getY(), waypoint.z);
|
waypoint = this.kingGroundPoint(radialOnly.x, radialOnly.z, target);
|
||||||
box = this.getBoundingBox().move(waypoint.x - this.getX(), waypoint.y - this.getY(), waypoint.z - this.getZ());
|
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.kingGroundCombatWaypointTicks = GROUND_COMBAT_WAYPOINT_MIN_TICKS
|
||||||
+ this.random.nextInt(GROUND_COMBAT_WAYPOINT_VARIANCE + 1);
|
+ this.random.nextInt(GROUND_COMBAT_WAYPOINT_VARIANCE + 1);
|
||||||
if (this.random.nextInt(4) == 0) {
|
if (this.random.nextInt(4) == 0) {
|
||||||
@@ -1119,6 +1230,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) {
|
public void requestCloseQuartersAssault(LivingEntity target) {
|
||||||
if (target != null && target.isAlive() && this.isRoyalFlying()
|
if (target != null && target.isAlive() && this.isRoyalFlying()
|
||||||
&& this.kingLandingStage == KingLandingStage.AERIAL) {
|
&& this.kingLandingStage == KingLandingStage.AERIAL) {
|
||||||
@@ -1441,11 +1559,9 @@ public class KingEntity extends RoyalBossEntity {
|
|||||||
this.bumpBehavior(Behavior.SPRINTING, target.isSprinting() ? 1 : -1);
|
this.bumpBehavior(Behavior.SPRINTING, target.isSprinting() ? 1 : -1);
|
||||||
this.bumpBehavior(Behavior.HUGGING, distance <= 10.0D ? 1 : -1);
|
this.bumpBehavior(Behavior.HUGGING, distance <= 10.0D ? 1 : -1);
|
||||||
this.bumpBehavior(Behavior.KEEPING_FAR, distance >= 22.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
|
this.bumpBehavior(Behavior.RETREATING, distance > 20.0D && target.getDeltaMovement().horizontalDistanceSqr() > 0.02D
|
||||||
&& target.position().subtract(this.position()).dot(target.getDeltaMovement()) > 0.0D ? 1 : -1);
|
&& target.position().subtract(this.position()).dot(target.getDeltaMovement()) > 0.0D ? 1 : -1);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private void bumpBehavior(Behavior behavior, int delta) {
|
private void bumpBehavior(Behavior behavior, int delta) {
|
||||||
int updated = Math.max(0, Math.min(BEHAVIOR_SCORE_CAP, this.behaviorScores.getOrDefault(behavior, 0) + delta));
|
int updated = Math.max(0, Math.min(BEHAVIOR_SCORE_CAP, this.behaviorScores.getOrDefault(behavior, 0) + delta));
|
||||||
@@ -1716,10 +1832,6 @@ public class KingEntity extends RoyalBossEntity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void tickKingElementalProjectiles(ServerLevel level, LivingEntity target) {
|
private void tickKingElementalProjectiles(ServerLevel level, LivingEntity target) {
|
||||||
if (this.fireballCooldownTicks > 0) this.fireballCooldownTicks--;
|
|
||||||
if (this.iceballCooldownTicks > 0) this.iceballCooldownTicks--;
|
|
||||||
if (this.iceSpikeCooldownTicks > 0) this.iceSpikeCooldownTicks--;
|
|
||||||
|
|
||||||
double distance = this.distanceTo(target);
|
double distance = this.distanceTo(target);
|
||||||
String[] candidates = {"fireball", "iceball", "ice_spikes"};
|
String[] candidates = {"fireball", "iceball", "ice_spikes"};
|
||||||
String selected = this.attackScheduler.chooseWeighted(candidates, id -> {
|
String selected = this.attackScheduler.chooseWeighted(candidates, id -> {
|
||||||
@@ -1797,14 +1909,25 @@ public class KingEntity extends RoyalBossEntity {
|
|||||||
Vec3 start = this.position();
|
Vec3 start = this.position();
|
||||||
Vec3 end = target.position();
|
Vec3 end = target.position();
|
||||||
Vec3 line = end.subtract(start).multiply(1.0D, 0.0D, 1.0D);
|
Vec3 line = end.subtract(start).multiply(1.0D, 0.0D, 1.0D);
|
||||||
if (line.lengthSqr() < 1.0E-4D) return;
|
double reach = line.length();
|
||||||
line = line.normalize();
|
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);
|
Vec3 side = new Vec3(-line.z, 0.0D, line.x);
|
||||||
for (int i = 1; i <= 5; i++) {
|
double firstDistance = Math.max(4.0D, reach - 16.0D);
|
||||||
double distance = i * 4.0D;
|
for (int i = 0; i < 5; i++) {
|
||||||
|
double distance = firstDistance + i * 4.0D;
|
||||||
for (int lane = -1; lane <= 1; lane++) {
|
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)));
|
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) + 0.1D;
|
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);
|
RoyalIceSpikeEntity spike = RoyalIceSpikeEntity.create(level, new Vec3(point.x, y, point.z), this);
|
||||||
level.addFreshEntity(spike);
|
level.addFreshEntity(spike);
|
||||||
}
|
}
|
||||||
|
|||||||
+129
@@ -3,13 +3,31 @@ package com.craisinlord.antarchy.content.entity.royal;
|
|||||||
import com.craisinlord.antarchy.config.AntarchySettings;
|
import com.craisinlord.antarchy.config.AntarchySettings;
|
||||||
import com.craisinlord.antarchy.content.AntarchyObjects;
|
import com.craisinlord.antarchy.content.AntarchyObjects;
|
||||||
import com.craisinlord.antarchy.content.AntarchySoundEvents;
|
import com.craisinlord.antarchy.content.AntarchySoundEvents;
|
||||||
|
import com.craisinlord.antarchy.content.effect.RoyalEffectEligibility;
|
||||||
|
import com.craisinlord.antarchy.content.effect.RoyalEffectHooks;
|
||||||
import com.craisinlord.antarchy.content.entity.royal.beam.RoyalBeamElement;
|
import com.craisinlord.antarchy.content.entity.royal.beam.RoyalBeamElement;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.List;
|
||||||
|
import net.minecraft.core.particles.DustParticleOptions;
|
||||||
|
import net.minecraft.core.particles.ParticleOptions;
|
||||||
|
import net.minecraft.core.particles.ParticleTypes;
|
||||||
|
import net.minecraft.server.level.ServerLevel;
|
||||||
import net.minecraft.sounds.SoundEvent;
|
import net.minecraft.sounds.SoundEvent;
|
||||||
|
import net.minecraft.util.Mth;
|
||||||
|
import net.minecraft.world.effect.MobEffectInstance;
|
||||||
import net.minecraft.world.entity.EntityType;
|
import net.minecraft.world.entity.EntityType;
|
||||||
|
import net.minecraft.world.entity.LivingEntity;
|
||||||
|
import net.minecraft.world.entity.Mob;
|
||||||
import net.minecraft.world.entity.ai.attributes.AttributeSupplier;
|
import net.minecraft.world.entity.ai.attributes.AttributeSupplier;
|
||||||
|
import net.minecraft.world.entity.monster.Enemy;
|
||||||
import net.minecraft.world.level.Level;
|
import net.minecraft.world.level.Level;
|
||||||
|
import net.minecraft.world.phys.Vec3;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import org.joml.Vector3f;
|
||||||
|
|
||||||
public class PrinceEntity extends RoyalMountEntity {
|
public class PrinceEntity extends RoyalMountEntity {
|
||||||
|
private static final DustParticleOptions COMMAND_DUST = new DustParticleOptions(new Vector3f(1.0F, 0.75F, 0.15F), 1.6F);
|
||||||
|
|
||||||
public PrinceEntity(EntityType<? extends PrinceEntity> entityType, Level level) {
|
public PrinceEntity(EntityType<? extends PrinceEntity> entityType, Level level) {
|
||||||
super(entityType, level);
|
super(entityType, level);
|
||||||
}
|
}
|
||||||
@@ -40,6 +58,117 @@ public class PrinceEntity extends RoyalMountEntity {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ParticleOptions beamWindupParticle() {
|
||||||
|
return switch (this.getBeamElement()) {
|
||||||
|
case FIRE -> ParticleTypes.FLAME;
|
||||||
|
case ICE -> ParticleTypes.SNOWFLAKE;
|
||||||
|
default -> ParticleTypes.ELECTRIC_SPARK;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected float beamBaseDamage() {
|
||||||
|
return 12.0F;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean prefersAerialCombat() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double preferredCombatRange() {
|
||||||
|
return 0.0D;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected float biteWeight() {
|
||||||
|
return 3.0F;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected float spitWeight() {
|
||||||
|
return 1.0F;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected float beamWeight() {
|
||||||
|
return 1.2F;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected float signatureWeight() {
|
||||||
|
return 4.0F;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int signatureCooldownTicks() {
|
||||||
|
return 360;
|
||||||
|
}
|
||||||
|
|
||||||
|
private double commandRadius() {
|
||||||
|
return 14.0D + 2.0D * this.getGrowthScale();
|
||||||
|
}
|
||||||
|
|
||||||
|
private int commandLimit() {
|
||||||
|
return 3 + Mth.floor(this.getGrowthScale());
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Mob> commandCandidates(Vec3 center) {
|
||||||
|
double radius = this.commandRadius();
|
||||||
|
return this.level().getEntitiesOfClass(Mob.class, this.getBoundingBox().inflate(radius), mob ->
|
||||||
|
mob != this && mob.isAlive() && mob instanceof Enemy && !mob.isAlliedTo(this)
|
||||||
|
&& !(mob instanceof RoyalBossEntity) && RoyalEffectEligibility.canApplyCommanded(mob)
|
||||||
|
&& RoyalEffectHooks.commandedHolder() != null
|
||||||
|
&& !mob.hasEffect(RoyalEffectHooks.commandedHolder())
|
||||||
|
&& mob.distanceToSqr(center) <= radius * radius);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean isSignatureUseful(LivingEntity target) {
|
||||||
|
return RoyalEffectHooks.commandedHolder() != null && this.commandCandidates(this.position()).size() >= 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void signatureTelegraph(ServerLevel level, int tick, @Nullable LivingEntity target, @Nullable Vec3 aimPoint) {
|
||||||
|
if (tick % 3 != 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
double radius = (this.getBbWidth() * 0.5D + 1.0D) + tick * 0.25D * this.getGrowthScale();
|
||||||
|
for (int i = 0; i < 16; i++) {
|
||||||
|
double angle = i * Mth.TWO_PI / 16.0D + tick * 0.1D;
|
||||||
|
level.sendParticles(COMMAND_DUST, this.getX() + Math.cos(angle) * radius, this.getY() + 0.3D,
|
||||||
|
this.getZ() + Math.sin(angle) * radius, 1, 0.0D, 0.05D, 0.0D, 0.0D);
|
||||||
|
}
|
||||||
|
if (tick == 3) {
|
||||||
|
level.sendParticles(ParticleTypes.ENCHANTED_HIT, this.getX(), this.getY() + this.getBbHeight(), this.getZ(),
|
||||||
|
20, this.getBbWidth() * 0.3D, 0.3D, this.getBbWidth() * 0.3D, 0.1D);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean performSignature(ServerLevel level, @Nullable LivingEntity target, @Nullable Vec3 aimPoint, boolean ridden) {
|
||||||
|
if (RoyalEffectHooks.commandedHolder() == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Vec3 center = this.position();
|
||||||
|
List<Mob> candidates = this.commandCandidates(center);
|
||||||
|
candidates.sort(Comparator.comparingDouble(mob -> mob.distanceToSqr(center)));
|
||||||
|
int applied = 0;
|
||||||
|
for (Mob mob : candidates) {
|
||||||
|
if (applied >= this.commandLimit()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (mob.addEffect(new MobEffectInstance(RoyalEffectHooks.commandedHolder(), 300 + 20 * Mth.floor(this.getGrowthScale())), this)) {
|
||||||
|
applied++;
|
||||||
|
level.sendParticles(COMMAND_DUST, mob.getX(), mob.getY() + mob.getBbHeight() + 0.3D, mob.getZ(), 8, 0.25D, 0.2D, 0.25D, 0.0D);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
level.sendParticles(ParticleTypes.SONIC_BOOM, this.getX(), this.getY() + this.getBbHeight() * 0.6D, this.getZ(), 1, 0.0D, 0.0D, 0.0D, 0.0D);
|
||||||
|
return applied > 0;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String geoName() {
|
protected String geoName() {
|
||||||
return "prince";
|
return "prince";
|
||||||
|
|||||||
+122
-5
@@ -4,10 +4,18 @@ import com.craisinlord.antarchy.config.AntarchySettings;
|
|||||||
import com.craisinlord.antarchy.content.AntarchyObjects;
|
import com.craisinlord.antarchy.content.AntarchyObjects;
|
||||||
import com.craisinlord.antarchy.content.AntarchySoundEvents;
|
import com.craisinlord.antarchy.content.AntarchySoundEvents;
|
||||||
import com.craisinlord.antarchy.content.entity.royal.beam.RoyalBeamElement;
|
import com.craisinlord.antarchy.content.entity.royal.beam.RoyalBeamElement;
|
||||||
|
import com.craisinlord.anthology.time.TimeDilationApi;
|
||||||
|
import net.minecraft.core.particles.ParticleOptions;
|
||||||
|
import net.minecraft.core.particles.ParticleTypes;
|
||||||
|
import net.minecraft.server.level.ServerLevel;
|
||||||
import net.minecraft.sounds.SoundEvent;
|
import net.minecraft.sounds.SoundEvent;
|
||||||
|
import net.minecraft.util.Mth;
|
||||||
import net.minecraft.world.entity.EntityType;
|
import net.minecraft.world.entity.EntityType;
|
||||||
|
import net.minecraft.world.entity.LivingEntity;
|
||||||
import net.minecraft.world.entity.ai.attributes.AttributeSupplier;
|
import net.minecraft.world.entity.ai.attributes.AttributeSupplier;
|
||||||
import net.minecraft.world.level.Level;
|
import net.minecraft.world.level.Level;
|
||||||
|
import net.minecraft.world.phys.Vec3;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
public class PrincessEntity extends RoyalMountEntity {
|
public class PrincessEntity extends RoyalMountEntity {
|
||||||
public PrincessEntity(EntityType<? extends PrincessEntity> entityType, Level level) {
|
public PrincessEntity(EntityType<? extends PrincessEntity> entityType, Level level) {
|
||||||
@@ -24,11 +32,120 @@ public class PrincessEntity extends RoyalMountEntity {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected RoyalBeamElement boltElement(int lateralIndex) {
|
protected RoyalBeamElement boltElement(int lateralIndex) {
|
||||||
return switch (lateralIndex) {
|
return RoyalBeamElement.GENERIC;
|
||||||
case -1 -> RoyalBeamElement.FIRE;
|
}
|
||||||
case 1 -> RoyalBeamElement.ICE;
|
|
||||||
default -> RoyalBeamElement.LIGHTNING;
|
@Override
|
||||||
};
|
protected Vec3 boltOrigin(int lateralIndex) {
|
||||||
|
float scale = this.getGrowthScale();
|
||||||
|
Vec3 forward = Vec3.directionFromRotation(0.0F, this.getYRot());
|
||||||
|
Vec3 side = new Vec3(-forward.z, 0.0D, forward.x);
|
||||||
|
return this.position()
|
||||||
|
.add(0.0D, (lateralIndex == 0 ? 0.4D : 0.33D) * scale, 0.0D)
|
||||||
|
.add(forward.scale((lateralIndex == 0 ? 0.32D : 0.23D) * scale + this.getBbWidth() * 0.15D))
|
||||||
|
.add(side.scale(lateralIndex * 0.24D * scale));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int nextBeamHead(int previous) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ParticleOptions beamWindupParticle() {
|
||||||
|
return ParticleTypes.REVERSE_PORTAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected float beamBaseDamage() {
|
||||||
|
return 10.0F;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean prefersAerialCombat() {
|
||||||
|
return this.canFly();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double preferredCombatRange() {
|
||||||
|
return 8.0D + 1.5D * this.getGrowthScale();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected float biteWeight() {
|
||||||
|
return 1.0F;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected float spitWeight() {
|
||||||
|
return 2.5F;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected float beamWeight() {
|
||||||
|
return 1.6F;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected float signatureWeight() {
|
||||||
|
return 2.5F;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int signatureCooldownTicks() {
|
||||||
|
return 300;
|
||||||
|
}
|
||||||
|
|
||||||
|
private double fieldRadius() {
|
||||||
|
return 5.0D + 0.8D * this.getGrowthScale();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean ownerInside(Vec3 center) {
|
||||||
|
LivingEntity owner = this.getOwner();
|
||||||
|
if (owner == null || owner.level() != this.level() || this.hasPassenger(owner)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
double clearance = this.fieldRadius() + 1.5D;
|
||||||
|
return owner.position().distanceToSqr(center) < clearance * clearance;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean isSignatureUseful(LivingEntity target) {
|
||||||
|
return this.combatGap(target) < 10.0D + 1.5D * this.getGrowthScale() && !this.ownerInside(target.position());
|
||||||
|
}
|
||||||
|
|
||||||
|
private Vec3 fieldCenter(@Nullable LivingEntity target, @Nullable Vec3 aimPoint) {
|
||||||
|
if (target != null && target.isAlive()) {
|
||||||
|
return target.position();
|
||||||
|
}
|
||||||
|
return aimPoint != null ? aimPoint : this.position().add(Vec3.directionFromRotation(0.0F, this.getYRot()).scale(this.getBbWidth() + 6.0D));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void signatureTelegraph(ServerLevel level, int tick, @Nullable LivingEntity target, @Nullable Vec3 aimPoint) {
|
||||||
|
if (tick % 2 != 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Vec3 center = this.fieldCenter(target, aimPoint);
|
||||||
|
double radius = this.fieldRadius() * (1.0D - tick / 24.0D) + 0.5D;
|
||||||
|
for (int i = 0; i < 12; i++) {
|
||||||
|
double angle = i * Mth.TWO_PI / 12.0D - tick * 0.15D;
|
||||||
|
level.sendParticles(ParticleTypes.REVERSE_PORTAL, center.x + Math.cos(angle) * radius, center.y + 0.2D,
|
||||||
|
center.z + Math.sin(angle) * radius, 1, 0.0D, 0.05D, 0.0D, 0.0D);
|
||||||
|
}
|
||||||
|
Vec3 orb = this.boltOrigin(0);
|
||||||
|
level.sendParticles(ParticleTypes.END_ROD, orb.x, orb.y, orb.z, 2, 0.1D, 0.1D, 0.1D, 0.01D);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean performSignature(ServerLevel level, @Nullable LivingEntity target, @Nullable Vec3 aimPoint, boolean ridden) {
|
||||||
|
Vec3 center = this.fieldCenter(target, aimPoint);
|
||||||
|
if (this.ownerInside(center)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
TimeDilationApi.createField(level, center, this.fieldRadius(), 0.35D, 100 + 10 * Mth.floor(this.getGrowthScale()), this.getUUID());
|
||||||
|
level.sendParticles(ParticleTypes.END_ROD, center.x, center.y + 0.5D, center.z, 36, this.fieldRadius() * 0.3D, 0.6D, this.fieldRadius() * 0.3D, 0.03D);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
+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.AttributeModifier;
|
||||||
import net.minecraft.world.entity.ai.attributes.AttributeSupplier;
|
import net.minecraft.world.entity.ai.attributes.AttributeSupplier;
|
||||||
import net.minecraft.world.entity.ai.attributes.Attributes;
|
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.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.entity.MobSpawnType;
|
||||||
import net.minecraft.world.level.GameRules;
|
import net.minecraft.world.level.GameRules;
|
||||||
import net.minecraft.world.level.Level;
|
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_VOLLEY_DELAY = 18;
|
||||||
private static final int ACCELERATION_LANDING_DELAY = 55;
|
private static final int ACCELERATION_LANDING_DELAY = 55;
|
||||||
private static final int DIRECTED_LANDING_RETRY_TICKS = 60;
|
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 manticoreSummonCooldownTicks;
|
||||||
private int gravityStompCooldownTicks;
|
private int gravityStompCooldownTicks;
|
||||||
@@ -161,6 +170,7 @@ public class QueenEntity extends RoyalBossEntity {
|
|||||||
private Vec3 queenLandingWaypoint;
|
private Vec3 queenLandingWaypoint;
|
||||||
@Nullable
|
@Nullable
|
||||||
private Vec3 queenGroundChargeDirection;
|
private Vec3 queenGroundChargeDirection;
|
||||||
|
private final java.util.Set<UUID> queenGroundChargeHits = new java.util.HashSet<>();
|
||||||
@Nullable
|
@Nullable
|
||||||
private Phase pendingPhaseSignature;
|
private Phase pendingPhaseSignature;
|
||||||
private boolean pendingPhaseTwoCrushingGravity;
|
private boolean pendingPhaseTwoCrushingGravity;
|
||||||
@@ -295,11 +305,34 @@ public class QueenEntity extends RoyalBossEntity {
|
|||||||
@Override
|
@Override
|
||||||
protected double combatStandoffDistance() {
|
protected double combatStandoffDistance() {
|
||||||
if (this.queenLandingStage == QueenLandingStage.GROUND_COMBAT && !this.isRoyalFlying()) {
|
if (this.queenLandingStage == QueenLandingStage.GROUND_COMBAT && !this.isRoyalFlying()) {
|
||||||
return 0.0D;
|
return super.combatStandoffDistance();
|
||||||
}
|
}
|
||||||
return Math.max(super.combatStandoffDistance(), AntarchySettings.queenBeamMinimumRange() + 6.0D);
|
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
|
@Override
|
||||||
protected double biteDamageMultiplier() {
|
protected double biteDamageMultiplier() {
|
||||||
return super.biteDamageMultiplier() * 1.5D;
|
return super.biteDamageMultiplier() * 1.5D;
|
||||||
@@ -320,9 +353,13 @@ public class QueenEntity extends RoyalBossEntity {
|
|||||||
|
|
||||||
protected void registerRoyalTargetGoals() {
|
protected void registerRoyalTargetGoals() {
|
||||||
this.targetSelector.addGoal(1, new NearestAttackableTargetGoal<>(this, KingEntity.class, true));
|
this.targetSelector.addGoal(1, new NearestAttackableTargetGoal<>(this, KingEntity.class, true));
|
||||||
this.targetSelector.addGoal(2, new NearestAttackableTargetGoal<>(this, Player.class, true));
|
this.targetSelector.addGoal(2, new HurtByTargetGoal(this).setUnseenMemoryTicks(ROYAL_UNSEEN_MEMORY_TICKS));
|
||||||
this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(
|
this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, Player.class, true)
|
||||||
this, Mob.class, 10, true, false, this::canAttack));
|
.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
|
@Override
|
||||||
@@ -495,6 +532,9 @@ public class QueenEntity extends RoyalBossEntity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void tickQueenLandingSequence(LivingEntity target) {
|
private void tickQueenLandingSequence(LivingEntity target) {
|
||||||
|
if (this.queenLandingStage == QueenLandingStage.AERIAL && !this.isRoyalFlying()) {
|
||||||
|
this.setRoyalFlying(true);
|
||||||
|
}
|
||||||
if (this.queenLandingStage == QueenLandingStage.AERIAL && this.isRoyalFlying()
|
if (this.queenLandingStage == QueenLandingStage.AERIAL && this.isRoyalFlying()
|
||||||
&& !this.isQueenAbilityTelegraphActive()
|
&& !this.isQueenAbilityTelegraphActive()
|
||||||
&& this.queenLandingCooldownTicks-- <= 0) {
|
&& this.queenLandingCooldownTicks-- <= 0) {
|
||||||
@@ -529,7 +569,7 @@ public class QueenEntity extends RoyalBossEntity {
|
|||||||
this.performGravityStomp((ServerLevel) this.level(), target);
|
this.performGravityStomp((ServerLevel) this.level(), target);
|
||||||
} else if (--this.queenLandingStageTicks <= 0) {
|
} else if (--this.queenLandingStageTicks <= 0) {
|
||||||
this.resetQueenLandingSequence();
|
this.resetQueenLandingSequence();
|
||||||
this.queenLandingCooldownTicks = 40;
|
this.queenLandingCooldownTicks = LANDING_TIMEOUT_COOLDOWN_TICKS;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case IMPACT -> {
|
case IMPACT -> {
|
||||||
@@ -544,7 +584,8 @@ public class QueenEntity extends RoyalBossEntity {
|
|||||||
}
|
}
|
||||||
case GROUND_COMBAT -> {
|
case GROUND_COMBAT -> {
|
||||||
this.tickQueenGroundCharge(target);
|
this.tickQueenGroundCharge(target);
|
||||||
if (--this.queenLandingStageTicks <= 0) {
|
if (--this.queenLandingStageTicks <= 0
|
||||||
|
|| this.royalElevatedTargetTicks() >= ELEVATED_TARGET_TAKEOFF_TICKS) {
|
||||||
this.beginQueenTakeoff();
|
this.beginQueenTakeoff();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -577,7 +618,7 @@ public class QueenEntity extends RoyalBossEntity {
|
|||||||
double baseAngle = away.lengthSqr() > 1.0E-4D
|
double baseAngle = away.lengthSqr() > 1.0E-4D
|
||||||
? Math.atan2(away.z, away.x) : this.random.nextDouble() * Mth.TWO_PI;
|
? Math.atan2(away.z, away.x) : this.random.nextDouble() * Mth.TWO_PI;
|
||||||
boolean inverted = GravityApi.isGravityInverted(this);
|
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++) {
|
for (int angleIndex = 0; angleIndex < 12; angleIndex++) {
|
||||||
double angle = baseAngle + angleIndex * Mth.TWO_PI / 12.0D;
|
double angle = baseAngle + angleIndex * Mth.TWO_PI / 12.0D;
|
||||||
for (double radius : radii) {
|
for (double radius : radii) {
|
||||||
@@ -585,7 +626,7 @@ public class QueenEntity extends RoyalBossEntity {
|
|||||||
double z = target.getZ() + Math.sin(angle) * radius;
|
double z = target.getZ() + Math.sin(angle) * radius;
|
||||||
double y = inverted
|
double y = inverted
|
||||||
? target.position().y - 0.1D
|
? 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);
|
Vec3 candidate = new Vec3(x, y, z);
|
||||||
AABB box = this.getBoundingBox().move(
|
AABB box = this.getBoundingBox().move(
|
||||||
candidate.x - this.getX(), candidate.y - this.getY(), candidate.z - this.getZ());
|
candidate.x - this.getX(), candidate.y - this.getY(), candidate.z - this.getZ());
|
||||||
@@ -596,7 +637,7 @@ public class QueenEntity extends RoyalBossEntity {
|
|||||||
}
|
}
|
||||||
double fallbackY = inverted
|
double fallbackY = inverted
|
||||||
? target.position().y - 0.1D
|
? 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());
|
Vec3 fallback = new Vec3(target.getX(), fallbackY, target.getZ());
|
||||||
AABB fallbackBox = this.getBoundingBox().move(
|
AABB fallbackBox = this.getBoundingBox().move(
|
||||||
fallback.x - this.getX(), fallback.y - this.getY(), fallback.z - this.getZ());
|
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) {
|
if (direction.lengthSqr() > 1.0E-4D) {
|
||||||
this.queenGroundChargeDirection = direction.normalize();
|
this.queenGroundChargeDirection = direction.normalize();
|
||||||
this.queenGroundChargeTicks = GROUND_CHARGE_DURATION_TICKS;
|
this.queenGroundChargeTicks = GROUND_CHARGE_DURATION_TICKS;
|
||||||
|
this.queenGroundChargeHits.clear();
|
||||||
} else {
|
} else {
|
||||||
this.finishQueenGroundCharge();
|
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.setYRot((float) (Mth.atan2(-direction.x, direction.z) * Mth.RAD_TO_DEG));
|
||||||
this.yBodyRot = this.getYRot();
|
this.yBodyRot = this.getYRot();
|
||||||
this.hasImpulse = true;
|
this.hasImpulse = true;
|
||||||
|
if (this.level() instanceof ServerLevel level) {
|
||||||
|
this.applyQueenGroundChargeHits(level, direction);
|
||||||
|
}
|
||||||
if (--this.queenGroundChargeTicks <= 0) {
|
if (--this.queenGroundChargeTicks <= 0) {
|
||||||
this.finishQueenGroundCharge();
|
this.finishQueenGroundCharge();
|
||||||
}
|
}
|
||||||
@@ -688,7 +733,8 @@ public class QueenEntity extends RoyalBossEntity {
|
|||||||
}
|
}
|
||||||
if (this.queenGroundChargeCooldownTicks-- > 0
|
if (this.queenGroundChargeCooldownTicks-- > 0
|
||||||
|| target.position().multiply(1.0D, 0.0D, 1.0D)
|
|| 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;
|
return;
|
||||||
}
|
}
|
||||||
this.queenGroundChargeDirection = target.position().subtract(this.position())
|
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() {
|
private void finishQueenGroundCharge() {
|
||||||
this.queenGroundChargeTicks = 0;
|
this.queenGroundChargeTicks = 0;
|
||||||
this.queenGroundChargeWindupTicks = 0;
|
this.queenGroundChargeWindupTicks = 0;
|
||||||
@@ -710,6 +784,7 @@ public class QueenEntity extends RoyalBossEntity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void clearQueenGroundCharge() {
|
private void clearQueenGroundCharge() {
|
||||||
|
this.queenGroundChargeHits.clear();
|
||||||
this.queenGroundChargeCooldownTicks = 0;
|
this.queenGroundChargeCooldownTicks = 0;
|
||||||
this.queenGroundChargeWindupTicks = 0;
|
this.queenGroundChargeWindupTicks = 0;
|
||||||
this.queenGroundChargeTicks = 0;
|
this.queenGroundChargeTicks = 0;
|
||||||
@@ -1048,14 +1123,21 @@ public class QueenEntity extends RoyalBossEntity {
|
|||||||
: this.pendingPhaseSignature == Phase.THREE ? "acceleration"
|
: this.pendingPhaseSignature == Phase.THREE ? "acceleration"
|
||||||
: phase == Phase.TWO && this.pendingPhaseTwoCrushingGravity ? "crushing_gravity"
|
: phase == Phase.TWO && this.pendingPhaseTwoCrushingGravity ? "crushing_gravity"
|
||||||
: phase == Phase.THREE && this.pendingPhaseThreeChronosphere ? "queen_chronosphere" : null;
|
: phase == Phase.THREE && this.pendingPhaseThreeChronosphere ? "queen_chronosphere" : null;
|
||||||
String selected = forcedAttack != null
|
String chosen = forcedAttack != null
|
||||||
&& this.queenAttackReady(forcedAttack, phase, majorBusy)
|
&& this.queenAttackReady(forcedAttack, phase, majorBusy)
|
||||||
? forcedAttack : this.attackScheduler.chooseWeighted(candidates, id -> {
|
? forcedAttack : this.attackScheduler.chooseWeighted(candidates, id -> {
|
||||||
if (!this.queenAttackReady(id, phase, majorBusy)
|
if (!this.queenAttackReady(id, phase, majorBusy)
|
||||||
|| id.equals(this.lastQueenMajorAttack)) return 0;
|
|| id.equals(this.lastQueenMajorAttack)) return 0;
|
||||||
return this.queenAttackWeight(id, phase);
|
return this.queenAttackWeight(id, phase);
|
||||||
}, this.random);
|
}, 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) {
|
int cooldown = switch (selected) {
|
||||||
case "gravity_stomp" -> GRAVITY_STOMP_COOLDOWN;
|
case "gravity_stomp" -> GRAVITY_STOMP_COOLDOWN;
|
||||||
case "black_hole" -> BLACK_HOLE_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 radius = this.random.nextDouble() * GRAVITIC_UPHEAVAL_RADIUS;
|
||||||
double x = center.x + Math.cos(angle) * radius;
|
double x = center.x + Math.cos(angle) * radius;
|
||||||
double z = center.z + Math.sin(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);
|
BlockState state = level.getBlockState(pos);
|
||||||
if (!state.is(AntarchyTags.Blocks.QUEEN_GRABBABLE_BLOCKS) || level.getBlockEntity(pos) != null
|
if (!state.is(AntarchyTags.Blocks.QUEEN_GRABBABLE_BLOCKS) || level.getBlockEntity(pos) != null
|
||||||
|| state.getDestroySpeed(level, pos) < 0.0F || state.getCollisionShape(level, pos).isEmpty()) {
|
|| state.getDestroySpeed(level, pos) < 0.0F || state.getCollisionShape(level, pos).isEmpty()) {
|
||||||
|
|||||||
+17
@@ -87,6 +87,14 @@ public class RoyalBoltEntity extends AbstractHurtingProjectile {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean canHitEntity(Entity target) {
|
||||||
|
if (this.getOwner() instanceof RoyalMountEntity mount && mount.isFriendlyTo(target)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return super.canHitEntity(target);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onHitEntity(EntityHitResult result) {
|
protected void onHitEntity(EntityHitResult result) {
|
||||||
super.onHitEntity(result);
|
super.onHitEntity(result);
|
||||||
@@ -102,7 +110,13 @@ public class RoyalBoltEntity extends AbstractHurtingProjectile {
|
|||||||
&& royal.isTame() && other.isTame()) {
|
&& royal.isTame() && other.isTame()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (owner instanceof RoyalMountEntity mount && mount.isFriendlyTo(target)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
float damage = (float) AntarchySettings.royalBoltDamage();
|
float damage = (float) AntarchySettings.royalBoltDamage();
|
||||||
|
if (owner instanceof RoyalMountEntity mount) {
|
||||||
|
damage *= mount.projectileDamageMultiplier();
|
||||||
|
}
|
||||||
target.hurt(this.damageSources().mobProjectile(this, owner instanceof LivingEntity le ? le : null), damage);
|
target.hurt(this.damageSources().mobProjectile(this, owner instanceof LivingEntity le ? le : null), damage);
|
||||||
if (target instanceof LivingEntity living) {
|
if (target instanceof LivingEntity living) {
|
||||||
living.knockback(0.35D, this.getX() - target.getX(), this.getZ() - target.getZ());
|
living.knockback(0.35D, this.getX() - target.getX(), this.getZ() - target.getZ());
|
||||||
@@ -126,6 +140,9 @@ public class RoyalBoltEntity extends AbstractHurtingProjectile {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
case GENERIC -> {
|
case GENERIC -> {
|
||||||
|
if (owner instanceof PrincessEntity) {
|
||||||
|
living.addEffect(new MobEffectInstance(MobEffects.MOVEMENT_SLOWDOWN, 60, 1), owner);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+736
-28
@@ -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.ai.navigation.PathNavigation;
|
||||||
import net.minecraft.world.entity.monster.Monster;
|
import net.minecraft.world.entity.monster.Monster;
|
||||||
import net.minecraft.world.entity.player.Player;
|
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.level.Level;
|
||||||
import net.minecraft.world.phys.AABB;
|
import net.minecraft.world.phys.AABB;
|
||||||
|
import net.minecraft.world.phys.HitResult;
|
||||||
import net.minecraft.world.phys.Vec3;
|
import net.minecraft.world.phys.Vec3;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
import software.bernie.geckolib.animatable.GeoEntity;
|
import software.bernie.geckolib.animatable.GeoEntity;
|
||||||
@@ -111,6 +116,9 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
|
|||||||
private static final int RECOVERY_AFTER_BEAM_TICKS = 30;
|
private static final int RECOVERY_AFTER_BEAM_TICKS = 30;
|
||||||
private static final ResourceLocation MULTIPLAYER_DAMAGE_ID = ResourceLocation.fromNamespaceAndPath("antarchy", "royal_multiplayer_damage");
|
private static final ResourceLocation MULTIPLAYER_DAMAGE_ID = ResourceLocation.fromNamespaceAndPath("antarchy", "royal_multiplayer_damage");
|
||||||
private static final int OBSTRUCTION_CLEAR_INTERVAL_TICKS = 5;
|
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 double OBSTRUCTION_CLEAR_RADIUS = 3.5D;
|
||||||
private static final int OBSTRUCTION_CLEAR_MAX_BLOCKS = 24;
|
private static final int OBSTRUCTION_CLEAR_MAX_BLOCKS = 24;
|
||||||
private static final double OBSTRUCTION_CLEAR_MAX_RESISTANCE = 60.0D;
|
private static final double OBSTRUCTION_CLEAR_MAX_RESISTANCE = 60.0D;
|
||||||
@@ -120,6 +128,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_SIDE_Y = 10.25D;
|
||||||
private static final double BEAM_MUZZLE_LATERAL = 2.625D;
|
private static final double BEAM_MUZZLE_LATERAL = 2.625D;
|
||||||
private static final int HEAVY_BLOCK_CHECK_INTERVAL_TICKS = 4;
|
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_MIN_HOVER = 2.5D;
|
||||||
protected static final double FLYING_PREFERRED_HOVER = 6.0D;
|
protected static final double FLYING_PREFERRED_HOVER = 6.0D;
|
||||||
@@ -196,6 +234,16 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
|
|||||||
private boolean multiplayerScalingInitialized;
|
private boolean multiplayerScalingInitialized;
|
||||||
private final Set<UUID> encounterParticipants = new HashSet<>();
|
private final Set<UUID> encounterParticipants = new HashSet<>();
|
||||||
private double royalDamageMultiplier = 1.0D;
|
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
|
@Nullable
|
||||||
private Entity[] multipartParts;
|
private Entity[] multipartParts;
|
||||||
@@ -206,8 +254,8 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
|
|||||||
this.beamControllers[i] = new BeamController(this);
|
this.beamControllers[i] = new BeamController(this);
|
||||||
}
|
}
|
||||||
this.xpReward = 500;
|
this.xpReward = 500;
|
||||||
this.groundMoveControl = new MoveControl(this);
|
this.groundMoveControl = new RoyalGroundMoveControl(this);
|
||||||
this.flyingMoveControl = new FlyingMoveControl(this, 20, true);
|
this.flyingMoveControl = new RoyalFlyingMoveControl(this, 20);
|
||||||
this.groundNavigation = new GroundPathNavigation(this, level);
|
this.groundNavigation = new GroundPathNavigation(this, level);
|
||||||
FlyingPathNavigation flyingNavigation = new FlyingPathNavigation(this, level);
|
FlyingPathNavigation flyingNavigation = new FlyingPathNavigation(this, level);
|
||||||
flyingNavigation.setCanFloat(true);
|
flyingNavigation.setCanFloat(true);
|
||||||
@@ -216,6 +264,7 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
|
|||||||
this.moveControl = this.flyingMoveControl;
|
this.moveControl = this.flyingMoveControl;
|
||||||
this.navigation = this.flyingNavigation;
|
this.navigation = this.flyingNavigation;
|
||||||
} else {
|
} else {
|
||||||
|
this.moveControl = this.groundMoveControl;
|
||||||
this.navigation = this.groundNavigation;
|
this.navigation = this.groundNavigation;
|
||||||
}
|
}
|
||||||
this.bossEvent = new com.craisinlord.antarchy.content.boss.EntityLinkedServerBossEvent(
|
this.bossEvent = new com.craisinlord.antarchy.content.boss.EntityLinkedServerBossEvent(
|
||||||
@@ -386,8 +435,42 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void registerRoyalTargetGoals() {
|
protected void registerRoyalTargetGoals() {
|
||||||
this.targetSelector.addGoal(1, new HurtByTargetGoal(this));
|
this.targetSelector.addGoal(1, new HurtByTargetGoal(this).setUnseenMemoryTicks(ROYAL_UNSEEN_MEMORY_TICKS));
|
||||||
this.targetSelector.addGoal(2, new NearestAttackableTargetGoal<>(this, Player.class, true));
|
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) {
|
public boolean canDamageWithRoyalAttack(LivingEntity target) {
|
||||||
@@ -513,7 +596,7 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
|
|||||||
if (this.shouldClearObstruction()
|
if (this.shouldClearObstruction()
|
||||||
&& !this.level().isClientSide
|
&& !this.level().isClientSide
|
||||||
&& !this.isDeadOrDying()
|
&& !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()) {
|
&& this.isInWall()) {
|
||||||
RoyalBlockDestruction.destroySphere(
|
RoyalBlockDestruction.destroySphere(
|
||||||
(ServerLevel) this.level(),
|
(ServerLevel) this.level(),
|
||||||
@@ -576,13 +659,16 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
|
|||||||
for (RoyalHead head : this.heads) {
|
for (RoyalHead head : this.heads) {
|
||||||
head.setTarget(null);
|
head.setTarget(null);
|
||||||
}
|
}
|
||||||
|
this.resetRoyalCombatTracking();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.initializeMultiplayerScaling(primaryTarget);
|
this.initializeMultiplayerScaling(primaryTarget);
|
||||||
this.assignHeadTargets(primaryTarget);
|
this.assignHeadTargets(primaryTarget);
|
||||||
|
this.tickRoyalTargetElevation(primaryTarget);
|
||||||
|
|
||||||
this.tickCombatLocomotionMode(primaryTarget);
|
this.tickCombatLocomotionMode(primaryTarget);
|
||||||
|
this.tickRoyalStuckRecovery(primaryTarget);
|
||||||
boolean movementBlocksAttacks = this.blocksRoyalAttacksForMovement();
|
boolean movementBlocksAttacks = this.blocksRoyalAttacksForMovement();
|
||||||
if (!movementBlocksAttacks) {
|
if (!movementBlocksAttacks) {
|
||||||
boolean aeriallyStabilized = this.steerTowardTarget(primaryTarget);
|
boolean aeriallyStabilized = this.steerTowardTarget(primaryTarget);
|
||||||
@@ -596,6 +682,8 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.tickBodyCrush();
|
this.tickBodyCrush();
|
||||||
|
this.tickRoyalCloseRangeAnswer(primaryTarget);
|
||||||
|
this.tickRoyalConcealedTarget(primaryTarget);
|
||||||
|
|
||||||
Phase phase = this.phase();
|
Phase phase = this.phase();
|
||||||
int activeHeadAttacks = 0;
|
int activeHeadAttacks = 0;
|
||||||
@@ -920,15 +1008,18 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
|
|||||||
|
|
||||||
double verticalDistance = Math.abs(target.getY() + target.getBbHeight() * 0.5D
|
double verticalDistance = Math.abs(target.getY() + target.getBbHeight() * 0.5D
|
||||||
- (this.getY() + this.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.landingForCombat && verticalDistance > 6.0D) {
|
||||||
if (this.aerialCombatAnchor == null || this.aerialCombatAnchorTicks-- <= 0) {
|
if (this.aerialCombatAnchor == null || this.aerialCombatAnchorTicks-- <= 0) {
|
||||||
double anchorX = this.getX();
|
this.aerialCombatAnchor = new Vec3(wanted.x, wantedY, wanted.z);
|
||||||
double anchorZ = this.getZ();
|
|
||||||
if (horizontalDistance > 10.0D) {
|
|
||||||
anchorX = wanted.x;
|
|
||||||
anchorZ = wanted.z;
|
|
||||||
}
|
|
||||||
this.aerialCombatAnchor = new Vec3(anchorX, this.getY(), anchorZ);
|
|
||||||
this.aerialCombatAnchorTicks = 20;
|
this.aerialCombatAnchorTicks = 20;
|
||||||
}
|
}
|
||||||
Vec3 anchor = this.aerialCombatAnchor;
|
Vec3 anchor = this.aerialCombatAnchor;
|
||||||
@@ -938,15 +1029,6 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
|
|||||||
this.aerialCombatAnchor = null;
|
this.aerialCombatAnchor = null;
|
||||||
this.aerialCombatAnchorTicks = 0;
|
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;
|
boolean closeVertically = Math.abs(wantedY - this.getY()) <= 2.5D;
|
||||||
if (atStandoff && closeVertically) {
|
if (atStandoff && closeVertically) {
|
||||||
this.getNavigation().stop();
|
this.getNavigation().stop();
|
||||||
@@ -956,6 +1038,536 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
|
|||||||
return false;
|
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.value(), 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() {
|
protected double combatStandoffDistance() {
|
||||||
return Math.max(this.getBbWidth() * 0.55D,
|
return Math.max(this.getBbWidth() * 0.55D,
|
||||||
BEAM_MUZZLE_FORWARD - this.biteReach() * 0.65D + 1.0D);
|
BEAM_MUZZLE_FORWARD - this.biteReach() * 0.65D + 1.0D);
|
||||||
@@ -968,8 +1580,9 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
|
|||||||
boolean inverted = GravityApi.isGravityInverted(this);
|
boolean inverted = GravityApi.isGravityInverted(this);
|
||||||
double groundY = this.groundYBelow(this.getX(), this.getZ());
|
double groundY = this.groundYBelow(this.getX(), this.getZ());
|
||||||
if (this.landingForCombat) {
|
if (this.landingForCombat) {
|
||||||
if (!target.onGround()) {
|
if (!target.onGround() || ++this.landingForCombatTicks > LANDING_FOR_COMBAT_TIMEOUT_TICKS) {
|
||||||
this.landingForCombat = false;
|
this.landingForCombat = false;
|
||||||
|
this.landingForCombatTicks = 0;
|
||||||
this.locomotionDecisionTicks = 60;
|
this.locomotionDecisionTicks = 60;
|
||||||
} else if (this.onGround() || !inverted && this.getY() <= groundY + 1.0D) {
|
} else if (this.onGround() || !inverted && this.getY() <= groundY + 1.0D) {
|
||||||
this.landingForCombat = false;
|
this.landingForCombat = false;
|
||||||
@@ -986,6 +1599,7 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
|
|||||||
this.locomotionDecisionTicks = 60 + this.random.nextInt(80);
|
this.locomotionDecisionTicks = 60 + this.random.nextInt(80);
|
||||||
if (target.onGround() && this.random.nextDouble() < this.groundCombatBias()) {
|
if (target.onGround() && this.random.nextDouble() < this.groundCombatBias()) {
|
||||||
this.landingForCombat = true;
|
this.landingForCombat = true;
|
||||||
|
this.landingForCombatTicks = 0;
|
||||||
}
|
}
|
||||||
} else if (!target.onGround() || this.random.nextDouble() >= this.groundCombatBias()) {
|
} else if (!target.onGround() || this.random.nextDouble() >= this.groundCombatBias()) {
|
||||||
this.setRoyalFlying(true);
|
this.setRoyalFlying(true);
|
||||||
@@ -1012,8 +1626,61 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected double groundYBelow(double x, double z) {
|
protected double groundYBelow(double x, double z) {
|
||||||
return this.level().getHeight(net.minecraft.world.level.levelgen.Heightmap.Types.MOTION_BLOCKING_NO_LEAVES,
|
return this.groundYBelow(x, z, this.getY() + 1.0D);
|
||||||
Mth.floor(x), Mth.floor(z));
|
}
|
||||||
|
|
||||||
|
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() {
|
private void tickFlyingAltitude() {
|
||||||
@@ -1025,6 +1692,10 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
|
|||||||
}
|
}
|
||||||
double groundY = this.groundYBelow(this.getX(), this.getZ());
|
double groundY = this.groundYBelow(this.getX(), this.getZ());
|
||||||
double ceiling = groundY + FLYING_MAX_HOVER_ABOVE_GROUND;
|
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) {
|
if (this.getY() > ceiling) {
|
||||||
double desiredY = groundY + FLYING_PREFERRED_HOVER;
|
double desiredY = groundY + FLYING_PREFERRED_HOVER;
|
||||||
this.getMoveControl().setWantedPosition(this.getX(), desiredY, this.getZ(), 1.0D);
|
this.getMoveControl().setWantedPosition(this.getX(), desiredY, this.getZ(), 1.0D);
|
||||||
@@ -1035,15 +1706,33 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isFightingRoyalRival() {
|
||||||
|
return this.getTarget() instanceof RoyalBossEntity;
|
||||||
|
}
|
||||||
|
|
||||||
private void tickBodyCrush() {
|
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;
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
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;
|
double yaw = this.yBodyRot * Mth.DEG_TO_RAD;
|
||||||
Vec3 forward = new Vec3(-Math.sin(yaw), 0.0D, Math.cos(yaw));
|
forward = new Vec3(-Math.sin(yaw), 0.0D, Math.cos(yaw));
|
||||||
|
}
|
||||||
|
}
|
||||||
Vec3 center = new Vec3(
|
Vec3 center = new Vec3(
|
||||||
this.getX() + forward.x * (this.getBbWidth() * 0.45D),
|
this.getX() + forward.x * (this.getBbWidth() * 0.45D),
|
||||||
this.getY() + (this.isRoyalFlying() ? 0.0D : 1.0D),
|
this.getY() + (this.isRoyalFlying() ? 0.0D : 1.0D),
|
||||||
@@ -1166,11 +1855,30 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
|
|||||||
|
|
||||||
protected Vec3 beamDirection(RoyalHead head) {
|
protected Vec3 beamDirection(RoyalHead head) {
|
||||||
float yaw = this.getYRot() * Mth.DEG_TO_RAD;
|
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,
|
return GravityRotationUtil.vecPlayerToWorld(localDirection,
|
||||||
GravityApi.getGravityDirection(this)).normalize();
|
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) {
|
protected boolean targetInBeamLine(RoyalHead head, LivingEntity target) {
|
||||||
Vec3 origin = this.beamAnchor(head);
|
Vec3 origin = this.beamAnchor(head);
|
||||||
Vec3 direction = this.beamDirection(head);
|
Vec3 direction = this.beamDirection(head);
|
||||||
|
|||||||
+19
@@ -133,6 +133,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
|
@Override
|
||||||
protected void onHitEntity(EntityHitResult result) {
|
protected void onHitEntity(EntityHitResult result) {
|
||||||
if (!this.level().isClientSide && this.level() instanceof ServerLevel level) {
|
if (!this.level().isClientSide && this.level() instanceof ServerLevel level) {
|
||||||
|
|||||||
+239
@@ -0,0 +1,239 @@
|
|||||||
|
package com.craisinlord.antarchy.content.entity.royal;
|
||||||
|
|
||||||
|
import java.util.EnumSet;
|
||||||
|
import net.minecraft.util.Mth;
|
||||||
|
import net.minecraft.world.entity.LivingEntity;
|
||||||
|
import net.minecraft.world.entity.ai.goal.Goal;
|
||||||
|
import net.minecraft.world.phys.Vec3;
|
||||||
|
|
||||||
|
public class RoyalMountCombatGoal extends Goal {
|
||||||
|
private static final int FLIGHT_DECISION_INTERVAL = 40;
|
||||||
|
|
||||||
|
private final RoyalMountEntity mount;
|
||||||
|
private LivingEntity target;
|
||||||
|
private int repathTicks;
|
||||||
|
private int flightDecisionTicks;
|
||||||
|
private boolean wantsFlight;
|
||||||
|
private int stuckTicks;
|
||||||
|
private Vec3 lastCheckPosition = Vec3.ZERO;
|
||||||
|
private float orbitAngle;
|
||||||
|
private int orbitDirection = 1;
|
||||||
|
private int diveTicks;
|
||||||
|
private int unseenTicks;
|
||||||
|
|
||||||
|
public RoyalMountCombatGoal(RoyalMountEntity mount) {
|
||||||
|
this.mount = mount;
|
||||||
|
this.setFlags(EnumSet.of(Goal.Flag.MOVE, Goal.Flag.LOOK));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canUse() {
|
||||||
|
LivingEntity candidate = this.mount.getTarget();
|
||||||
|
if (!this.isValid(candidate)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
this.target = candidate;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canContinueToUse() {
|
||||||
|
LivingEntity current = this.mount.getTarget();
|
||||||
|
if (current != this.target || !this.isValid(current)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
double range = this.mount.getAttributeValue(net.minecraft.world.entity.ai.attributes.Attributes.FOLLOW_RANGE) * 1.2D;
|
||||||
|
return this.mount.distanceToSqr(current) < range * range && this.unseenTicks < 200;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isValid(LivingEntity candidate) {
|
||||||
|
return candidate != null && candidate.isAlive() && !this.mount.isVehicle() && !this.mount.isOrderedToSit()
|
||||||
|
&& candidate.level() == this.mount.level() && this.mount.canAttack(candidate);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void start() {
|
||||||
|
this.repathTicks = 0;
|
||||||
|
this.flightDecisionTicks = 0;
|
||||||
|
this.wantsFlight = false;
|
||||||
|
this.stuckTicks = 0;
|
||||||
|
this.unseenTicks = 0;
|
||||||
|
this.diveTicks = 0;
|
||||||
|
this.lastCheckPosition = this.mount.position();
|
||||||
|
Vec3 offset = this.mount.position().subtract(this.target.position());
|
||||||
|
this.orbitAngle = (float) Mth.atan2(offset.z, offset.x);
|
||||||
|
this.orbitDirection = this.mount.getRandom().nextBoolean() ? 1 : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void stop() {
|
||||||
|
this.target = null;
|
||||||
|
this.mount.getNavigation().stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean requiresUpdateEveryTick() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void tick() {
|
||||||
|
LivingEntity current = this.target;
|
||||||
|
if (current == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
boolean canSee = this.mount.getSensing().hasLineOfSight(current);
|
||||||
|
this.unseenTicks = canSee ? 0 : this.unseenTicks + 1;
|
||||||
|
double gap = this.mount.combatGap(current);
|
||||||
|
this.mount.getLookControl().setLookAt(current, 30.0F, 30.0F);
|
||||||
|
this.trackStuck();
|
||||||
|
this.updateFlightDecision(current, gap);
|
||||||
|
|
||||||
|
if (this.mount.isActing()) {
|
||||||
|
this.holdDuringAction(current);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int chosen = this.mount.chooseCombatAction(current, gap, canSee);
|
||||||
|
if (chosen != RoyalMountEntity.ACTION_NONE && this.isFacing(current, this.facingTolerance(chosen))) {
|
||||||
|
if (this.mount.startAction(chosen, current, current.getEyePosition(), false)) {
|
||||||
|
this.diveTicks = 0;
|
||||||
|
this.holdDuringAction(current);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (chosen != RoyalMountEntity.ACTION_NONE) {
|
||||||
|
this.mount.turnTowards(current.position(), 18.0F);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.mount.isFlying()) {
|
||||||
|
this.tickAerial(current, gap);
|
||||||
|
} else {
|
||||||
|
this.tickGround(current, gap, canSee);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private float facingTolerance(int action) {
|
||||||
|
return switch (action) {
|
||||||
|
case RoyalMountEntity.ACTION_BITE -> 50.0F;
|
||||||
|
case RoyalMountEntity.ACTION_SIGNATURE -> 180.0F;
|
||||||
|
default -> 120.0F;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isFacing(LivingEntity current, float tolerance) {
|
||||||
|
Vec3 toTarget = current.position().subtract(this.mount.position());
|
||||||
|
float desired = (float) (Mth.atan2(toTarget.z, toTarget.x) * Mth.RAD_TO_DEG) - 90.0F;
|
||||||
|
return Math.abs(Mth.wrapDegrees(desired - this.mount.getYRot())) <= tolerance;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void holdDuringAction(LivingEntity current) {
|
||||||
|
this.mount.getNavigation().stop();
|
||||||
|
if (this.mount.isFlying()) {
|
||||||
|
this.mount.claimFlight();
|
||||||
|
if (this.mount.currentAction() == RoyalMountEntity.ACTION_BITE) {
|
||||||
|
this.mount.getMoveControl().setWantedPosition(current.getX(), current.getY() + current.getBbHeight() * 0.5D, current.getZ(), 0.6D);
|
||||||
|
} else {
|
||||||
|
this.mount.getMoveControl().setWantedPosition(this.mount.getX(), this.mount.getY(), this.mount.getZ(), 0.4D);
|
||||||
|
}
|
||||||
|
} else if (this.mount.currentAction() != RoyalMountEntity.ACTION_BITE) {
|
||||||
|
this.mount.setDeltaMovement(this.mount.getDeltaMovement().multiply(0.5D, 1.0D, 0.5D));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void trackStuck() {
|
||||||
|
if (this.mount.tickCount % 20 != 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Vec3 position = this.mount.position();
|
||||||
|
boolean trying = !this.mount.getNavigation().isDone() || this.mount.isFlying();
|
||||||
|
if (trying && position.distanceToSqr(this.lastCheckPosition) < 1.0D) {
|
||||||
|
this.stuckTicks += 20;
|
||||||
|
} else {
|
||||||
|
this.stuckTicks = 0;
|
||||||
|
}
|
||||||
|
this.lastCheckPosition = position;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateFlightDecision(LivingEntity current, double gap) {
|
||||||
|
if (!this.mount.canFly()) {
|
||||||
|
this.wantsFlight = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (--this.flightDecisionTicks <= 0 || this.stuckTicks >= 40) {
|
||||||
|
this.flightDecisionTicks = FLIGHT_DECISION_INTERVAL;
|
||||||
|
boolean targetAbove = current.getY() - this.mount.getY() > 4.0D + this.mount.getBbHeight() * 0.25D;
|
||||||
|
boolean targetAirborne = !current.onGround() && !current.isInWater() && RoyalMountEntity.isAirborne(current) || targetAbove;
|
||||||
|
boolean groundBlocked = !this.mount.isFlying() && (this.stuckTicks >= 40
|
||||||
|
|| this.mount.getNavigation().isDone() && gap > 4.0D && this.mount.getNavigation().createPath(current, 0) == null);
|
||||||
|
boolean prefersAir = this.mount.prefersAerialCombat() && gap > 3.0D;
|
||||||
|
this.wantsFlight = targetAirborne || groundBlocked || prefersAir || (this.mount.isFlying() && this.diveTicks > 0);
|
||||||
|
if (this.stuckTicks >= 40) {
|
||||||
|
this.stuckTicks = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (this.wantsFlight) {
|
||||||
|
this.mount.claimFlight();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void tickAerial(LivingEntity current, double gap) {
|
||||||
|
if (!this.wantsFlight) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.mount.claimFlight();
|
||||||
|
float scale = this.mount.getGrowthScale();
|
||||||
|
boolean wantsBite = this.mount.isActionAvailable(RoyalMountEntity.ACTION_BITE)
|
||||||
|
&& (!this.mount.prefersAerialCombat() || gap < this.mount.preferredCombatRange() * 0.5D || this.diveTicks > 0);
|
||||||
|
if (wantsBite && this.mount.isActionReady()) {
|
||||||
|
this.diveTicks++;
|
||||||
|
Vec3 approach = current.position().subtract(this.mount.position()).multiply(1.0D, 0.0D, 1.0D);
|
||||||
|
Vec3 offset = approach.lengthSqr() > 1.0E-4D ? approach.normalize().scale(-(this.mount.getBbWidth() * 0.5D + 0.6D * scale)) : Vec3.ZERO;
|
||||||
|
this.mount.getMoveControl().setWantedPosition(current.getX() + offset.x, current.getY() + 0.2D, current.getZ() + offset.z, 1.4D);
|
||||||
|
if (this.diveTicks > 80) {
|
||||||
|
this.diveTicks = 0;
|
||||||
|
this.orbitDirection = -this.orbitDirection;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.diveTicks = 0;
|
||||||
|
double radius = Math.max(6.0D + 1.5D * scale, this.mount.preferredCombatRange()) + this.mount.getBbWidth() * 0.5D;
|
||||||
|
this.orbitAngle += this.orbitDirection * (float) Math.min(0.08D, 1.2D / radius);
|
||||||
|
double x = current.getX() + Math.cos(this.orbitAngle) * radius;
|
||||||
|
double z = current.getZ() + Math.sin(this.orbitAngle) * radius;
|
||||||
|
double y = current.getY() + 3.0D + 0.8D * scale;
|
||||||
|
if (!this.mount.canOccupy(x, y, z)) {
|
||||||
|
this.orbitDirection = -this.orbitDirection;
|
||||||
|
y += 2.0D + scale;
|
||||||
|
}
|
||||||
|
this.mount.getMoveControl().setWantedPosition(x, y, z, 1.1D);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void tickGround(LivingEntity current, double gap, boolean canSee) {
|
||||||
|
double preferred = this.mount.preferredCombatRange();
|
||||||
|
boolean ranged = preferred > 0.0D && this.mount.canSpit();
|
||||||
|
if (ranged && canSee && gap < preferred * 0.5D && !this.mount.isActionAvailable(RoyalMountEntity.ACTION_BITE)) {
|
||||||
|
Vec3 away = this.mount.position().subtract(current.position()).multiply(1.0D, 0.0D, 1.0D);
|
||||||
|
if (away.lengthSqr() > 1.0E-4D && --this.repathTicks <= 0) {
|
||||||
|
this.repathTicks = 10;
|
||||||
|
Vec3 destination = this.mount.position().add(away.normalize().scale(4.0D + this.mount.getBbWidth() * 0.5D));
|
||||||
|
this.mount.getNavigation().moveTo(destination.x, destination.y, destination.z, 1.2D);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (ranged && canSee && gap <= preferred && gap >= preferred * 0.5D && !this.mount.isActionAvailable(RoyalMountEntity.ACTION_BITE)) {
|
||||||
|
this.mount.getNavigation().stop();
|
||||||
|
this.mount.turnTowards(current.position(), 12.0F);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this.mount.canBiteReach(current)) {
|
||||||
|
this.mount.getNavigation().stop();
|
||||||
|
this.mount.turnTowards(current.position(), 18.0F);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (--this.repathTicks <= 0) {
|
||||||
|
this.repathTicks = 6 + this.mount.getRandom().nextInt(6);
|
||||||
|
this.mount.getNavigation().moveTo(current, 1.2D);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+175
@@ -0,0 +1,175 @@
|
|||||||
|
package com.craisinlord.antarchy.content.entity.royal;
|
||||||
|
|
||||||
|
import java.util.EnumSet;
|
||||||
|
import net.minecraft.util.Mth;
|
||||||
|
import net.minecraft.world.entity.LivingEntity;
|
||||||
|
import net.minecraft.world.entity.ai.goal.Goal;
|
||||||
|
import net.minecraft.world.phys.Vec3;
|
||||||
|
|
||||||
|
public class RoyalMountCompanionGoal extends Goal {
|
||||||
|
private static final double MAX_OWNER_DISTANCE = 40.0D;
|
||||||
|
|
||||||
|
private final RoyalMountEntity mount;
|
||||||
|
private LivingEntity owner;
|
||||||
|
private int side = 1;
|
||||||
|
private float orbitAngle;
|
||||||
|
private int repathTicks;
|
||||||
|
private int settledTicks;
|
||||||
|
private int cooldown;
|
||||||
|
private Vec3 spot;
|
||||||
|
|
||||||
|
public RoyalMountCompanionGoal(RoyalMountEntity mount) {
|
||||||
|
this.mount = mount;
|
||||||
|
this.setFlags(EnumSet.of(Goal.Flag.MOVE, Goal.Flag.LOOK));
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isEligible(LivingEntity candidate) {
|
||||||
|
return candidate != null && candidate.isAlive() && !candidate.isSpectator()
|
||||||
|
&& candidate.level() == this.mount.level()
|
||||||
|
&& this.mount.isTame() && !this.mount.isVehicle() && !this.mount.isOrderedToSit()
|
||||||
|
&& !this.mount.isLeashed() && this.mount.getTarget() == null && !this.mount.isActing()
|
||||||
|
&& this.mount.distanceToSqr(candidate) < MAX_OWNER_DISTANCE * MAX_OWNER_DISTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
private double settleDistance() {
|
||||||
|
return 4.0D + this.mount.getBbWidth() * 0.6D;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean needsToReposition(LivingEntity candidate) {
|
||||||
|
if (this.mount.canFly() && RoyalMountEntity.isAirborne(candidate)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (this.mount.isFlying()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
double settle = this.settleDistance() + 2.0D;
|
||||||
|
return this.mount.distanceToSqr(candidate) > settle * settle;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canUse() {
|
||||||
|
if (this.cooldown > 0) {
|
||||||
|
this.cooldown--;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
LivingEntity candidate = this.mount.getOwner();
|
||||||
|
if (!this.isEligible(candidate) || !this.needsToReposition(candidate)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
this.owner = candidate;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canContinueToUse() {
|
||||||
|
return this.isEligible(this.owner) && this.owner == this.mount.getOwner() && this.settledTicks < 40;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void start() {
|
||||||
|
this.side = this.mount.getRandom().nextBoolean() ? 1 : -1;
|
||||||
|
this.repathTicks = 0;
|
||||||
|
this.settledTicks = 0;
|
||||||
|
this.spot = null;
|
||||||
|
Vec3 offset = this.mount.position().subtract(this.owner.position());
|
||||||
|
this.orbitAngle = (float) Mth.atan2(offset.z, offset.x);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void stop() {
|
||||||
|
this.owner = null;
|
||||||
|
this.spot = null;
|
||||||
|
this.cooldown = 20;
|
||||||
|
this.mount.getNavigation().stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean requiresUpdateEveryTick() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void tick() {
|
||||||
|
float scale = this.mount.getGrowthScale();
|
||||||
|
if (this.mount.canFly() && RoyalMountEntity.isAirborne(this.owner)) {
|
||||||
|
this.settledTicks = 0;
|
||||||
|
this.spot = null;
|
||||||
|
this.mount.claimFlight();
|
||||||
|
this.circleOwner(scale);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (--this.repathTicks <= 0 || this.spot == null) {
|
||||||
|
this.repathTicks = 20;
|
||||||
|
this.spot = this.pickSpotBesideOwner();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.mount.isFlying()) {
|
||||||
|
this.settledTicks = 0;
|
||||||
|
this.mount.getLookControl().setLookAt(this.owner, 10.0F, this.mount.getMaxHeadXRot());
|
||||||
|
if (this.mount.onGround() || this.mount.isInWater()) {
|
||||||
|
this.mount.setFlying(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.mount.claimFlight();
|
||||||
|
Vec3 target = this.spot != null ? this.spot : this.owner.position();
|
||||||
|
double horizontal = Math.sqrt(Mth.square(target.x - this.mount.getX()) + Mth.square(target.z - this.mount.getZ()));
|
||||||
|
double y = horizontal > 3.0D + scale ? Math.max(target.y + 1.5D * scale, this.mount.getY() - 2.0D) : target.y - 0.5D;
|
||||||
|
this.mount.getMoveControl().setWantedPosition(target.x, y, target.z, 1.0D);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
double settle = this.settleDistance();
|
||||||
|
boolean nearOwner = this.mount.distanceToSqr(this.owner) <= settle * settle;
|
||||||
|
boolean nearSpot = this.spot != null && this.mount.position().distanceToSqr(this.spot) <= Mth.square(1.5D + 0.5D * scale);
|
||||||
|
if (nearOwner || nearSpot) {
|
||||||
|
this.mount.getNavigation().stop();
|
||||||
|
if (this.settledTicks == 0) {
|
||||||
|
this.mount.turnTowards(this.mount.position().add(this.owner.getLookAngle().multiply(1.0D, 0.0D, 1.0D)), 30.0F);
|
||||||
|
if (this.mount.getRandom().nextInt(4) == 0) {
|
||||||
|
this.mount.triggerQuirk();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.mount.getLookControl().setLookAt(this.owner, 10.0F, this.mount.getMaxHeadXRot());
|
||||||
|
this.settledTicks++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.settledTicks = 0;
|
||||||
|
this.mount.getLookControl().setLookAt(this.owner, 10.0F, this.mount.getMaxHeadXRot());
|
||||||
|
if (this.repathTicks == 20) {
|
||||||
|
Vec3 destination = this.spot != null ? this.spot : this.owner.position();
|
||||||
|
if (!this.mount.getNavigation().moveTo(destination.x, destination.y, destination.z, 1.0D)) {
|
||||||
|
this.mount.getNavigation().moveTo(this.owner, 1.0D);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void circleOwner(float scale) {
|
||||||
|
double radius = 5.0D + this.mount.getBbWidth() * 0.75D;
|
||||||
|
this.orbitAngle += this.side * (float) Math.min(0.07D, 1.0D / radius);
|
||||||
|
double x = this.owner.getX() + Math.cos(this.orbitAngle) * radius;
|
||||||
|
double z = this.owner.getZ() + Math.sin(this.orbitAngle) * radius;
|
||||||
|
double y = this.owner.getY() + 2.0D + 0.6D * scale;
|
||||||
|
if (!this.mount.canOccupy(x, y, z)) {
|
||||||
|
y += 2.0D + scale;
|
||||||
|
}
|
||||||
|
this.mount.getMoveControl().setWantedPosition(x, y, z, 1.2D);
|
||||||
|
this.mount.getLookControl().setLookAt(this.owner, 10.0F, this.mount.getMaxHeadXRot());
|
||||||
|
}
|
||||||
|
|
||||||
|
private Vec3 pickSpotBesideOwner() {
|
||||||
|
Vec3 facing = Vec3.directionFromRotation(0.0F, this.owner.getYRot());
|
||||||
|
Vec3 right = new Vec3(-facing.z, 0.0D, facing.x);
|
||||||
|
double distance = this.mount.getBbWidth() * 0.5D + 1.5D;
|
||||||
|
for (int attempt = 0; attempt < 2; attempt++) {
|
||||||
|
int currentSide = attempt == 0 ? this.side : -this.side;
|
||||||
|
Vec3 base = this.owner.position().add(right.scale(currentSide * distance));
|
||||||
|
double ground = this.mount.findGroundY(base.x, this.owner.getY() + 3.0D, base.z, 10);
|
||||||
|
if (!Double.isNaN(ground) && this.mount.canOccupy(base.x, ground, base.z)) {
|
||||||
|
this.side = currentSide;
|
||||||
|
return new Vec3(base.x, ground, base.z);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
+918
-315
File diff suppressed because it is too large.
Load diff
+81
-57
@@ -1,81 +1,76 @@
|
|||||||
package com.craisinlord.antarchy.content.entity.royal;
|
package com.craisinlord.antarchy.content.entity.royal;
|
||||||
|
|
||||||
import net.minecraft.core.BlockPos;
|
import java.util.EnumSet;
|
||||||
import net.minecraft.world.entity.LivingEntity;
|
import net.minecraft.world.entity.LivingEntity;
|
||||||
import net.minecraft.world.entity.ai.goal.Goal;
|
import net.minecraft.world.entity.ai.goal.Goal;
|
||||||
import net.minecraft.world.phys.Vec3;
|
import net.minecraft.world.phys.Vec3;
|
||||||
|
|
||||||
public class RoyalMountFollowOwnerGoal extends Goal {
|
public class RoyalMountFollowOwnerGoal extends Goal {
|
||||||
private static final double START_DISTANCE = 12.0D;
|
|
||||||
private static final double STOP_DISTANCE = 6.0D;
|
|
||||||
private static final double TELEPORT_DISTANCE = 48.0D;
|
|
||||||
|
|
||||||
private final RoyalMountEntity mount;
|
private final RoyalMountEntity mount;
|
||||||
private LivingEntity owner;
|
private LivingEntity owner;
|
||||||
private int recalcTimer;
|
private int recalcTimer;
|
||||||
|
private int stuckTicks;
|
||||||
|
private int teleportCooldown;
|
||||||
|
private Vec3 lastCheckPosition = Vec3.ZERO;
|
||||||
|
|
||||||
public RoyalMountFollowOwnerGoal(RoyalMountEntity mount) {
|
public RoyalMountFollowOwnerGoal(RoyalMountEntity mount) {
|
||||||
this.mount = mount;
|
this.mount = mount;
|
||||||
|
this.setFlags(EnumSet.of(Goal.Flag.MOVE, Goal.Flag.LOOK));
|
||||||
|
}
|
||||||
|
|
||||||
|
private double startDistance() {
|
||||||
|
return 14.0D + 2.0D * this.mount.getGrowthScale();
|
||||||
|
}
|
||||||
|
|
||||||
|
private double stopDistance() {
|
||||||
|
return 6.0D + 1.5D * this.mount.getGrowthScale();
|
||||||
|
}
|
||||||
|
|
||||||
|
private double teleportDistance() {
|
||||||
|
return 48.0D + 4.0D * this.mount.getGrowthScale();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean canFollow(LivingEntity candidate) {
|
||||||
|
return candidate != null && candidate.isAlive() && !candidate.isSpectator()
|
||||||
|
&& candidate.level() == this.mount.level()
|
||||||
|
&& this.mount.isTame() && !this.mount.isVehicle() && !this.mount.isOrderedToSit()
|
||||||
|
&& !this.mount.isLeashed() && this.mount.getTarget() == null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canUse() {
|
public boolean canUse() {
|
||||||
if (!this.mount.isTame() || this.mount.isVehicle() || this.mount.isOrderedToSit()) {
|
LivingEntity candidate = this.mount.getOwner();
|
||||||
|
if (!this.canFollow(candidate)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
LivingEntity potentialOwner = this.mount.getOwner();
|
double start = this.startDistance();
|
||||||
if (potentialOwner == null || potentialOwner.isSpectator()) {
|
if (this.mount.distanceToSqr(candidate) < start * start) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (this.mount.distanceToSqr(potentialOwner) < START_DISTANCE * START_DISTANCE) {
|
this.owner = candidate;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
this.owner = potentialOwner;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canContinueToUse() {
|
public boolean canContinueToUse() {
|
||||||
return !this.mount.getNavigation().isDone()
|
if (!this.canFollow(this.owner) || this.owner != this.mount.getOwner()) {
|
||||||
&& this.mount.distanceToSqr(this.owner) > STOP_DISTANCE * STOP_DISTANCE
|
return false;
|
||||||
&& !this.mount.isOrderedToSit();
|
}
|
||||||
|
double stop = this.stopDistance();
|
||||||
|
return this.mount.distanceToSqr(this.owner) > stop * stop;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void start() {
|
public void start() {
|
||||||
this.recalcTimer = 0;
|
this.recalcTimer = 0;
|
||||||
|
this.stuckTicks = 0;
|
||||||
|
this.lastCheckPosition = this.mount.position();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void stop() {
|
public void stop() {
|
||||||
this.owner = null;
|
this.owner = null;
|
||||||
this.mount.getNavigation().stop();
|
this.mount.getNavigation().stop();
|
||||||
this.mount.setFlying(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void tick() {
|
|
||||||
this.mount.getLookControl().setLookAt(this.owner, 10.0F, this.mount.getMaxHeadXRot());
|
|
||||||
|
|
||||||
double distanceSqr = this.mount.distanceToSqr(this.owner);
|
|
||||||
if (distanceSqr > TELEPORT_DISTANCE * TELEPORT_DISTANCE) {
|
|
||||||
this.trySafeTeleport();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean ownerAirborne = !this.owner.onGround() || (this.owner.getY() - this.mount.getY()) > 3.0D;
|
|
||||||
this.mount.setFlying(ownerAirborne || distanceSqr > 24.0D * 24.0D);
|
|
||||||
|
|
||||||
if (--this.recalcTimer <= 0) {
|
|
||||||
this.recalcTimer = 10;
|
|
||||||
if (this.mount.isFlying()) {
|
|
||||||
Vec3 to = this.owner.position().subtract(this.mount.position());
|
|
||||||
Vec3 step = this.mount.position().add(to.normalize().scale(Math.min(to.length(), 8.0D)));
|
|
||||||
this.mount.getMoveControl().setWantedPosition(step.x, step.y + 1.0D, step.z, 1.2D);
|
|
||||||
} else {
|
|
||||||
this.mount.getNavigation().moveTo(this.owner, 1.1D);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -83,28 +78,57 @@ public class RoyalMountFollowOwnerGoal extends Goal {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void trySafeTeleport() {
|
@Override
|
||||||
Vec3 ownerPosition = this.owner.position();
|
public void tick() {
|
||||||
BlockPos ownerBlock = this.owner.blockPosition();
|
this.mount.getLookControl().setLookAt(this.owner, 10.0F, this.mount.getMaxHeadXRot());
|
||||||
for (int radius = 0; radius <= 2; radius++) {
|
if (this.teleportCooldown > 0) {
|
||||||
for (int dx = -radius; dx <= radius; dx++) {
|
this.teleportCooldown--;
|
||||||
for (int dz = -radius; dz <= radius; dz++) {
|
|
||||||
for (int dy = -1; dy <= 2; dy++) {
|
|
||||||
double x = ownerPosition.x + dx + 0.5D;
|
|
||||||
double y = ownerBlock.getY() + dy;
|
|
||||||
double z = ownerPosition.z + dz + 0.5D;
|
|
||||||
BlockPos floor = BlockPos.containing(x, y - 0.1D, z);
|
|
||||||
var destination = this.mount.getBoundingBox().move(x - this.mount.getX(), y - this.mount.getY(), z - this.mount.getZ());
|
|
||||||
if (!this.mount.level().noCollision(this.mount, destination)) {
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
if (this.mount.isFlying() || !this.mount.level().getBlockState(floor).getCollisionShape(this.mount.level(), floor).isEmpty()) {
|
this.trackStuck();
|
||||||
this.mount.teleportTo(x, y, z);
|
|
||||||
|
double distance = this.mount.distanceTo(this.owner);
|
||||||
|
if ((distance > this.teleportDistance() || this.stuckTicks >= 100 && distance > 24.0D) && this.teleportCooldown <= 0) {
|
||||||
|
this.teleportCooldown = 20;
|
||||||
|
if (this.mount.tryTeleportNear(this.owner)) {
|
||||||
|
this.stuckTicks = 0;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boolean ownerAirborne = RoyalMountEntity.isAirborne(this.owner);
|
||||||
|
boolean wantsFlight = this.mount.canFly() && (ownerAirborne || distance > 24.0D + this.mount.getBbWidth()
|
||||||
|
|| this.stuckTicks >= 40 || this.owner.getY() - this.mount.getY() > 4.0D);
|
||||||
|
if (wantsFlight && this.mount.claimFlight()) {
|
||||||
|
if (--this.recalcTimer <= 0) {
|
||||||
|
this.recalcTimer = 5;
|
||||||
|
float scale = this.mount.getGrowthScale();
|
||||||
|
Vec3 destination = this.owner.position().add(0.0D, 2.0D + 0.6D * scale, 0.0D);
|
||||||
|
Vec3 to = destination.subtract(this.mount.position());
|
||||||
|
double stepLength = Math.min(to.length(), 12.0D + 2.0D * scale);
|
||||||
|
Vec3 step = to.lengthSqr() > 1.0E-4D ? this.mount.position().add(to.normalize().scale(stepLength)) : destination;
|
||||||
|
this.mount.getMoveControl().setWantedPosition(step.x, step.y, step.z, 1.3D);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this.mount.isFlying()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (--this.recalcTimer <= 0) {
|
||||||
|
this.recalcTimer = 10;
|
||||||
|
this.mount.getNavigation().moveTo(this.owner, 1.2D);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void trackStuck() {
|
||||||
|
if (this.mount.tickCount % 20 != 0) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
Vec3 position = this.mount.position();
|
||||||
|
if (position.distanceToSqr(this.lastCheckPosition) < 1.0D) {
|
||||||
|
this.stuckTicks += 20;
|
||||||
|
} else {
|
||||||
|
this.stuckTicks = 0;
|
||||||
|
}
|
||||||
|
this.lastCheckPosition = position;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
-49
@@ -1,49 +0,0 @@
|
|||||||
package com.craisinlord.antarchy.content.entity.royal;
|
|
||||||
|
|
||||||
import net.minecraft.world.entity.LivingEntity;
|
|
||||||
import net.minecraft.world.entity.ai.goal.Goal;
|
|
||||||
|
|
||||||
public class RoyalMountSpitGoal extends Goal {
|
|
||||||
private final RoyalMountEntity mount;
|
|
||||||
private int cooldown;
|
|
||||||
|
|
||||||
public RoyalMountSpitGoal(RoyalMountEntity mount) {
|
|
||||||
this.mount = mount;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean canUse() {
|
|
||||||
if (this.mount.isVehicle() || this.mount.isOrderedToSit()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
LivingEntity target = this.mount.getTarget();
|
|
||||||
if (target == null || !target.isAlive() || !this.mount.canAttack(target)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
double distance = this.mount.distanceTo(target);
|
|
||||||
return distance > 5.0D && distance < 20.0D && this.mount.hasLineOfSight(target);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void start() {
|
|
||||||
this.cooldown = 20;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void tick() {
|
|
||||||
LivingEntity target = this.mount.getTarget();
|
|
||||||
if (target == null || !this.mount.canAttack(target)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.mount.getLookControl().setLookAt(target, 30.0F, 30.0F);
|
|
||||||
if (this.cooldown-- <= 0) {
|
|
||||||
this.mount.startSpitVolley(target);
|
|
||||||
this.cooldown = 60;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean requiresUpdateEveryTick() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+2
-1
@@ -100,7 +100,8 @@ public final class RoyalAttackScheduler {
|
|||||||
int total = 0;
|
int total = 0;
|
||||||
int[] weights = new int[ids.length];
|
int[] weights = new int[ids.length];
|
||||||
for (int i = 0; i < ids.length; i++) {
|
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];
|
total += weights[i];
|
||||||
}
|
}
|
||||||
if (total <= 0) {
|
if (total <= 0) {
|
||||||
|
|||||||
+2
@@ -12,6 +12,8 @@ public record RoyalMountActionPayload(int action) implements CustomPacketPayload
|
|||||||
public static final int BITE = 1;
|
public static final int BITE = 1;
|
||||||
public static final int SPIT = 2;
|
public static final int SPIT = 2;
|
||||||
public static final int MOUNT = 3;
|
public static final int MOUNT = 3;
|
||||||
|
public static final int BEAM = 4;
|
||||||
|
public static final int SIGNATURE = 5;
|
||||||
|
|
||||||
public static final Type<RoyalMountActionPayload> TYPE =
|
public static final Type<RoyalMountActionPayload> TYPE =
|
||||||
new Type<>(ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "royal_mount_action"));
|
new Type<>(ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "royal_mount_action"));
|
||||||
|
|||||||
+30
-2
@@ -1,6 +1,7 @@
|
|||||||
package com.craisinlord.antarchy.content.portal;
|
package com.craisinlord.antarchy.content.portal;
|
||||||
|
|
||||||
import com.craisinlord.antarchy.config.AntarchySettings;
|
import com.craisinlord.antarchy.config.AntarchySettings;
|
||||||
|
import com.craisinlord.antarchy.content.entity.ant.AntTeleportHelper;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -13,6 +14,7 @@ import net.minecraft.server.level.ServerLevel;
|
|||||||
import net.minecraft.server.level.ServerPlayer;
|
import net.minecraft.server.level.ServerPlayer;
|
||||||
import net.minecraft.util.Mth;
|
import net.minecraft.util.Mth;
|
||||||
import net.minecraft.world.entity.Entity;
|
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.PoiManager;
|
||||||
import net.minecraft.world.entity.ai.village.poi.PoiRecord;
|
import net.minecraft.world.entity.ai.village.poi.PoiRecord;
|
||||||
import net.minecraft.world.entity.vehicle.DismountHelper;
|
import net.minecraft.world.entity.vehicle.DismountHelper;
|
||||||
@@ -47,7 +49,20 @@ public final class PermanentPortalTeleporter {
|
|||||||
}
|
}
|
||||||
return new DimensionTransition(sourceLevel, entity, DimensionTransition.DO_NOTHING);
|
return new DimensionTransition(sourceLevel, entity, DimensionTransition.DO_NOTHING);
|
||||||
}
|
}
|
||||||
return new DimensionTransition(destination, arrival, Vec3.ZERO, entity.getYRot(), entity.getXRot(), DimensionTransition.PLAY_PORTAL_SOUND);
|
DimensionTransition.PostDimensionTransition postTransition = DimensionTransition.PLAY_PORTAL_SOUND;
|
||||||
|
if (entity instanceof ServerPlayer player) {
|
||||||
|
List<Mob> companions = AntTeleportHelper.collectCompanions(player);
|
||||||
|
if (!companions.isEmpty()) {
|
||||||
|
postTransition = postTransition.then(moved -> {
|
||||||
|
for (Mob companion : companions) {
|
||||||
|
if (companion.isAlive()) {
|
||||||
|
AntTeleportHelper.moveCompanion(companion, destination, arrival);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new DimensionTransition(destination, arrival, Vec3.ZERO, entity.getYRot(), entity.getXRot(), postTransition);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@@ -68,6 +83,7 @@ public final class PermanentPortalTeleporter {
|
|||||||
return safePortalPosition != null ? safePortalPosition : existing.center();
|
return safePortalPosition != null ? safePortalPosition : existing.center();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AntTeleportHelper.loadArrivalSearchArea(destination, preferredPos, SEARCH_RADIUS);
|
||||||
Vec3 safe = findSafeArrivalPosition(entity, destination, preferredPos, type);
|
Vec3 safe = findSafeArrivalPosition(entity, destination, preferredPos, type);
|
||||||
if (safe != null) {
|
if (safe != null) {
|
||||||
PermanentPortalShape created = createReturnPortal(destination, safe, type);
|
PermanentPortalShape created = createReturnPortal(destination, safe, type);
|
||||||
@@ -77,7 +93,19 @@ public final class PermanentPortalTeleporter {
|
|||||||
return createFallbackPortal(destination, BlockPos.containing(safe), type);
|
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
|
@Nullable
|
||||||
|
|||||||
+17
@@ -1,12 +1,14 @@
|
|||||||
package com.craisinlord.antarchy.mixins.client;
|
package com.craisinlord.antarchy.mixins.client;
|
||||||
|
|
||||||
import com.craisinlord.antarchy.content.entity.HerculesBeetleEntity;
|
import com.craisinlord.antarchy.content.entity.HerculesBeetleEntity;
|
||||||
|
import com.craisinlord.antarchy.content.entity.royal.RoyalMountEntity;
|
||||||
import net.minecraft.client.Camera;
|
import net.minecraft.client.Camera;
|
||||||
import net.minecraft.world.entity.Entity;
|
import net.minecraft.world.entity.Entity;
|
||||||
import net.minecraft.world.level.BlockGetter;
|
import net.minecraft.world.level.BlockGetter;
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
import org.spongepowered.asm.mixin.Shadow;
|
import org.spongepowered.asm.mixin.Shadow;
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.ModifyArg;
|
||||||
import org.spongepowered.asm.mixin.injection.ModifyArgs;
|
import org.spongepowered.asm.mixin.injection.ModifyArgs;
|
||||||
import org.spongepowered.asm.mixin.injection.invoke.arg.Args;
|
import org.spongepowered.asm.mixin.injection.invoke.arg.Args;
|
||||||
|
|
||||||
@@ -15,6 +17,18 @@ public abstract class MountedVehicleCameraMixin {
|
|||||||
@Shadow
|
@Shadow
|
||||||
private Entity entity;
|
private Entity entity;
|
||||||
|
|
||||||
|
@ModifyArg(
|
||||||
|
method = "setup",
|
||||||
|
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Camera;getMaxZoom(F)F"),
|
||||||
|
index = 0
|
||||||
|
)
|
||||||
|
private float antarchy$extendRoyalMountZoom(float distance) {
|
||||||
|
if (this.entity != null && this.entity.getVehicle() instanceof RoyalMountEntity mount) {
|
||||||
|
return distance + mount.thirdPersonCameraBonus();
|
||||||
|
}
|
||||||
|
return distance;
|
||||||
|
}
|
||||||
|
|
||||||
@ModifyArgs(
|
@ModifyArgs(
|
||||||
method = "setup",
|
method = "setup",
|
||||||
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Camera;move(FFF)V")
|
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Camera;move(FFF)V")
|
||||||
@@ -38,6 +52,9 @@ public abstract class MountedVehicleCameraMixin {
|
|||||||
args.set(2, (Float) args.get(2) + 0.45F);
|
args.set(2, (Float) args.get(2) + 0.45F);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (vehicle instanceof RoyalMountEntity mount) {
|
||||||
|
args.set(1, (Float) args.get(1) + mount.thirdPersonCameraLift());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+4
-5
@@ -1,18 +1,17 @@
|
|||||||
package com.craisinlord.antarchy.mixins.client;
|
package com.craisinlord.antarchy.mixins.client;
|
||||||
|
|
||||||
import com.craisinlord.antarchy.content.client.PortalGunZoomClientState;
|
import com.craisinlord.antarchy.content.client.PortalGunZoomClientState;
|
||||||
|
import com.llamalad7.mixinextras.injector.ModifyReturnValue;
|
||||||
import net.minecraft.client.Camera;
|
import net.minecraft.client.Camera;
|
||||||
import net.minecraft.client.Minecraft;
|
import net.minecraft.client.Minecraft;
|
||||||
import net.minecraft.client.renderer.GameRenderer;
|
import net.minecraft.client.renderer.GameRenderer;
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
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)
|
@Mixin(GameRenderer.class)
|
||||||
public abstract class PortalGunZoomFovMixin {
|
public abstract class PortalGunZoomFovMixin {
|
||||||
@Inject(method = "getFov", at = @At("RETURN"), cancellable = true)
|
@ModifyReturnValue(method = "getFov", at = @At("RETURN"))
|
||||||
private void antarchy$applyPortalGunZoom(Camera camera, float partialTick, boolean useFovSetting, CallbackInfoReturnable<Double> cir) {
|
private double antarchy$applyPortalGunZoom(double fov, Camera camera, float partialTick, boolean useFovSetting) {
|
||||||
cir.setReturnValue(PortalGunZoomClientState.applyFov(cir.getReturnValue(), partialTick, Minecraft.getInstance().options));
|
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": [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": [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, -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]}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -604,6 +604,16 @@
|
|||||||
"message.antarchy.royal_boundary.activated": "Royal Boundary raised",
|
"message.antarchy.royal_boundary.activated": "Royal Boundary raised",
|
||||||
"message.antarchy.royal_boundary.recharging": "Royal Boundary is recharging",
|
"message.antarchy.royal_boundary.recharging": "Royal Boundary is recharging",
|
||||||
"key.antarchy.mount_flight_toggle": "Mount: Toggle Flight",
|
"key.antarchy.mount_flight_toggle": "Mount: Toggle Flight",
|
||||||
|
"key.antarchy.mount_beam": "Royal Mount: Beam",
|
||||||
|
"key.antarchy.mount_signature": "Royal Mount: Command / Time Field",
|
||||||
|
"entity.antarchy.royal_mount.stage.hatchling": "Hatchling",
|
||||||
|
"entity.antarchy.royal_mount.stage.juvenile": "Juvenile",
|
||||||
|
"entity.antarchy.royal_mount.stage.adult": "Adult",
|
||||||
|
"message.antarchy.royal_mount.stage_up": "%s has reached the %s stage!",
|
||||||
|
"message.antarchy.royal_mount.growth": "%s: %s, %s%% grown",
|
||||||
|
"message.antarchy.royal_mount.too_young_ride": "%s must reach the %s stage before it can be ridden",
|
||||||
|
"message.antarchy.royal_mount.too_young_fly": "%s must reach the %s stage before it can fly",
|
||||||
|
"message.antarchy.royal_mount.too_young_ability": "%s must reach the %s stage to use that",
|
||||||
"item.antarchy.springbug_leg": "Springbug Leg",
|
"item.antarchy.springbug_leg": "Springbug Leg",
|
||||||
"item.antarchy.springy_boots": "Springy Boots",
|
"item.antarchy.springy_boots": "Springy Boots",
|
||||||
"tooltip.antarchy.springy_boots": "Sneak to charge, then jump to spring upward.",
|
"tooltip.antarchy.springy_boots": "Sneak to charge, then jump to spring upward.",
|
||||||
@@ -1642,12 +1652,12 @@
|
|||||||
,"guide.antarchy.entry.queen.description6": "In her final phase she also casts her own time dilation field, slowing anyone caught inside it."
|
,"guide.antarchy.entry.queen.description6": "In her final phase she also casts her own time dilation field, slowing anyone caught inside it."
|
||||||
,"guide.antarchy.entry.prince.title": "Prince"
|
,"guide.antarchy.entry.prince.title": "Prince"
|
||||||
,"guide.antarchy.entry.prince.subtitle": "Royal mount record"
|
,"guide.antarchy.entry.prince.subtitle": "Royal mount record"
|
||||||
,"guide.antarchy.entry.prince.description": "Prince Eggs hatch into small Princes that grow when fed golden items."
|
,"guide.antarchy.entry.prince.description": "Prince Eggs hatch into Hatchlings bonded to whoever placed the egg. Golden apples grow them a little and enchanted golden apples grow them a lot: Juveniles can be ridden and spit bolts, and Adults can fly, fire beams, and use their signature ability."
|
||||||
,"guide.antarchy.entry.prince.description2": "A grown Prince can be tamed and saddled for flight, fires elemental bolts at your enemies, and can command nearby hostile mobs. Feed responsibly; you are raising a small flying warlord."
|
,"guide.antarchy.entry.prince.description2": "The Prince is a ground brawler: he closes in to bite with all three heads, spits fire, ice, and lightning, and as an Adult commands nearby hostile mobs to fight for him. Feed responsibly; you are raising a small flying warlord."
|
||||||
,"guide.antarchy.entry.princess.title": "Princess"
|
,"guide.antarchy.entry.princess.title": "Princess"
|
||||||
,"guide.antarchy.entry.princess.subtitle": "Royal mount record"
|
,"guide.antarchy.entry.princess.subtitle": "Royal mount record"
|
||||||
,"guide.antarchy.entry.princess.description": "Princess Eggs hatch into small Princesses that grow when fed golden items."
|
,"guide.antarchy.entry.princess.description": "Princess Eggs hatch into Hatchlings bonded to whoever placed the egg. Golden apples grow them a little and enchanted golden apples grow them a lot: Juveniles can be ridden and spit bolts, and Adults can fly, fire beams, and use their signature ability."
|
||||||
,"guide.antarchy.entry.princess.description2": "A grown Princess can be tamed and saddled for flight, fires elemental bolts, and creates time-dilation fields around enemies."
|
,"guide.antarchy.entry.princess.description2": "The Princess fights at range: she keeps her distance, fires slowing bolts from her orbs, and as an Adult hovers above her foes and traps them in time-dilation fields."
|
||||||
,"subtitles.antarchy.hoverboard_mount": "Hoverboard starts"
|
,"subtitles.antarchy.hoverboard_mount": "Hoverboard starts"
|
||||||
,"subtitles.antarchy.hoverboard_dismount": "Hoverboard stops"
|
,"subtitles.antarchy.hoverboard_dismount": "Hoverboard stops"
|
||||||
,"subtitles.antarchy.hoverboard_idle": "Hoverboard hums"
|
,"subtitles.antarchy.hoverboard_idle": "Hoverboard hums"
|
||||||
|
|||||||
File diff suppressed because it is too large.
Load diff
@@ -27,7 +27,7 @@
|
|||||||
{
|
{
|
||||||
"type": "minecraft:item",
|
"type": "minecraft:item",
|
||||||
"name": "antarchy:cornea_ear",
|
"name": "antarchy:cornea_ear",
|
||||||
"weight": 8,
|
"weight": 5,
|
||||||
"functions": [
|
"functions": [
|
||||||
{
|
{
|
||||||
"function": "minecraft:set_count",
|
"function": "minecraft:set_count",
|
||||||
@@ -42,7 +42,7 @@
|
|||||||
{
|
{
|
||||||
"type": "minecraft:item",
|
"type": "minecraft:item",
|
||||||
"name": "antarchy:bluestone",
|
"name": "antarchy:bluestone",
|
||||||
"weight": 10,
|
"weight": 6,
|
||||||
"functions": [
|
"functions": [
|
||||||
{
|
{
|
||||||
"function": "minecraft:set_count",
|
"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",
|
"type": "minecraft:item",
|
||||||
"name": "minecraft:diamond",
|
"name": "minecraft:diamond",
|
||||||
|
|||||||
@@ -4,11 +4,11 @@
|
|||||||
"pattern": [
|
"pattern": [
|
||||||
" B",
|
" B",
|
||||||
" B ",
|
" B ",
|
||||||
"N "
|
"I "
|
||||||
],
|
],
|
||||||
"key": {
|
"key": {
|
||||||
"N": {
|
"I": {
|
||||||
"item": "minecraft:netherite_ingot"
|
"item": "minecraft:iron_ingot"
|
||||||
},
|
},
|
||||||
"B": {
|
"B": {
|
||||||
"item": "antarchy:blood_crystal_shard"
|
"item": "antarchy:blood_crystal_shard"
|
||||||
|
|||||||
BIN
Binary file not shown.
+1
-1
@@ -25,7 +25,7 @@ public final class AntarchyFabricClient implements ClientModInitializer {
|
|||||||
com.craisinlord.antarchy.Antarchy.physicalClient = true;
|
com.craisinlord.antarchy.Antarchy.physicalClient = true;
|
||||||
com.craisinlord.antarchy.content.AntarchyAnthologyHooks.registerClient();
|
com.craisinlord.antarchy.content.AntarchyAnthologyHooks.registerClient();
|
||||||
ModelLoadingPlugin.register(plugin -> plugin.addModels(SeparateLargeItemModels.all()));
|
ModelLoadingPlugin.register(plugin -> plugin.addModels(SeparateLargeItemModels.all()));
|
||||||
ClientTickEvents.END_CLIENT_TICK.register(client -> QueenMusicHandler.tick(client, AntarchyFabricSounds.THE_QUEEN.get()));
|
ClientTickEvents.END_CLIENT_TICK.register(client -> QueenMusicHandler.tick(client, AntarchyFabricSounds.THE_QUEEN));
|
||||||
AntarchyFabricClientNetworking.register();
|
AntarchyFabricClientNetworking.register();
|
||||||
AntarchyFabricClientBootstrap.register();
|
AntarchyFabricClientBootstrap.register();
|
||||||
ResourceManagerHelper.get(PackType.CLIENT_RESOURCES).registerReloadListener(new IdentifiableResourceReloadListener() {
|
ResourceManagerHelper.get(PackType.CLIENT_RESOURCES).registerReloadListener(new IdentifiableResourceReloadListener() {
|
||||||
|
|||||||
+1
-1
@@ -26,7 +26,7 @@ public final class AntarchyFabricClient implements ClientModInitializer {
|
|||||||
com.craisinlord.antarchy.Antarchy.physicalClient = true;
|
com.craisinlord.antarchy.Antarchy.physicalClient = true;
|
||||||
com.craisinlord.antarchy.content.AntarchyAnthologyHooks.registerClient();
|
com.craisinlord.antarchy.content.AntarchyAnthologyHooks.registerClient();
|
||||||
ModelLoadingPlugin.register(plugin -> plugin.addModels(SeparateLargeItemModels.all()));
|
ModelLoadingPlugin.register(plugin -> plugin.addModels(SeparateLargeItemModels.all()));
|
||||||
ClientTickEvents.END_CLIENT_TICK.register(client -> QueenMusicHandler.tick(client, AntarchyFabricSounds.THE_QUEEN.get()));
|
ClientTickEvents.END_CLIENT_TICK.register(client -> QueenMusicHandler.tick(client, AntarchyFabricSounds.THE_QUEEN));
|
||||||
AntarchyFabricClientNetworking.register();
|
AntarchyFabricClientNetworking.register();
|
||||||
AntarchyFabricClientBootstrap.register();
|
AntarchyFabricClientBootstrap.register();
|
||||||
AntarchyComputerGames.register();
|
AntarchyComputerGames.register();
|
||||||
|
|||||||
+23
@@ -30,6 +30,19 @@ public final class AntarchyKeyBindings {
|
|||||||
GLFW.GLFW_KEY_R,
|
GLFW.GLFW_KEY_R,
|
||||||
CATEGORY
|
CATEGORY
|
||||||
);
|
);
|
||||||
|
public static final KeyMapping MOUNT_BEAM = new KeyMapping(
|
||||||
|
"key.antarchy.mount_beam",
|
||||||
|
InputConstants.Type.KEYSYM,
|
||||||
|
GLFW.GLFW_KEY_H,
|
||||||
|
CATEGORY
|
||||||
|
);
|
||||||
|
|
||||||
|
public static final KeyMapping MOUNT_SIGNATURE = new KeyMapping(
|
||||||
|
"key.antarchy.mount_signature",
|
||||||
|
InputConstants.Type.KEYSYM,
|
||||||
|
GLFW.GLFW_KEY_J,
|
||||||
|
CATEGORY
|
||||||
|
);
|
||||||
public static final KeyMapping TIGERS_EYE_CAMOUFLAGE = new KeyMapping(
|
public static final KeyMapping TIGERS_EYE_CAMOUFLAGE = new KeyMapping(
|
||||||
"key.antarchy.tigers_eye_camouflage",
|
"key.antarchy.tigers_eye_camouflage",
|
||||||
InputConstants.Type.KEYSYM,
|
InputConstants.Type.KEYSYM,
|
||||||
@@ -68,6 +81,8 @@ public final class AntarchyKeyBindings {
|
|||||||
KeyBindingHelper.registerKeyBinding(BRUTALFLY_FLAP);
|
KeyBindingHelper.registerKeyBinding(BRUTALFLY_FLAP);
|
||||||
KeyBindingHelper.registerKeyBinding(MOUNT_SPECIAL);
|
KeyBindingHelper.registerKeyBinding(MOUNT_SPECIAL);
|
||||||
KeyBindingHelper.registerKeyBinding(MOUNT_FLIGHT_TOGGLE);
|
KeyBindingHelper.registerKeyBinding(MOUNT_FLIGHT_TOGGLE);
|
||||||
|
KeyBindingHelper.registerKeyBinding(MOUNT_BEAM);
|
||||||
|
KeyBindingHelper.registerKeyBinding(MOUNT_SIGNATURE);
|
||||||
KeyBindingHelper.registerKeyBinding(TIGERS_EYE_CAMOUFLAGE);
|
KeyBindingHelper.registerKeyBinding(TIGERS_EYE_CAMOUFLAGE);
|
||||||
KeyBindingHelper.registerKeyBinding(ROYAL_INVERSION_TOGGLE);
|
KeyBindingHelper.registerKeyBinding(ROYAL_INVERSION_TOGGLE);
|
||||||
KeyBindingHelper.registerKeyBinding(ROYAL_BOUNDARY);
|
KeyBindingHelper.registerKeyBinding(ROYAL_BOUNDARY);
|
||||||
@@ -87,6 +102,14 @@ public final class AntarchyKeyBindings {
|
|||||||
return Minecraft.getInstance().screen == null && MOUNT_FLIGHT_TOGGLE.isDown();
|
return Minecraft.getInstance().screen == null && MOUNT_FLIGHT_TOGGLE.isDown();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isMountBeamPressed() {
|
||||||
|
return Minecraft.getInstance().screen == null && MOUNT_BEAM.isDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isMountSignaturePressed() {
|
||||||
|
return Minecraft.getInstance().screen == null && MOUNT_SIGNATURE.isDown();
|
||||||
|
}
|
||||||
|
|
||||||
public static boolean consumeTigerEyeCamouflagePressed() {
|
public static boolean consumeTigerEyeCamouflagePressed() {
|
||||||
return Minecraft.getInstance().screen == null && TIGERS_EYE_CAMOUFLAGE.consumeClick();
|
return Minecraft.getInstance().screen == null && TIGERS_EYE_CAMOUFLAGE.consumeClick();
|
||||||
}
|
}
|
||||||
|
|||||||
+16
@@ -12,6 +12,8 @@ public final class RoyalMountClientHandler {
|
|||||||
private static boolean wasAttack;
|
private static boolean wasAttack;
|
||||||
private static boolean wasSpecial;
|
private static boolean wasSpecial;
|
||||||
private static boolean wasFlightToggle;
|
private static boolean wasFlightToggle;
|
||||||
|
private static boolean wasBeam;
|
||||||
|
private static boolean wasSignature;
|
||||||
private static boolean wasAscend;
|
private static boolean wasAscend;
|
||||||
private static boolean wasDescend;
|
private static boolean wasDescend;
|
||||||
private static boolean wasUse;
|
private static boolean wasUse;
|
||||||
@@ -57,6 +59,18 @@ public final class RoyalMountClientHandler {
|
|||||||
}
|
}
|
||||||
wasFlightToggle = flightToggle;
|
wasFlightToggle = flightToggle;
|
||||||
|
|
||||||
|
boolean beam = AntarchyKeyBindings.isMountBeamPressed();
|
||||||
|
if (beam && !wasBeam) {
|
||||||
|
ClientPlayNetworking.send(new RoyalMountActionPayload(RoyalMountActionPayload.BEAM));
|
||||||
|
}
|
||||||
|
wasBeam = beam;
|
||||||
|
|
||||||
|
boolean signature = AntarchyKeyBindings.isMountSignaturePressed();
|
||||||
|
if (signature && !wasSignature) {
|
||||||
|
ClientPlayNetworking.send(new RoyalMountActionPayload(RoyalMountActionPayload.SIGNATURE));
|
||||||
|
}
|
||||||
|
wasSignature = signature;
|
||||||
|
|
||||||
boolean ascend = mc.options.keyJump.isDown();
|
boolean ascend = mc.options.keyJump.isDown();
|
||||||
boolean descend = mc.options.keyShift.isDown();
|
boolean descend = mc.options.keyShift.isDown();
|
||||||
if (ascend != wasAscend || descend != wasDescend) {
|
if (ascend != wasAscend || descend != wasDescend) {
|
||||||
@@ -73,6 +87,8 @@ public final class RoyalMountClientHandler {
|
|||||||
wasAttack = false;
|
wasAttack = false;
|
||||||
wasSpecial = false;
|
wasSpecial = false;
|
||||||
wasFlightToggle = false;
|
wasFlightToggle = false;
|
||||||
|
wasBeam = false;
|
||||||
|
wasSignature = false;
|
||||||
wasAscend = false;
|
wasAscend = false;
|
||||||
wasDescend = false;
|
wasDescend = false;
|
||||||
}
|
}
|
||||||
|
|||||||
+6
@@ -29,5 +29,11 @@ public class AmendmentRareOresMixin {
|
|||||||
new Amendment.MatchingBlockTagSelector("antarchy:infinity_dimension_rare_ores"),
|
new Amendment.MatchingBlockTagSelector("antarchy:infinity_dimension_rare_ores"),
|
||||||
new Amendment.SetValue(0.02)
|
new Amendment.SetValue(0.02)
|
||||||
));
|
));
|
||||||
|
data.computeIfAbsent(ConfigType.EFFECTS, k -> new ArrayList<>()).add(new Amendment(
|
||||||
|
ConfigType.EFFECTS,
|
||||||
|
new Amendment.MatchingModSelector("antarchy"),
|
||||||
|
new Amendment.MatchingSelector("antarchy:paralyzed"),
|
||||||
|
new Amendment.SetValue(0.0)
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+2
@@ -207,6 +207,8 @@ public final class AntarchyFabricNetworking {
|
|||||||
case RoyalMountActionPayload.FLIGHT_TOGGLE -> mount.toggleMountedFlight(player);
|
case RoyalMountActionPayload.FLIGHT_TOGGLE -> mount.toggleMountedFlight(player);
|
||||||
case RoyalMountActionPayload.BITE -> mount.handleMountedBite(player);
|
case RoyalMountActionPayload.BITE -> mount.handleMountedBite(player);
|
||||||
case RoyalMountActionPayload.SPIT -> mount.handleMountedSpit(player);
|
case RoyalMountActionPayload.SPIT -> mount.handleMountedSpit(player);
|
||||||
|
case RoyalMountActionPayload.BEAM -> mount.handleMountedBeam(player);
|
||||||
|
case RoyalMountActionPayload.SIGNATURE -> mount.handleMountedSignature(player);
|
||||||
default -> {
|
default -> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ mod_name=Antarchy
|
|||||||
mod_description=ANTS! ANTS! ANTS! ANTS!
|
mod_description=ANTS! ANTS! ANTS! ANTS!
|
||||||
mod_authors=CraisinLord
|
mod_authors=CraisinLord
|
||||||
mod_group_id=com.craisinlord
|
mod_group_id=com.craisinlord
|
||||||
mod_version=2.2.4
|
mod_version=2.2.6
|
||||||
mod_license=All Rights Reserved
|
mod_license=All Rights Reserved
|
||||||
|
|
||||||
minecraft_version=1.20.1
|
minecraft_version=1.20.1
|
||||||
|
|||||||
+1
-1
@@ -18,6 +18,6 @@ public final class QueenMusicClientHandler {
|
|||||||
public static void onClientTick(TickEvent.ClientTickEvent event) {
|
public static void onClientTick(TickEvent.ClientTickEvent event) {
|
||||||
if (event.phase != TickEvent.Phase.END) return;
|
if (event.phase != TickEvent.Phase.END) return;
|
||||||
Minecraft minecraft = Minecraft.getInstance();
|
Minecraft minecraft = Minecraft.getInstance();
|
||||||
QueenMusicHandler.tick(minecraft, AntarchyForgeSounds.THE_QUEEN.get());
|
QueenMusicHandler.tick(minecraft, AntarchyForgeSounds.THE_QUEEN);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+49
@@ -0,0 +1,49 @@
|
|||||||
|
package com.craisinlord.antarchy.forge.mixins.compat;
|
||||||
|
|
||||||
|
import com.mojang.authlib.GameProfile;
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.server.level.ServerLevel;
|
||||||
|
import net.minecraft.world.phys.Vec3;
|
||||||
|
import net.minecraftforge.common.util.FakePlayer;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.Pseudo;
|
||||||
|
import org.spongepowered.asm.mixin.Unique;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||||
|
|
||||||
|
/** Protects virtual position calls made before the drone constructor has finished. */
|
||||||
|
@Pseudo
|
||||||
|
@Mixin(targets = "me.desht.pneumaticcraft.common.util.fakeplayer.DroneFakePlayer", remap = false)
|
||||||
|
public abstract class DroneFakePlayerConstructionMixin extends FakePlayer {
|
||||||
|
@Unique
|
||||||
|
private boolean antarchy$droneConstructionComplete;
|
||||||
|
|
||||||
|
protected DroneFakePlayerConstructionMixin(ServerLevel level, GameProfile profile) {
|
||||||
|
super(level, profile);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Inject(method = "<init>", at = @At("RETURN"), remap = false)
|
||||||
|
private void antarchy$finishDroneConstruction(CallbackInfo ci) {
|
||||||
|
antarchy$droneConstructionComplete = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Explicit Mojmap/SRG selectors: the optional target is unavailable to the
|
||||||
|
// annotation processor, but its Minecraft overrides are reobfuscated by Forge.
|
||||||
|
@Inject(method = {"position()Lnet/minecraft/world/phys/Vec3;", "m_20182_()Lnet/minecraft/world/phys/Vec3;"},
|
||||||
|
at = @At("HEAD"), cancellable = true, remap = false, require = 0)
|
||||||
|
private void antarchy$positionDuringConstruction(CallbackInfoReturnable<Vec3> cir) {
|
||||||
|
if (!antarchy$droneConstructionComplete) {
|
||||||
|
cir.setReturnValue(super.position());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Inject(method = {"blockPosition()Lnet/minecraft/core/BlockPos;", "m_20183_()Lnet/minecraft/core/BlockPos;"},
|
||||||
|
at = @At("HEAD"), cancellable = true, remap = false, require = 0)
|
||||||
|
private void antarchy$blockPositionDuringConstruction(CallbackInfoReturnable<BlockPos> cir) {
|
||||||
|
if (!antarchy$droneConstructionComplete) {
|
||||||
|
cir.setReturnValue(super.blockPosition());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+6
@@ -24,5 +24,11 @@ public class AmendmentRareOresMixin {
|
|||||||
new Amendment.MatchingBlockTagSelector("antarchy:infinity_dimension_rare_ores"),
|
new Amendment.MatchingBlockTagSelector("antarchy:infinity_dimension_rare_ores"),
|
||||||
new Amendment.SetValue(0.02)
|
new Amendment.SetValue(0.02)
|
||||||
));
|
));
|
||||||
|
data.computeIfAbsent(ConfigType.EFFECTS, k -> new ArrayList<>()).add(new Amendment(
|
||||||
|
ConfigType.EFFECTS,
|
||||||
|
new Amendment.MatchingModSelector("antarchy"),
|
||||||
|
new Amendment.MatchingSelector("antarchy:paralyzed"),
|
||||||
|
new Amendment.SetValue(0.0)
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4,6 +4,7 @@
|
|||||||
"refmap": "antarchy-forge.mixins.refmap.json",
|
"refmap": "antarchy-forge.mixins.refmap.json",
|
||||||
"compatibilityLevel": "JAVA_17",
|
"compatibilityLevel": "JAVA_17",
|
||||||
"mixins": [
|
"mixins": [
|
||||||
|
"compat.DroneFakePlayerConstructionMixin",
|
||||||
"compat.SablePhysicsColliderBlockGetterMixin",
|
"compat.SablePhysicsColliderBlockGetterMixin",
|
||||||
"item.BucketItemThoraxisWaterMixin",
|
"item.BucketItemThoraxisWaterMixin",
|
||||||
"entity.ParalyzedMobFreezeMixin",
|
"entity.ParalyzedMobFreezeMixin",
|
||||||
|
|||||||
+1
-1
@@ -6,7 +6,7 @@ org.gradle.configureondemand=true
|
|||||||
minecraft_version_1_21_1=1.21.1
|
minecraft_version_1_21_1=1.21.1
|
||||||
modid=antarchy
|
modid=antarchy
|
||||||
mod_id=antarchy
|
mod_id=antarchy
|
||||||
mod_version=2.2.5
|
mod_version=2.2.6
|
||||||
maven_group=com.craisinlord
|
maven_group=com.craisinlord
|
||||||
mixin_version=0.15.4+mixin.0.8.7
|
mixin_version=0.15.4+mixin.0.8.7
|
||||||
|
|
||||||
|
|||||||
+25
@@ -36,6 +36,21 @@ public final class AntarchyKeyBindings {
|
|||||||
InputConstants.KEY_R,
|
InputConstants.KEY_R,
|
||||||
CATEGORY
|
CATEGORY
|
||||||
);
|
);
|
||||||
|
public static final KeyMapping MOUNT_BEAM = new KeyMapping(
|
||||||
|
"key.antarchy.mount_beam",
|
||||||
|
KeyConflictContext.IN_GAME,
|
||||||
|
InputConstants.Type.KEYSYM,
|
||||||
|
InputConstants.KEY_H,
|
||||||
|
CATEGORY
|
||||||
|
);
|
||||||
|
|
||||||
|
public static final KeyMapping MOUNT_SIGNATURE = new KeyMapping(
|
||||||
|
"key.antarchy.mount_signature",
|
||||||
|
KeyConflictContext.IN_GAME,
|
||||||
|
InputConstants.Type.KEYSYM,
|
||||||
|
InputConstants.KEY_J,
|
||||||
|
CATEGORY
|
||||||
|
);
|
||||||
public static final KeyMapping TIGERS_EYE_CAMOUFLAGE = new KeyMapping(
|
public static final KeyMapping TIGERS_EYE_CAMOUFLAGE = new KeyMapping(
|
||||||
"key.antarchy.tigers_eye_camouflage",
|
"key.antarchy.tigers_eye_camouflage",
|
||||||
KeyConflictContext.IN_GAME,
|
KeyConflictContext.IN_GAME,
|
||||||
@@ -80,6 +95,8 @@ public final class AntarchyKeyBindings {
|
|||||||
event.register(BRUTALFLY_FLAP);
|
event.register(BRUTALFLY_FLAP);
|
||||||
event.register(MOUNT_SPECIAL);
|
event.register(MOUNT_SPECIAL);
|
||||||
event.register(MOUNT_FLIGHT_TOGGLE);
|
event.register(MOUNT_FLIGHT_TOGGLE);
|
||||||
|
event.register(MOUNT_BEAM);
|
||||||
|
event.register(MOUNT_SIGNATURE);
|
||||||
event.register(TIGERS_EYE_CAMOUFLAGE);
|
event.register(TIGERS_EYE_CAMOUFLAGE);
|
||||||
event.register(ROYAL_INVERSION_TOGGLE);
|
event.register(ROYAL_INVERSION_TOGGLE);
|
||||||
event.register(ROYAL_BOUNDARY);
|
event.register(ROYAL_BOUNDARY);
|
||||||
@@ -91,6 +108,14 @@ public final class AntarchyKeyBindings {
|
|||||||
return MOUNT_FLIGHT_TOGGLE.isDown();
|
return MOUNT_FLIGHT_TOGGLE.isDown();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isMountBeamPressed() {
|
||||||
|
return MOUNT_BEAM.isDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isMountSignaturePressed() {
|
||||||
|
return MOUNT_SIGNATURE.isDown();
|
||||||
|
}
|
||||||
|
|
||||||
public static boolean isMountSpecialPressed() {
|
public static boolean isMountSpecialPressed() {
|
||||||
return MOUNT_SPECIAL.isDown();
|
return MOUNT_SPECIAL.isDown();
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -17,6 +17,6 @@ public final class QueenMusicClientHandler {
|
|||||||
@SubscribeEvent
|
@SubscribeEvent
|
||||||
public static void onClientTick(ClientTickEvent.Post event) {
|
public static void onClientTick(ClientTickEvent.Post event) {
|
||||||
Minecraft minecraft = Minecraft.getInstance();
|
Minecraft minecraft = Minecraft.getInstance();
|
||||||
QueenMusicHandler.tick(minecraft, AntarchyNeoforgeSounds.THE_QUEEN.get());
|
QueenMusicHandler.tick(minecraft, AntarchyNeoforgeSounds.THE_QUEEN);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+16
@@ -18,6 +18,8 @@ public final class RoyalMountClientHandler {
|
|||||||
private static boolean wasAttack;
|
private static boolean wasAttack;
|
||||||
private static boolean wasSpecial;
|
private static boolean wasSpecial;
|
||||||
private static boolean wasFlightToggle;
|
private static boolean wasFlightToggle;
|
||||||
|
private static boolean wasBeam;
|
||||||
|
private static boolean wasSignature;
|
||||||
private static boolean wasAscend;
|
private static boolean wasAscend;
|
||||||
private static boolean wasDescend;
|
private static boolean wasDescend;
|
||||||
private static boolean wasUse;
|
private static boolean wasUse;
|
||||||
@@ -64,6 +66,18 @@ public final class RoyalMountClientHandler {
|
|||||||
}
|
}
|
||||||
wasFlightToggle = flightToggle;
|
wasFlightToggle = flightToggle;
|
||||||
|
|
||||||
|
boolean beam = AntarchyKeyBindings.isMountBeamPressed();
|
||||||
|
if (beam && !wasBeam) {
|
||||||
|
PacketDistributor.sendToServer(new RoyalMountActionPayload(RoyalMountActionPayload.BEAM));
|
||||||
|
}
|
||||||
|
wasBeam = beam;
|
||||||
|
|
||||||
|
boolean signature = AntarchyKeyBindings.isMountSignaturePressed();
|
||||||
|
if (signature && !wasSignature) {
|
||||||
|
PacketDistributor.sendToServer(new RoyalMountActionPayload(RoyalMountActionPayload.SIGNATURE));
|
||||||
|
}
|
||||||
|
wasSignature = signature;
|
||||||
|
|
||||||
boolean ascend = mc.options.keyJump.isDown();
|
boolean ascend = mc.options.keyJump.isDown();
|
||||||
boolean descend = mc.options.keyShift.isDown();
|
boolean descend = mc.options.keyShift.isDown();
|
||||||
if (ascend != wasAscend || descend != wasDescend) {
|
if (ascend != wasAscend || descend != wasDescend) {
|
||||||
@@ -80,6 +94,8 @@ public final class RoyalMountClientHandler {
|
|||||||
wasAttack = false;
|
wasAttack = false;
|
||||||
wasSpecial = false;
|
wasSpecial = false;
|
||||||
wasFlightToggle = false;
|
wasFlightToggle = false;
|
||||||
|
wasBeam = false;
|
||||||
|
wasSignature = false;
|
||||||
wasAscend = false;
|
wasAscend = false;
|
||||||
wasDescend = false;
|
wasDescend = false;
|
||||||
}
|
}
|
||||||
|
|||||||
+6
@@ -29,5 +29,11 @@ public class AmendmentRareOresMixin {
|
|||||||
new Amendment.MatchingBlockTagSelector("antarchy:infinity_dimension_rare_ores"),
|
new Amendment.MatchingBlockTagSelector("antarchy:infinity_dimension_rare_ores"),
|
||||||
new Amendment.SetValue(0.02)
|
new Amendment.SetValue(0.02)
|
||||||
));
|
));
|
||||||
|
data.computeIfAbsent(ConfigType.EFFECTS, k -> new ArrayList<>()).add(new Amendment(
|
||||||
|
ConfigType.EFFECTS,
|
||||||
|
new Amendment.MatchingModSelector("antarchy"),
|
||||||
|
new Amendment.MatchingSelector("antarchy:paralyzed"),
|
||||||
|
new Amendment.SetValue(0.0)
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+2
@@ -107,6 +107,8 @@ public class AntarchyNeoforgePayloadHandlers {
|
|||||||
case com.craisinlord.antarchy.content.network.RoyalMountActionPayload.FLIGHT_TOGGLE -> mount.toggleMountedFlight(player);
|
case com.craisinlord.antarchy.content.network.RoyalMountActionPayload.FLIGHT_TOGGLE -> mount.toggleMountedFlight(player);
|
||||||
case com.craisinlord.antarchy.content.network.RoyalMountActionPayload.BITE -> mount.handleMountedBite(player);
|
case com.craisinlord.antarchy.content.network.RoyalMountActionPayload.BITE -> mount.handleMountedBite(player);
|
||||||
case com.craisinlord.antarchy.content.network.RoyalMountActionPayload.SPIT -> mount.handleMountedSpit(player);
|
case com.craisinlord.antarchy.content.network.RoyalMountActionPayload.SPIT -> mount.handleMountedSpit(player);
|
||||||
|
case com.craisinlord.antarchy.content.network.RoyalMountActionPayload.BEAM -> mount.handleMountedBeam(player);
|
||||||
|
case com.craisinlord.antarchy.content.network.RoyalMountActionPayload.SIGNATURE -> mount.handleMountedSignature(player);
|
||||||
default -> {
|
default -> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user