undertrial sounds
inverted squidzooka, rpo king and queen tweaks fix attidue adjustor bug
This commit is contained in:
1 parent
aefbfb9f32
commit
82cea4c293
8 files changed
+676
-87
No files matched your search
+262
-70
@@ -67,7 +67,9 @@ import com.craisinlord.antarchy.content.entity.royal.attack.RoyalAttackLane;
|
||||
import java.util.EnumMap;
|
||||
import java.util.List;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
public class KingEntity extends RoyalBossEntity {
|
||||
@@ -102,6 +104,9 @@ public class KingEntity extends RoyalBossEntity {
|
||||
private static final int GROUND_ASSAULT_TICKS = 200;
|
||||
private static final int GROUND_ASSAULT_COOLDOWN_MIN = 600;
|
||||
private static final int GROUND_ASSAULT_COOLDOWN_VARIANCE = 300;
|
||||
private static final int LANDING_TELEGRAPH_TICKS = 24;
|
||||
private static final int LANDING_IMPACT_TICKS = 18;
|
||||
private static final int TAKEOFF_TICKS = 30;
|
||||
private static final int COMBAT_WAYPOINT_MIN_TICKS = 45;
|
||||
private static final int COMBAT_WAYPOINT_VARIANCE = 35;
|
||||
|
||||
@@ -130,12 +135,13 @@ public class KingEntity extends RoyalBossEntity {
|
||||
private boolean decreeRetreatPressure;
|
||||
@Nullable
|
||||
private String lastDecreeKey;
|
||||
private boolean closeQuartersEntered;
|
||||
private int closeQuartersOutsideTicks;
|
||||
private final Set<UUID> closeQuartersEntered = new HashSet<>();
|
||||
private final Map<UUID, Integer> closeQuartersOutsideTicks = new HashMap<>();
|
||||
private final Set<UUID> decreeRecipients = new HashSet<>();
|
||||
private int projectilePressure;
|
||||
private int groundAssaultCooldownTicks = 400;
|
||||
private int groundAssaultTicks;
|
||||
private boolean kingLandingForCombat;
|
||||
private KingLandingStage kingLandingStage = KingLandingStage.AERIAL;
|
||||
private int kingLandingStageTicks;
|
||||
@Nullable
|
||||
private Vec3 kingLandingWaypoint;
|
||||
@Nullable
|
||||
@@ -174,6 +180,15 @@ public class KingEntity extends RoyalBossEntity {
|
||||
new StandTallDecree(), new StandYourGroundDecree(),
|
||||
new HandsOffTheCrownDecree(), new ShowNoMercyDecree());
|
||||
|
||||
private enum KingLandingStage {
|
||||
AERIAL,
|
||||
TELEGRAPH,
|
||||
DESCENT,
|
||||
IMPACT,
|
||||
GROUND_COMBAT,
|
||||
TAKEOFF
|
||||
}
|
||||
|
||||
public KingEntity(EntityType<? extends KingEntity> entityType, Level level) {
|
||||
super(entityType, level);
|
||||
}
|
||||
@@ -188,6 +203,60 @@ public class KingEntity extends RoyalBossEntity {
|
||||
return phase.maxConcurrentHeadAttacks();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean blocksRoyalAttacksForMovement() {
|
||||
return this.kingLandingStage == KingLandingStage.TELEGRAPH
|
||||
|| this.kingLandingStage == KingLandingStage.DESCENT
|
||||
|| this.kingLandingStage == KingLandingStage.IMPACT
|
||||
|| this.kingLandingStage == KingLandingStage.TAKEOFF;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onRoyalPhaseTransitionStarted(Phase previousPhase, Phase nextPhase) {
|
||||
if (this.activeDecree != null) {
|
||||
this.endDecree(null);
|
||||
}
|
||||
this.resetKingCombatMovement();
|
||||
this.setRoyalFlying(true);
|
||||
this.setDeltaMovement(this.getDeltaMovement().add(0.0D, 0.25D, 0.0D));
|
||||
this.getNavigation().stop();
|
||||
this.setDeltaMovement(this.getDeltaMovement().scale(0.35D));
|
||||
this.playRoyalSound(AntarchySoundEvents.KING_ROAR.get(), nextPhase == Phase.THREE ? 0.72F : 0.88F);
|
||||
if (this.level() instanceof ServerLevel level) {
|
||||
Component title = Component.translatable(nextPhase == Phase.TWO
|
||||
? "boss.antarchy.king.phase_two" : "boss.antarchy.king.phase_three")
|
||||
.withStyle(style -> style.withColor(nextPhase == Phase.TWO
|
||||
? ChatFormatting.GOLD : ChatFormatting.DARK_PURPLE).withBold(true));
|
||||
for (ServerPlayer player : this.royalEncounterPlayers(level)) {
|
||||
player.connection.send(new ClientboundSetTitlesAnimationPacket(5, 45, 10));
|
||||
player.connection.send(new ClientboundSetTitleTextPacket(title));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void tickRoyalPhaseTransition(Phase nextPhase, int elapsedTicks, int remainingTicks) {
|
||||
this.getNavigation().stop();
|
||||
this.setDeltaMovement(this.getDeltaMovement().scale(0.7D));
|
||||
if (this.level() instanceof ServerLevel level && elapsedTicks % 3 == 0) {
|
||||
level.sendParticles(nextPhase == Phase.THREE ? ParticleTypes.WITCH : ParticleTypes.ENCHANT,
|
||||
this.getX(), this.getY() + this.getBbHeight() * 0.55D, this.getZ(),
|
||||
18, this.getBbWidth() * 0.25D, this.getBbHeight() * 0.25D,
|
||||
this.getBbWidth() * 0.25D, 0.08D);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onRoyalPhaseTransitionCompleted(Phase nextPhase) {
|
||||
this.decreeCooldownTicks = 0;
|
||||
this.groundAssaultCooldownTicks = nextPhase == Phase.THREE ? 0 : Math.min(this.groundAssaultCooldownTicks, 100);
|
||||
if (nextPhase == Phase.THREE) {
|
||||
this.fireballCooldownTicks = 0;
|
||||
this.iceballCooldownTicks = 0;
|
||||
this.iceSpikeCooldownTicks = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean managesOwnCombatLocomotion() {
|
||||
return true;
|
||||
@@ -432,12 +501,17 @@ public class KingEntity extends RoyalBossEntity {
|
||||
if (this.tickCount % 20 == 0 && this.getArrowCount() > 6) {
|
||||
this.setArrowCount(6);
|
||||
}
|
||||
if (this.isRoyalPhaseTransitionActive()) {
|
||||
return;
|
||||
}
|
||||
LivingEntity target = this.getTarget();
|
||||
if (target != null && !this.isDeadOrDying() && this.level() instanceof ServerLevel level) {
|
||||
this.trackBehavior(target);
|
||||
this.tickDecree(level, target);
|
||||
this.tickKingWholeBody(level, target);
|
||||
this.tickKingCombatMovement(target);
|
||||
if (!this.blocksRoyalAttacksForMovement()) {
|
||||
this.tickKingWholeBody(level, target);
|
||||
}
|
||||
this.stabilizeKingRotation();
|
||||
return;
|
||||
}
|
||||
@@ -627,53 +701,82 @@ public class KingEntity extends RoyalBossEntity {
|
||||
|
||||
private void tickKingCombatMovement(LivingEntity target) {
|
||||
boolean closeQuarters = this.activeDecree instanceof CloseQuartersDecree;
|
||||
if (closeQuarters && this.isRoyalFlying() && !this.kingLandingForCombat) {
|
||||
if (closeQuarters && this.isRoyalFlying() && this.kingLandingStage == KingLandingStage.AERIAL) {
|
||||
this.beginKingLanding(target);
|
||||
} else if (!closeQuarters && this.isRoyalFlying() && !this.kingLandingForCombat
|
||||
} else if (!closeQuarters && this.isRoyalFlying() && this.kingLandingStage == KingLandingStage.AERIAL
|
||||
&& this.groundAssaultCooldownTicks-- <= 0 && target.onGround()) {
|
||||
this.beginKingLanding(target);
|
||||
}
|
||||
|
||||
if (this.kingLandingForCombat) {
|
||||
if (!target.onGround() && !closeQuarters) {
|
||||
this.kingLandingForCombat = false;
|
||||
this.kingLandingWaypoint = null;
|
||||
this.groundAssaultCooldownTicks = 160;
|
||||
switch (this.kingLandingStage) {
|
||||
case TELEGRAPH -> {
|
||||
this.getNavigation().stop();
|
||||
this.setDeltaMovement(this.getDeltaMovement().scale(0.72D));
|
||||
this.tickKingLandingIndicator();
|
||||
if (--this.kingLandingStageTicks <= 0) {
|
||||
this.kingLandingStage = KingLandingStage.DESCENT;
|
||||
this.playRoyalSound(AntarchySoundEvents.KING_WING_FLAP.get(), 0.72F);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (this.kingLandingWaypoint == null) {
|
||||
this.kingLandingWaypoint = this.findKingLandingPoint(target);
|
||||
}
|
||||
Vec3 landing = this.kingLandingWaypoint;
|
||||
if (landing != null) {
|
||||
case DESCENT -> {
|
||||
Vec3 landing = this.kingLandingWaypoint;
|
||||
if (landing == null) {
|
||||
this.resetKingCombatMovement();
|
||||
return;
|
||||
}
|
||||
this.setRoyalFlying(true);
|
||||
this.getMoveControl().setWantedPosition(landing.x, landing.y, landing.z, 1.35D);
|
||||
if (this.onGround() || this.getY() <= landing.y + 1.25D) {
|
||||
this.tickKingLandingIndicator();
|
||||
boolean reachedLanding = this.onGround()
|
||||
|| this.position().distanceToSqr(landing) <= 2.25D
|
||||
|| this.getY() <= landing.y + 1.25D;
|
||||
if (reachedLanding) {
|
||||
this.setRoyalFlying(false);
|
||||
this.kingLandingForCombat = false;
|
||||
this.kingLandingWaypoint = null;
|
||||
this.groundAssaultTicks = closeQuarters
|
||||
this.setPos(this.getX(), landing.y, this.getZ());
|
||||
this.kingLandingStage = KingLandingStage.IMPACT;
|
||||
this.kingLandingStageTicks = LANDING_IMPACT_TICKS;
|
||||
this.triggerAnim("body_action", "stomp");
|
||||
this.playRoyalSound(AntarchySoundEvents.KING_ROAR.get(), 0.68F);
|
||||
if (this.level() instanceof ServerLevel level) {
|
||||
this.performStomp(level, target);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
case IMPACT -> {
|
||||
this.getNavigation().stop();
|
||||
this.setDeltaMovement(Vec3.ZERO);
|
||||
if (--this.kingLandingStageTicks <= 0) {
|
||||
this.kingLandingStage = KingLandingStage.GROUND_COMBAT;
|
||||
this.kingLandingStageTicks = closeQuarters
|
||||
? Math.max(GROUND_ASSAULT_TICKS, this.activeDecreeTicks + 20)
|
||||
: GROUND_ASSAULT_TICKS;
|
||||
this.kingLandingWaypoint = null;
|
||||
}
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.isRoyalFlying()) {
|
||||
if (closeQuarters) {
|
||||
this.groundAssaultTicks = Math.max(this.groundAssaultTicks, this.activeDecreeTicks + 20);
|
||||
case GROUND_COMBAT -> {
|
||||
if (closeQuarters) {
|
||||
this.kingLandingStageTicks = Math.max(this.kingLandingStageTicks, this.activeDecreeTicks + 20);
|
||||
}
|
||||
this.getMoveControl().setWantedPosition(target.getX(), target.getY(), target.getZ(), 1.25D);
|
||||
if (--this.kingLandingStageTicks <= 0 && !closeQuarters) {
|
||||
this.beginKingTakeoff(target);
|
||||
}
|
||||
return;
|
||||
}
|
||||
this.getMoveControl().setWantedPosition(target.getX(), target.getY(), target.getZ(), 1.25D);
|
||||
if (--this.groundAssaultTicks <= 0 && !closeQuarters) {
|
||||
this.setRoyalFlying(true);
|
||||
this.setDeltaMovement(this.getDeltaMovement().add(0.0D, 0.42D, 0.0D));
|
||||
this.hasImpulse = true;
|
||||
this.groundAssaultCooldownTicks = GROUND_ASSAULT_COOLDOWN_MIN
|
||||
+ this.random.nextInt(GROUND_ASSAULT_COOLDOWN_VARIANCE + 1);
|
||||
this.kingCombatWaypoint = null;
|
||||
case TAKEOFF -> {
|
||||
if (--this.kingLandingStageTicks <= 0) {
|
||||
this.kingLandingStage = KingLandingStage.AERIAL;
|
||||
this.groundAssaultCooldownTicks = GROUND_ASSAULT_COOLDOWN_MIN
|
||||
+ this.random.nextInt(GROUND_ASSAULT_COOLDOWN_VARIANCE + 1);
|
||||
this.kingCombatWaypoint = null;
|
||||
}
|
||||
return;
|
||||
}
|
||||
case AERIAL -> {
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.kingCombatWaypoint != null && ++this.kingCombatProgressTicks >= 40) {
|
||||
@@ -700,13 +803,46 @@ public class KingEntity extends RoyalBossEntity {
|
||||
private void beginKingLanding(LivingEntity target) {
|
||||
this.kingLandingWaypoint = this.findKingLandingPoint(target);
|
||||
if (this.kingLandingWaypoint != null) {
|
||||
this.kingLandingForCombat = true;
|
||||
this.kingLandingStage = KingLandingStage.TELEGRAPH;
|
||||
this.kingLandingStageTicks = LANDING_TELEGRAPH_TICKS;
|
||||
this.kingCombatWaypoint = null;
|
||||
this.playRoyalSound(AntarchySoundEvents.KING_ROAR.get(), 1.12F);
|
||||
} else {
|
||||
this.groundAssaultCooldownTicks = 100;
|
||||
}
|
||||
}
|
||||
|
||||
private void beginKingTakeoff(LivingEntity target) {
|
||||
this.kingLandingStage = KingLandingStage.TAKEOFF;
|
||||
this.kingLandingStageTicks = TAKEOFF_TICKS;
|
||||
this.triggerAnim("wing_action", "wing_gust");
|
||||
this.playRoyalSound(AntarchySoundEvents.KING_WING_FLAP.get(), 0.92F);
|
||||
if (this.level() instanceof ServerLevel level) {
|
||||
this.performWingGust(level, target);
|
||||
}
|
||||
this.setRoyalFlying(true);
|
||||
this.setDeltaMovement(this.getDeltaMovement().add(0.0D, 0.42D, 0.0D));
|
||||
this.hasImpulse = true;
|
||||
}
|
||||
|
||||
private void tickKingLandingIndicator() {
|
||||
if (!(this.level() instanceof ServerLevel level) || this.kingLandingWaypoint == null
|
||||
|| this.tickCount % 2 != 0) {
|
||||
return;
|
||||
}
|
||||
double progress = this.kingLandingStage == KingLandingStage.TELEGRAPH
|
||||
? 1.0D - this.kingLandingStageTicks / (double) LANDING_TELEGRAPH_TICKS : 1.0D;
|
||||
double radius = Mth.lerp(progress, 4.0D, STOMP_RADIUS);
|
||||
for (int index = 0; index < 36; index++) {
|
||||
double angle = Mth.TWO_PI * index / 36.0D;
|
||||
level.sendParticles(ParticleTypes.ENCHANT,
|
||||
this.kingLandingWaypoint.x + Math.cos(angle) * radius,
|
||||
this.kingLandingWaypoint.y + 0.2D,
|
||||
this.kingLandingWaypoint.z + Math.sin(angle) * radius,
|
||||
1, 0.0D, 0.02D, 0.0D, 0.0D);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Vec3 findKingLandingPoint(LivingEntity target) {
|
||||
Vec3 away = this.position().subtract(target.position()).multiply(1.0D, 0.0D, 1.0D);
|
||||
@@ -765,36 +901,39 @@ public class KingEntity extends RoyalBossEntity {
|
||||
}
|
||||
|
||||
private void resetKingCombatMovement() {
|
||||
this.kingLandingForCombat = false;
|
||||
this.kingLandingStage = KingLandingStage.AERIAL;
|
||||
this.kingLandingStageTicks = 0;
|
||||
this.kingLandingWaypoint = null;
|
||||
this.kingCombatWaypoint = null;
|
||||
this.kingCombatProgressPosition = null;
|
||||
this.kingCombatWaypointTicks = 0;
|
||||
this.kingCombatProgressTicks = 0;
|
||||
this.groundAssaultTicks = 0;
|
||||
if (this.groundAssaultCooldownTicks <= 0) {
|
||||
this.groundAssaultCooldownTicks = 400;
|
||||
}
|
||||
}
|
||||
|
||||
public void requestCloseQuartersAssault(LivingEntity target) {
|
||||
if (target != null && target.isAlive() && this.isRoyalFlying() && !this.kingLandingForCombat) {
|
||||
if (target != null && target.isAlive() && this.isRoyalFlying()
|
||||
&& this.kingLandingStage == KingLandingStage.AERIAL) {
|
||||
this.beginKingLanding(target);
|
||||
}
|
||||
}
|
||||
|
||||
public RoyalDecree.Evaluation evaluateCloseQuarters(LivingEntity target) {
|
||||
UUID targetId = target.getUUID();
|
||||
double distanceSquared = target.distanceToSqr(this);
|
||||
if (distanceSquared <= CLOSE_QUARTERS_RADIUS * CLOSE_QUARTERS_RADIUS) {
|
||||
this.closeQuartersEntered = true;
|
||||
this.closeQuartersOutsideTicks = 0;
|
||||
this.closeQuartersEntered.add(targetId);
|
||||
this.closeQuartersOutsideTicks.remove(targetId);
|
||||
return RoyalDecree.Evaluation.COMPLIANT;
|
||||
}
|
||||
if (!this.closeQuartersEntered) {
|
||||
if (!this.closeQuartersEntered.contains(targetId)) {
|
||||
return 300 - this.activeDecreeTicks >= CLOSE_QUARTERS_APPROACH_TICKS
|
||||
? RoyalDecree.Evaluation.VIOLATED : RoyalDecree.Evaluation.COMPLIANT;
|
||||
}
|
||||
return ++this.closeQuartersOutsideTicks > CLOSE_QUARTERS_EXIT_GRACE_TICKS
|
||||
int outsideTicks = this.closeQuartersOutsideTicks.merge(targetId, 1, Integer::sum);
|
||||
return outsideTicks > CLOSE_QUARTERS_EXIT_GRACE_TICKS
|
||||
? RoyalDecree.Evaluation.VIOLATED : RoyalDecree.Evaluation.COMPLIANT;
|
||||
}
|
||||
|
||||
@@ -1009,10 +1148,11 @@ public class KingEntity extends RoyalBossEntity {
|
||||
this.activeDecree = decree;
|
||||
this.activeDecreeTicks = 300;
|
||||
this.decreeCooldownTicks = 0;
|
||||
this.closeQuartersEntered = false;
|
||||
this.closeQuartersOutsideTicks = 0;
|
||||
this.closeQuartersEntered.clear();
|
||||
this.closeQuartersOutsideTicks.clear();
|
||||
this.decreeRecipients.clear();
|
||||
this.projectRoyalSound(AntarchySoundEvents.KING_DECREE.get(), 4.0F, 1.0F, target);
|
||||
this.sendDecreeTitle(target);
|
||||
this.broadcastActiveDecree((ServerLevel) this.level());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1342,7 +1482,8 @@ public class KingEntity extends RoyalBossEntity {
|
||||
if (this.isRoyalRecoveryActive() && this.activeDecree == null) {
|
||||
return;
|
||||
}
|
||||
if (!(target instanceof ServerPlayer player) || !target.isAlive() || target.level() != level) {
|
||||
List<ServerPlayer> participants = this.royalEncounterPlayers(level);
|
||||
if (participants.isEmpty()) {
|
||||
if (this.activeDecree != null) {
|
||||
this.endDecree(target);
|
||||
}
|
||||
@@ -1355,28 +1496,49 @@ public class KingEntity extends RoyalBossEntity {
|
||||
if (this.decreeCooldownTicks-- > 0) {
|
||||
return;
|
||||
}
|
||||
int decreeCooldown = this.decreeCooldownForPhase();
|
||||
if (!this.beginRoyalAttack("decree", RoyalAttackLane.DECREE,
|
||||
AntarchySettings.royalDecreeCooldownTicks(), 300)) {
|
||||
decreeCooldown, 300)) {
|
||||
return;
|
||||
}
|
||||
this.activeDecreeTicks = 300;
|
||||
this.activeDecree = this.pickDecree(target);
|
||||
this.decreeRetreatPressure = false;
|
||||
this.closeQuartersEntered = false;
|
||||
this.closeQuartersOutsideTicks = 0;
|
||||
this.closeQuartersEntered.clear();
|
||||
this.closeQuartersOutsideTicks.clear();
|
||||
this.decreeRecipients.clear();
|
||||
this.playRoyalSound(AntarchySoundEvents.KING_DECREE_CAST.get(), 0.9F + this.random.nextFloat() * 0.15F);
|
||||
this.playRoyalSound(AntarchySoundEvents.KING_ROAR.get(), 0.8F + this.random.nextFloat() * 0.12F);
|
||||
this.projectRoyalSound(AntarchySoundEvents.KING_DECREE.get(), 4.0F, 1.0F, target);
|
||||
this.sendDecreeTitle(player);
|
||||
this.broadcastActiveDecree(level);
|
||||
}
|
||||
int countdown = this.activeDecree.countdownTicks(target);
|
||||
if (countdown > 0 && countdown <= 100 && this.tickCount % 20 == 0) {
|
||||
level.playSound(null, target.blockPosition(), SoundEvents.BELL_BLOCK, SoundSource.HOSTILE, 0.8F, 1.0F + countdown / 500.0F);
|
||||
this.broadcastActiveDecree(level);
|
||||
boolean hasCompletableParticipants = false;
|
||||
boolean everyParticipantComplete = true;
|
||||
for (ServerPlayer participant : participants) {
|
||||
int countdown = this.activeDecree.countdownTicks(participant);
|
||||
if (countdown > 0 && countdown <= 100 && this.tickCount % 20 == 0) {
|
||||
level.playSound(null, participant.blockPosition(), SoundEvents.BELL_BLOCK,
|
||||
SoundSource.HOSTILE, 0.8F, 1.0F + countdown / 500.0F);
|
||||
}
|
||||
this.activeDecree.apply(level, this, participant);
|
||||
if (this.activeDecree == null) {
|
||||
return;
|
||||
}
|
||||
RoyalDecree.Evaluation evaluation = this.activeDecree.evaluate(level, this, participant);
|
||||
if (evaluation == RoyalDecree.Evaluation.VIOLATED) {
|
||||
this.failActiveDecree(participant);
|
||||
return;
|
||||
}
|
||||
if (evaluation == RoyalDecree.Evaluation.COMPLETE) {
|
||||
hasCompletableParticipants = true;
|
||||
} else {
|
||||
everyParticipantComplete = false;
|
||||
}
|
||||
}
|
||||
this.activeDecree.apply(level, this, target);
|
||||
if (this.activeDecree != null
|
||||
&& this.activeDecree.evaluate(level, this, target) == RoyalDecree.Evaluation.VIOLATED) {
|
||||
this.failActiveDecree(target);
|
||||
if (this.activeDecree != null && hasCompletableParticipants && everyParticipantComplete) {
|
||||
this.projectRoyalSound(AntarchySoundEvents.KING_SUCCESS.get(), 4.0F, 1.0F, target);
|
||||
this.endDecree(target);
|
||||
return;
|
||||
}
|
||||
if (this.activeDecree != null && --this.activeDecreeTicks <= 0) {
|
||||
@@ -1420,9 +1582,34 @@ public class KingEntity extends RoyalBossEntity {
|
||||
.withStyle(style -> style.withColor(ChatFormatting.WHITE))));
|
||||
}
|
||||
|
||||
private void broadcastActiveDecree(ServerLevel level) {
|
||||
if (this.activeDecree == null) {
|
||||
return;
|
||||
}
|
||||
for (ServerPlayer participant : this.royalEncounterPlayers(level)) {
|
||||
if (this.decreeRecipients.add(participant.getUUID())) {
|
||||
this.sendDecreeTitle(participant);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int decreeCooldownForPhase() {
|
||||
double scale = switch (this.phase()) {
|
||||
case ONE -> 1.0D;
|
||||
case TWO -> 0.82D;
|
||||
case THREE -> 0.65D;
|
||||
};
|
||||
return Math.max(200, Mth.floor(AntarchySettings.royalDecreeCooldownTicks() * scale));
|
||||
}
|
||||
|
||||
private void endDecree(@Nullable LivingEntity target) {
|
||||
if (target instanceof ServerPlayer player) {
|
||||
player.connection.send(new ClientboundClearTitlesPacket(false));
|
||||
if (this.level() instanceof ServerLevel level) {
|
||||
for (UUID recipientId : this.decreeRecipients) {
|
||||
Player recipientPlayer = level.getPlayerByUUID(recipientId);
|
||||
if (recipientPlayer instanceof ServerPlayer recipient) {
|
||||
recipient.connection.send(new ClientboundClearTitlesPacket(false));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this.activeDecree != null) {
|
||||
this.lastDecreeKey = this.activeDecree.translationKey();
|
||||
@@ -1431,9 +1618,10 @@ public class KingEntity extends RoyalBossEntity {
|
||||
this.activeDecree = null;
|
||||
this.activeDecreeTicks = 0;
|
||||
this.decreeRetreatPressure = false;
|
||||
this.closeQuartersEntered = false;
|
||||
this.closeQuartersOutsideTicks = 0;
|
||||
this.decreeCooldownTicks = AntarchySettings.royalDecreeCooldownTicks();
|
||||
this.closeQuartersEntered.clear();
|
||||
this.closeQuartersOutsideTicks.clear();
|
||||
this.decreeRecipients.clear();
|
||||
this.decreeCooldownTicks = this.decreeCooldownForPhase();
|
||||
this.startRoyalRecovery(40);
|
||||
}
|
||||
|
||||
@@ -1544,12 +1732,16 @@ public class KingEntity extends RoyalBossEntity {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this.activeDecree instanceof CloseQuartersDecree && projectile) {
|
||||
this.failActiveDecree(attacker);
|
||||
} else if (this.activeDecree instanceof ComeNoCloserDecree && !projectile) {
|
||||
this.failActiveDecree(attacker);
|
||||
} else if (this.activeDecree instanceof HandsOffTheCrownDecree) {
|
||||
this.failActiveDecree(attacker);
|
||||
boolean decreeApplies = attacker instanceof ServerPlayer
|
||||
&& this.decreeRecipients.contains(attacker.getUUID());
|
||||
if (decreeApplies) {
|
||||
if (this.activeDecree instanceof CloseQuartersDecree && projectile) {
|
||||
this.failActiveDecree(attacker);
|
||||
} else if (this.activeDecree instanceof ComeNoCloserDecree && !projectile) {
|
||||
this.failActiveDecree(attacker);
|
||||
} else if (this.activeDecree instanceof HandsOffTheCrownDecree) {
|
||||
this.failActiveDecree(attacker);
|
||||
}
|
||||
}
|
||||
}
|
||||
return super.hurt(source, amount);
|
||||
|
||||
+315
-9
@@ -17,9 +17,16 @@ import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.particles.DustParticleOptions;
|
||||
import net.minecraft.core.particles.ParticleTypes;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.protocol.game.ClientboundSetTitleTextPacket;
|
||||
import net.minecraft.network.protocol.game.ClientboundSetTitlesAnimationPacket;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.sounds.SoundEvent;
|
||||
import net.minecraft.sounds.SoundEvents;
|
||||
import net.minecraft.sounds.SoundSource;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.util.Mth;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.BossEvent;
|
||||
@@ -68,6 +75,13 @@ public class QueenEntity extends RoyalBossEntity {
|
||||
private static final int ACCELERATION_DURATION = 140;
|
||||
private static final int BLACK_HOLE_COOLDOWN = 340;
|
||||
private static final double QUEEN_BITE_COOLDOWN_MULTIPLIER = 1.5D;
|
||||
private static final int MOMENTUM_LOCK_TELEGRAPH_TICKS = 40;
|
||||
private static final int LANDING_TELEGRAPH_TICKS = 30;
|
||||
private static final int LANDING_IMPACT_TICKS = 18;
|
||||
private static final int GROUND_COMBAT_TICKS = 140;
|
||||
private static final int TAKEOFF_TICKS = 30;
|
||||
private static final int LANDING_COOLDOWN_MIN = 360;
|
||||
private static final int LANDING_COOLDOWN_VARIANCE = 240;
|
||||
|
||||
private int manticoreSummonCooldownTicks;
|
||||
private int gravityStompCooldownTicks;
|
||||
@@ -75,6 +89,14 @@ public class QueenEntity extends RoyalBossEntity {
|
||||
private int crushingGravityCooldownTicks;
|
||||
private int accelerationCooldownTicks;
|
||||
private int blackHoleCooldownTicks;
|
||||
private int momentumLockTelegraphTicks;
|
||||
private int queenLandingCooldownTicks = 300;
|
||||
private int queenLandingStageTicks;
|
||||
private QueenLandingStage queenLandingStage = QueenLandingStage.AERIAL;
|
||||
@Nullable
|
||||
private Vec3 queenLandingWaypoint;
|
||||
@Nullable
|
||||
private Phase pendingPhaseSignature;
|
||||
private final int[] dreamFireballCooldownTicks = new int[3];
|
||||
private int idleWanderCooldownTicks;
|
||||
private boolean manticoreAnimationPending;
|
||||
@@ -84,6 +106,15 @@ public class QueenEntity extends RoyalBossEntity {
|
||||
@Nullable
|
||||
private BlockPos naturalTrailHome;
|
||||
|
||||
private enum QueenLandingStage {
|
||||
AERIAL,
|
||||
TELEGRAPH,
|
||||
DESCENT,
|
||||
IMPACT,
|
||||
GROUND_COMBAT,
|
||||
TAKEOFF
|
||||
}
|
||||
|
||||
public QueenEntity(EntityType<? extends QueenEntity> entityType, Level level) {
|
||||
super(entityType, level);
|
||||
}
|
||||
@@ -107,6 +138,64 @@ public class QueenEntity extends RoyalBossEntity {
|
||||
return 0.72D;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean managesOwnCombatLocomotion() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean blocksRoyalAttacksForMovement() {
|
||||
return this.queenLandingStage == QueenLandingStage.TELEGRAPH
|
||||
|| this.queenLandingStage == QueenLandingStage.DESCENT
|
||||
|| this.queenLandingStage == QueenLandingStage.IMPACT
|
||||
|| this.queenLandingStage == QueenLandingStage.TAKEOFF;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onRoyalPhaseTransitionStarted(Phase previousPhase, Phase nextPhase) {
|
||||
this.resetQueenLandingSequence();
|
||||
this.momentumLockTelegraphTicks = 0;
|
||||
this.pendingPhaseSignature = null;
|
||||
this.getNavigation().stop();
|
||||
this.setDeltaMovement(this.getDeltaMovement().scale(0.35D));
|
||||
this.playRoyalSound(AntarchySoundEvents.QUEEN_ROAR.get(), nextPhase == Phase.THREE ? 0.66F : 0.82F);
|
||||
if (this.level() instanceof ServerLevel level) {
|
||||
Component title = Component.translatable(nextPhase == Phase.TWO
|
||||
? "boss.antarchy.queen.phase_two" : "boss.antarchy.queen.phase_three")
|
||||
.withStyle(style -> style.withColor(nextPhase == Phase.TWO
|
||||
? ChatFormatting.DARK_PURPLE : ChatFormatting.RED).withBold(true));
|
||||
for (ServerPlayer player : this.royalEncounterPlayers(level)) {
|
||||
player.connection.send(new ClientboundSetTitlesAnimationPacket(5, 45, 10));
|
||||
player.connection.send(new ClientboundSetTitleTextPacket(title));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void tickRoyalPhaseTransition(Phase nextPhase, int elapsedTicks, int remainingTicks) {
|
||||
this.getNavigation().stop();
|
||||
this.setDeltaMovement(this.getDeltaMovement().scale(0.7D));
|
||||
if (this.level() instanceof ServerLevel level && elapsedTicks % 3 == 0) {
|
||||
level.sendParticles(nextPhase == Phase.THREE ? ACCEL_DUST : ParticleTypes.REVERSE_PORTAL,
|
||||
this.getX(), this.getY() + this.getBbHeight() * 0.55D, this.getZ(),
|
||||
20, this.getBbWidth() * 0.25D, this.getBbHeight() * 0.25D,
|
||||
this.getBbWidth() * 0.25D, 0.08D);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onRoyalPhaseTransitionCompleted(Phase nextPhase) {
|
||||
if (nextPhase == Phase.TWO) {
|
||||
this.momentumLockCooldownTicks = 0;
|
||||
this.pendingPhaseSignature = Phase.TWO;
|
||||
this.queenLandingCooldownTicks = Math.min(this.queenLandingCooldownTicks, 100);
|
||||
} else if (nextPhase == Phase.THREE) {
|
||||
this.accelerationCooldownTicks = 0;
|
||||
this.startRoyalAcceleration();
|
||||
this.queenLandingCooldownTicks = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected double biteApproachSpeed() {
|
||||
return 1.55D;
|
||||
@@ -239,9 +328,21 @@ public class QueenEntity extends RoyalBossEntity {
|
||||
if (this.level().isClientSide || this.isDeadOrDying()) {
|
||||
return;
|
||||
}
|
||||
if (this.isRoyalPhaseTransitionActive()) {
|
||||
this.royalEffects.tick();
|
||||
return;
|
||||
}
|
||||
LivingEntity target = this.getTarget();
|
||||
if (target == null || !target.isAlive() || !this.canAttack(target)) {
|
||||
this.resetQueenLandingSequence();
|
||||
this.tickIdleWander();
|
||||
} else {
|
||||
this.tickQueenLandingSequence(target);
|
||||
}
|
||||
this.tickMomentumLockTelegraph();
|
||||
if (this.blocksRoyalAttacksForMovement()) {
|
||||
this.royalEffects.tick();
|
||||
return;
|
||||
}
|
||||
this.tickManticoreSummon();
|
||||
this.tickPendingManticoreAnimation();
|
||||
@@ -251,6 +352,150 @@ public class QueenEntity extends RoyalBossEntity {
|
||||
this.royalEffects.tick();
|
||||
}
|
||||
|
||||
private void tickQueenLandingSequence(LivingEntity target) {
|
||||
if (this.queenLandingStage == QueenLandingStage.AERIAL && this.isRoyalFlying()
|
||||
&& this.queenLandingCooldownTicks-- <= 0 && target.onGround()) {
|
||||
this.beginQueenLanding(target);
|
||||
}
|
||||
|
||||
switch (this.queenLandingStage) {
|
||||
case TELEGRAPH -> {
|
||||
this.getNavigation().stop();
|
||||
this.setDeltaMovement(this.getDeltaMovement().scale(0.72D));
|
||||
this.tickQueenLandingIndicator();
|
||||
if (--this.queenLandingStageTicks <= 0) {
|
||||
this.queenLandingStage = QueenLandingStage.DESCENT;
|
||||
this.playRoyalSound(AntarchySoundEvents.QUEEN_FLY_LOOP.get(), 0.72F);
|
||||
}
|
||||
}
|
||||
case DESCENT -> {
|
||||
Vec3 landing = this.queenLandingWaypoint;
|
||||
if (landing == null) {
|
||||
this.resetQueenLandingSequence();
|
||||
return;
|
||||
}
|
||||
this.setRoyalFlying(true);
|
||||
this.getMoveControl().setWantedPosition(landing.x, landing.y, landing.z, 1.15D);
|
||||
this.tickQueenLandingIndicator();
|
||||
boolean reachedLanding = this.onGround() || this.position().distanceToSqr(landing) <= 2.25D;
|
||||
if (reachedLanding) {
|
||||
this.setRoyalFlying(false);
|
||||
this.queenLandingStage = QueenLandingStage.IMPACT;
|
||||
this.queenLandingStageTicks = LANDING_IMPACT_TICKS;
|
||||
this.performGravityStomp((ServerLevel) this.level(), target);
|
||||
}
|
||||
}
|
||||
case IMPACT -> {
|
||||
this.getNavigation().stop();
|
||||
this.setDeltaMovement(Vec3.ZERO);
|
||||
if (--this.queenLandingStageTicks <= 0) {
|
||||
this.queenLandingStage = QueenLandingStage.GROUND_COMBAT;
|
||||
this.queenLandingStageTicks = GROUND_COMBAT_TICKS;
|
||||
this.queenLandingWaypoint = null;
|
||||
}
|
||||
}
|
||||
case GROUND_COMBAT -> {
|
||||
if (--this.queenLandingStageTicks <= 0) {
|
||||
this.beginQueenTakeoff();
|
||||
}
|
||||
}
|
||||
case TAKEOFF -> {
|
||||
if (--this.queenLandingStageTicks <= 0) {
|
||||
this.queenLandingStage = QueenLandingStage.AERIAL;
|
||||
this.queenLandingCooldownTicks = LANDING_COOLDOWN_MIN
|
||||
+ this.random.nextInt(LANDING_COOLDOWN_VARIANCE + 1);
|
||||
}
|
||||
}
|
||||
case AERIAL -> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void beginQueenLanding(LivingEntity target) {
|
||||
this.queenLandingWaypoint = this.findQueenLandingPoint(target);
|
||||
if (this.queenLandingWaypoint == null) {
|
||||
this.queenLandingCooldownTicks = 100;
|
||||
return;
|
||||
}
|
||||
this.queenLandingStage = QueenLandingStage.TELEGRAPH;
|
||||
this.queenLandingStageTicks = LANDING_TELEGRAPH_TICKS;
|
||||
this.playRoyalSound(AntarchySoundEvents.QUEEN_ROAR.get(), 1.08F);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Vec3 findQueenLandingPoint(LivingEntity target) {
|
||||
Vec3 away = this.position().subtract(target.position()).multiply(1.0D, 0.0D, 1.0D);
|
||||
double baseAngle = away.lengthSqr() > 1.0E-4D
|
||||
? Math.atan2(away.z, away.x) : this.random.nextDouble() * Mth.TWO_PI;
|
||||
boolean inverted = AntarchyGravityApi.isGravityInverted(this);
|
||||
double[] radii = {10.0D, 14.0D, 18.0D, 7.0D};
|
||||
for (int angleIndex = 0; angleIndex < 12; angleIndex++) {
|
||||
double angle = baseAngle + angleIndex * Mth.TWO_PI / 12.0D;
|
||||
for (double radius : radii) {
|
||||
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) + 0.1D;
|
||||
Vec3 candidate = new Vec3(x, y, z);
|
||||
AABB box = this.getBoundingBox().move(
|
||||
candidate.x - this.getX(), candidate.y - this.getY(), candidate.z - this.getZ());
|
||||
if (this.level().noCollision(this, box)) {
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void tickQueenLandingIndicator() {
|
||||
if (!(this.level() instanceof ServerLevel level) || this.queenLandingWaypoint == null
|
||||
|| this.tickCount % 2 != 0) {
|
||||
return;
|
||||
}
|
||||
double progress = this.queenLandingStage == QueenLandingStage.TELEGRAPH
|
||||
? 1.0D - this.queenLandingStageTicks / (double) LANDING_TELEGRAPH_TICKS : 1.0D;
|
||||
double radius = Mth.lerp(progress, 4.0D, 18.0D);
|
||||
double indicatorY = this.queenLandingWaypoint.y
|
||||
+ (AntarchyGravityApi.isGravityInverted(this) ? this.getBbHeight() : 0.2D);
|
||||
for (int index = 0; index < 40; index++) {
|
||||
double angle = Mth.TWO_PI * index / 40.0D;
|
||||
level.sendParticles(ParticleTypes.REVERSE_PORTAL,
|
||||
this.queenLandingWaypoint.x + Math.cos(angle) * radius,
|
||||
indicatorY,
|
||||
this.queenLandingWaypoint.z + Math.sin(angle) * radius,
|
||||
1, 0.0D, 0.0D, 0.0D, 0.0D);
|
||||
}
|
||||
}
|
||||
|
||||
private void beginQueenTakeoff() {
|
||||
this.queenLandingStage = QueenLandingStage.TAKEOFF;
|
||||
this.queenLandingStageTicks = TAKEOFF_TICKS;
|
||||
this.triggerAnim("wing_action", "wing_gust");
|
||||
this.playRoyalSound(AntarchySoundEvents.QUEEN_FLY_LOOP.get(), 0.9F);
|
||||
if (this.level() instanceof ServerLevel level) {
|
||||
level.sendParticles(ParticleTypes.REVERSE_PORTAL,
|
||||
this.getX(), this.getY() + this.getBbHeight() * 0.4D, this.getZ(),
|
||||
60, 7.0D, 1.0D, 7.0D, 0.12D);
|
||||
}
|
||||
this.setRoyalFlying(true);
|
||||
double lift = AntarchyGravityApi.isGravityInverted(this) ? -0.42D : 0.42D;
|
||||
this.setDeltaMovement(this.getDeltaMovement().add(0.0D, lift, 0.0D));
|
||||
this.hasImpulse = true;
|
||||
}
|
||||
|
||||
private void resetQueenLandingSequence() {
|
||||
if (this.queenLandingStage != QueenLandingStage.AERIAL) {
|
||||
this.setRoyalFlying(true);
|
||||
}
|
||||
this.queenLandingStage = QueenLandingStage.AERIAL;
|
||||
this.queenLandingStageTicks = 0;
|
||||
this.queenLandingWaypoint = null;
|
||||
if (this.queenLandingCooldownTicks <= 0) {
|
||||
this.queenLandingCooldownTicks = 100;
|
||||
}
|
||||
}
|
||||
|
||||
private void tickDreamFireballs() {
|
||||
if (this.isRoyalRecoveryActive()) return;
|
||||
if (!(this.level() instanceof ServerLevel level)) return;
|
||||
@@ -478,14 +723,17 @@ public class QueenEntity extends RoyalBossEntity {
|
||||
String[] candidates = phase == Phase.ONE && !this.isMomentumLockAvailableEarly()
|
||||
? new String[] {"gravity_stomp", "black_hole"}
|
||||
: new String[] {"gravity_stomp", "black_hole", "momentum_lock", "crushing_gravity", "acceleration"};
|
||||
String selected = this.attackScheduler.chooseWeighted(candidates, id -> {
|
||||
if (!this.queenAttackReady(id, phase, majorBusy, allowOverlap)) return 0;
|
||||
return switch (id) {
|
||||
case "black_hole", "crushing_gravity" -> phase == Phase.THREE ? 5 : 3;
|
||||
case "momentum_lock", "acceleration" -> phase == Phase.THREE ? 4 : 2;
|
||||
default -> 4;
|
||||
};
|
||||
}, this.random);
|
||||
String selected = this.pendingPhaseSignature == Phase.TWO
|
||||
&& this.queenAttackReady("momentum_lock", phase, majorBusy, allowOverlap)
|
||||
? "momentum_lock"
|
||||
: this.attackScheduler.chooseWeighted(candidates, id -> {
|
||||
if (!this.queenAttackReady(id, phase, majorBusy, allowOverlap)) return 0;
|
||||
return switch (id) {
|
||||
case "black_hole", "crushing_gravity" -> phase == Phase.THREE ? 5 : 3;
|
||||
case "momentum_lock", "acceleration" -> phase == Phase.THREE ? 4 : 2;
|
||||
default -> 4;
|
||||
};
|
||||
}, this.random);
|
||||
if (selected == null) return;
|
||||
int cooldown = switch (selected) {
|
||||
case "gravity_stomp" -> GRAVITY_STOMP_COOLDOWN;
|
||||
@@ -495,7 +743,7 @@ public class QueenEntity extends RoyalBossEntity {
|
||||
default -> ACCELERATION_COOLDOWN;
|
||||
};
|
||||
RoyalAttackLane lane = "black_hole".equals(selected) ? RoyalAttackLane.CENTER_HEAD : RoyalAttackLane.HAZARD;
|
||||
int actionWindup = 10;
|
||||
int actionWindup = "momentum_lock".equals(selected) ? MOMENTUM_LOCK_TELEGRAPH_TICKS : 10;
|
||||
int actionRecovery = "momentum_lock".equals(selected)
|
||||
? this.animationRecovery(55, actionWindup, 1, 14)
|
||||
: this.animationRecovery(25, actionWindup, 1, 14);
|
||||
@@ -506,12 +754,17 @@ public class QueenEntity extends RoyalBossEntity {
|
||||
QueenEntity.this.triggerAnim("body_action", "stomp");
|
||||
} else if ("momentum_lock".equals(selected)) {
|
||||
QueenEntity.this.triggerAnim("wing_action", "wing_gust");
|
||||
QueenEntity.this.momentumLockTelegraphTicks = MOMENTUM_LOCK_TELEGRAPH_TICKS;
|
||||
QueenEntity.this.playRoyalSound(AntarchySoundEvents.QUEEN_ROAR.get(), 0.58F);
|
||||
} else if ("black_hole".equals(selected)) {
|
||||
QueenEntity.this.royalHead(RoyalHead.Slot.CENTER).startShoot();
|
||||
QueenEntity.this.triggerAnim(RoyalHead.Slot.CENTER.controllerName(), "shoot");
|
||||
}
|
||||
}
|
||||
@Override public void onActive(int elapsedTicks) {
|
||||
if ("momentum_lock".equals(selected)) {
|
||||
QueenEntity.this.momentumLockTelegraphTicks = 0;
|
||||
}
|
||||
QueenEntity.this.performSelectedQueenAttack(selected, cooldown, level, target);
|
||||
}
|
||||
@Override public void onComplete() {
|
||||
@@ -520,6 +773,9 @@ public class QueenEntity extends RoyalBossEntity {
|
||||
}
|
||||
}
|
||||
})) return;
|
||||
if ("momentum_lock".equals(selected)) {
|
||||
this.pendingPhaseSignature = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void performSelectedQueenAttack(String selected, int cooldown,
|
||||
@@ -599,6 +855,51 @@ public class QueenEntity extends RoyalBossEntity {
|
||||
30, 0.6D, 0.6D, 0.6D, 0.25D);
|
||||
}
|
||||
|
||||
private void tickMomentumLockTelegraph() {
|
||||
if (this.momentumLockTelegraphTicks <= 0 || !(this.level() instanceof ServerLevel level)) {
|
||||
return;
|
||||
}
|
||||
this.getNavigation().stop();
|
||||
this.setDeltaMovement(Vec3.ZERO);
|
||||
int elapsed = MOMENTUM_LOCK_TELEGRAPH_TICKS - this.momentumLockTelegraphTicks;
|
||||
if (this.tickCount % 2 == 0) {
|
||||
double inwardRadius = Mth.lerp(elapsed / (double) MOMENTUM_LOCK_TELEGRAPH_TICKS,
|
||||
MOMENTUM_LOCK_RADIUS, 2.0D);
|
||||
for (int index = 0; index < 48; index++) {
|
||||
double angle = Mth.TWO_PI * index / 48.0D;
|
||||
double cos = Math.cos(angle);
|
||||
double sin = Math.sin(angle);
|
||||
level.sendParticles(ParticleTypes.END_ROD,
|
||||
this.getX() + cos * MOMENTUM_LOCK_RADIUS,
|
||||
this.getY() + this.getBbHeight() * 0.45D,
|
||||
this.getZ() + sin * MOMENTUM_LOCK_RADIUS,
|
||||
1, 0.0D, 0.0D, 0.0D, 0.0D);
|
||||
if (index % 2 == 0) {
|
||||
level.sendParticles(ParticleTypes.REVERSE_PORTAL,
|
||||
this.getX() + cos * inwardRadius,
|
||||
this.getY() + this.getBbHeight() * 0.45D,
|
||||
this.getZ() + sin * inwardRadius,
|
||||
1, 0.0D, 0.0D, 0.0D, 0.0D);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this.momentumLockTelegraphTicks <= 10) {
|
||||
for (LivingEntity living : level.getEntitiesOfClass(LivingEntity.class,
|
||||
this.getBoundingBox().inflate(MOMENTUM_LOCK_RADIUS), this::canDamageWithRoyalAttack)) {
|
||||
level.sendParticles(ParticleTypes.END_ROD,
|
||||
living.getX(), living.getY() + living.getBbHeight() * 0.5D, living.getZ(),
|
||||
2, 0.25D, 0.35D, 0.25D, 0.0D);
|
||||
}
|
||||
}
|
||||
if (this.momentumLockTelegraphTicks == 40 || this.momentumLockTelegraphTicks == 20
|
||||
|| this.momentumLockTelegraphTicks == 10 || this.momentumLockTelegraphTicks == 4) {
|
||||
float pitch = 0.65F + elapsed / (float) MOMENTUM_LOCK_TELEGRAPH_TICKS;
|
||||
level.playSound(null, this.blockPosition(), SoundEvents.BEACON_POWER_SELECT,
|
||||
SoundSource.HOSTILE, 2.0F, pitch);
|
||||
}
|
||||
this.momentumLockTelegraphTicks--;
|
||||
}
|
||||
|
||||
private void startMomentumLock(ServerLevel level) {
|
||||
this.momentumLockCooldownTicks = MOMENTUM_LOCK_COOLDOWN;
|
||||
this.royalEffects.start("momentum_lock", MOMENTUM_LOCK_DURATION, () -> {
|
||||
@@ -755,8 +1056,11 @@ public class QueenEntity extends RoyalBossEntity {
|
||||
super.addAdditionalSaveData(tag);
|
||||
tag.putInt(SUMMON_COOLDOWN_KEY, this.manticoreSummonCooldownTicks);
|
||||
tag.putInt("GravityStompCooldownTicks", this.gravityStompCooldownTicks);
|
||||
tag.putInt("MomentumLockCooldownTicks", this.momentumLockCooldownTicks);
|
||||
tag.putInt("CrushingGravityCooldownTicks", this.crushingGravityCooldownTicks);
|
||||
tag.putInt("AccelerationCooldownTicks", this.accelerationCooldownTicks);
|
||||
tag.putInt("BlackHoleCooldownTicks", this.blackHoleCooldownTicks);
|
||||
tag.putInt("QueenLandingCooldownTicks", this.queenLandingCooldownTicks);
|
||||
if (this.naturalTrailHome != null) {
|
||||
tag.putLong("NaturalTrailSiteId", this.naturalTrailSiteId);
|
||||
tag.putLong("NaturalTrailHome", this.naturalTrailHome.asLong());
|
||||
@@ -772,6 +1076,8 @@ public class QueenEntity extends RoyalBossEntity {
|
||||
this.crushingGravityCooldownTicks = Math.max(0, tag.getInt("CrushingGravityCooldownTicks"));
|
||||
this.accelerationCooldownTicks = Math.max(0, tag.getInt("AccelerationCooldownTicks"));
|
||||
this.blackHoleCooldownTicks = Math.max(0, tag.getInt("BlackHoleCooldownTicks"));
|
||||
this.queenLandingCooldownTicks = tag.contains("QueenLandingCooldownTicks")
|
||||
? Math.max(0, tag.getInt("QueenLandingCooldownTicks")) : 300;
|
||||
if (tag.contains("NaturalTrailHome")) {
|
||||
this.naturalTrailSiteId = tag.getLong("NaturalTrailSiteId");
|
||||
this.naturalTrailHome = BlockPos.of(tag.getLong("NaturalTrailHome"));
|
||||
|
||||
+76
-1
@@ -15,6 +15,7 @@ import com.craisinlord.antarchy.content.gravity.AntarchyGravityRotationUtil;
|
||||
import com.craisinlord.antarchy.content.worldgen.thoraxis.ThoraxisUndersideManager;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
@@ -186,6 +187,10 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
|
||||
private Vec3 aerialCombatAnchor;
|
||||
private int aerialCombatAnchorTicks;
|
||||
private int recoveryWindowTicks;
|
||||
@Nullable
|
||||
private Phase trackedPhase;
|
||||
private int phaseTransitionTicks;
|
||||
private int phaseTransitionDuration;
|
||||
private boolean multiplayerScalingInitialized;
|
||||
private final Set<UUID> encounterParticipants = new HashSet<>();
|
||||
private double royalDamageMultiplier = 1.0D;
|
||||
@@ -297,6 +302,24 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
|
||||
return 1.3D;
|
||||
}
|
||||
|
||||
/** Prevents normal head/body scheduling while a bespoke movement sequence owns the boss. */
|
||||
protected boolean blocksRoyalAttacksForMovement() {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected int royalPhaseTransitionDuration(Phase nextPhase) {
|
||||
return 50;
|
||||
}
|
||||
|
||||
protected void onRoyalPhaseTransitionStarted(Phase previousPhase, Phase nextPhase) {
|
||||
}
|
||||
|
||||
protected void tickRoyalPhaseTransition(Phase nextPhase, int elapsedTicks, int remainingTicks) {
|
||||
}
|
||||
|
||||
protected void onRoyalPhaseTransitionCompleted(Phase nextPhase) {
|
||||
}
|
||||
|
||||
/** Allows a boss to tune its head-bite concurrency without changing the other royal boss. */
|
||||
protected int maxConcurrentHeadAttacks(Phase phase) {
|
||||
return phase.maxConcurrentHeadAttacks();
|
||||
@@ -462,7 +485,8 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
|
||||
if (this.level().isClientSide) {
|
||||
return;
|
||||
}
|
||||
if (this.isRoyalFlying() && !this.isDeadOrDying() && !this.landingForCombat) {
|
||||
if (this.isRoyalFlying() && !this.isDeadOrDying() && !this.landingForCombat
|
||||
&& !this.blocksRoyalAttacksForMovement()) {
|
||||
this.tickFlyingAltitude();
|
||||
}
|
||||
|
||||
@@ -480,6 +504,23 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
|
||||
return;
|
||||
}
|
||||
|
||||
Phase currentPhase = this.phase();
|
||||
if (this.trackedPhase == null) {
|
||||
this.trackedPhase = currentPhase;
|
||||
} else if (currentPhase != this.trackedPhase) {
|
||||
Phase previousPhase = this.trackedPhase;
|
||||
this.trackedPhase = currentPhase;
|
||||
this.startRoyalPhaseTransition(previousPhase, currentPhase);
|
||||
}
|
||||
if (this.phaseTransitionTicks > 0) {
|
||||
int elapsedTicks = this.phaseTransitionDuration - this.phaseTransitionTicks;
|
||||
this.tickRoyalPhaseTransition(this.trackedPhase, elapsedTicks, this.phaseTransitionTicks);
|
||||
if (--this.phaseTransitionTicks == 0) {
|
||||
this.onRoyalPhaseTransitionCompleted(this.trackedPhase);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
LivingEntity primaryTarget = this.getTarget();
|
||||
if (primaryTarget == null || !primaryTarget.isAlive() || !this.canAttack(primaryTarget)) {
|
||||
for (RoyalHead head : this.heads) {
|
||||
@@ -491,6 +532,9 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
|
||||
this.initializeMultiplayerScaling(primaryTarget);
|
||||
|
||||
this.tickCombatLocomotionMode(primaryTarget);
|
||||
if (this.blocksRoyalAttacksForMovement()) {
|
||||
return;
|
||||
}
|
||||
boolean aeriallyStabilized = this.steerTowardTarget(primaryTarget);
|
||||
if (!aeriallyStabilized) {
|
||||
this.getLookControl().setLookAt(primaryTarget, 30.0F, 30.0F);
|
||||
@@ -642,6 +686,37 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
|
||||
this.recoveryWindowTicks = Math.max(this.recoveryWindowTicks, ticks);
|
||||
}
|
||||
|
||||
protected final boolean isRoyalPhaseTransitionActive() {
|
||||
return this.phaseTransitionTicks > 0;
|
||||
}
|
||||
|
||||
private void startRoyalPhaseTransition(Phase previousPhase, Phase nextPhase) {
|
||||
this.phaseTransitionDuration = Math.max(1, this.royalPhaseTransitionDuration(nextPhase));
|
||||
this.phaseTransitionTicks = this.phaseTransitionDuration;
|
||||
for (RoyalAttackLane lane : RoyalAttackLane.values()) {
|
||||
this.attackScheduler.cancel(lane);
|
||||
}
|
||||
for (RoyalHead head : this.heads) {
|
||||
if (head.beamActive() || head.shooting()) {
|
||||
this.stopRoyalBeam(head);
|
||||
}
|
||||
}
|
||||
this.getNavigation().stop();
|
||||
this.onRoyalPhaseTransitionStarted(previousPhase, nextPhase);
|
||||
}
|
||||
|
||||
/** Returns the live players currently participating in the encounter. */
|
||||
protected final List<ServerPlayer> royalEncounterPlayers(ServerLevel level) {
|
||||
double range = Math.max(32.0D, this.getAttributeValue(Attributes.FOLLOW_RANGE));
|
||||
LinkedHashSet<ServerPlayer> players = new LinkedHashSet<>(this.bossEvent.getPlayers());
|
||||
players.addAll(level.getPlayers(player -> player.isAlive()
|
||||
&& !player.isSpectator()
|
||||
&& player.distanceToSqr(this) <= range * range));
|
||||
players.removeIf(player -> !player.isAlive() || player.isSpectator() || player.level() != level
|
||||
|| player.distanceToSqr(this) > range * range);
|
||||
return List.copyOf(players);
|
||||
}
|
||||
|
||||
private void initializeMultiplayerScaling(LivingEntity target) {
|
||||
if (this.multiplayerScalingInitialized || !(this.level() instanceof ServerLevel level)) {
|
||||
return;
|
||||
|
||||
+15
-3
@@ -10,13 +10,25 @@ public final class KneelDecree implements RoyalDecree {
|
||||
public String translationKey() { return "decree.antarchy.kneel"; }
|
||||
public void apply(ServerLevel level, KingEntity king, LivingEntity target) {
|
||||
UUID id = target.getUUID();
|
||||
if (target.isCrouching()) { deadlines.remove(id); king.clearActiveDecree(target); return; }
|
||||
int deadline = deadlines.computeIfAbsent(id, ignored -> target.tickCount + 100);
|
||||
if (target.tickCount >= deadline) { king.failActiveDecree(target); }
|
||||
deadlines.computeIfAbsent(id, ignored -> target.tickCount + 100);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Evaluation evaluate(ServerLevel level, KingEntity king, LivingEntity target) {
|
||||
if (target.isCrouching()) {
|
||||
return Evaluation.COMPLETE;
|
||||
}
|
||||
int deadline = deadlines.computeIfAbsent(target.getUUID(), ignored -> target.tickCount + 100);
|
||||
return target.tickCount >= deadline ? Evaluation.VIOLATED : Evaluation.COMPLIANT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int countdownTicks(LivingEntity target) {
|
||||
return this.deadlines.getOrDefault(target.getUUID(), target.tickCount + 100) - target.tickCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnded() {
|
||||
this.deadlines.clear();
|
||||
}
|
||||
}
|
||||
@@ -1245,6 +1245,10 @@
|
||||
"entity.antarchy.princess": "Princess",
|
||||
"entity.antarchy.king": "The King",
|
||||
"entity.antarchy.queen": "The Queen",
|
||||
"boss.antarchy.king.phase_two": "THE LAW TIGHTENS",
|
||||
"boss.antarchy.king.phase_three": "ROYAL JUDGMENT",
|
||||
"boss.antarchy.queen.phase_two": "GRAVITY DOMINION",
|
||||
"boss.antarchy.queen.phase_three": "TEMPORAL COLLAPSE",
|
||||
"entity.antarchy.manticore": "Manticore",
|
||||
"entity.antarchy.royal_black_hole": "Singularity",
|
||||
"entity.antarchy.royal_bolt": "Royal Bolt",
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"sender": "bloxiclean@antmail.com",
|
||||
"subject": "IS YOUR HIVE QUEEN DUSTY?",
|
||||
"body": "Bloxiclean now offers premium royal-area cleaning. We remove ash, ichor, bloodglass dust, and other difficult stains. Queen not included. Prices may vary by dimension.",
|
||||
"subject": "IS YOUR CARPET DIRTY?",
|
||||
"body": "Bloxiclean now offers premium royal-area cleaning. We remove ash, ichor, bloodglass dust, and other difficult stains. Prices may vary by dimension.",
|
||||
"trigger": {
|
||||
"type": "random",
|
||||
"value": 0.01
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"sender": "bloxiclean@antmail.com",
|
||||
"subject": "BLOXICLEAN: WE MAKE BLOCKS SPARKLE",
|
||||
"body": "Cobwebs? Sculk? Suspicious residue? Bloxiclean removes nearly anything from nearly anywhere. Book your complimentary pressure-wash before the stains become lore.",
|
||||
"body": "Cobwebs? Sculk? Suspicious residue? Bloxiclean removes nearly anything from nearly anywhere. Book your complimentary pressure-wash before the stains become ancient history.",
|
||||
"trigger": {
|
||||
"type": "random",
|
||||
"value": 0.01
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"sender": "dr_traeoris@antmail.com",
|
||||
"sender": "noreply@antmail.com",
|
||||
"subject": "WELCOME TO ANTMAIL",
|
||||
"body": "Your address has been registered. If this message arrived late, the network probably had ants in it.",
|
||||
"trigger": {
|
||||
|
||||
Reference in new issue
Block a user