diff --git a/common-1.20.1/src/main/resources/data/antarchy/worldgen/structure_set/tigers_eye_shrine.json b/common-1.20.1/src/main/resources/data/antarchy/worldgen/structure_set/tigers_eye_shrine.json new file mode 100644 index 00000000..5df0708e --- /dev/null +++ b/common-1.20.1/src/main/resources/data/antarchy/worldgen/structure_set/tigers_eye_shrine.json @@ -0,0 +1,14 @@ +{ + "structures": [ + { + "structure": "antarchy:tigers_eye_shrine", + "weight": 1 + } + ], + "placement": { + "salt": 149275031, + "spacing": 24, + "separation": 16, + "type": "integrated_api:advanced_random_spread" + } +} diff --git a/common-1.20.1/src/main/resources/data/antarchy/worldgen/structure_set/underside.json b/common-1.20.1/src/main/resources/data/antarchy/worldgen/structure_set/underside.json new file mode 100644 index 00000000..0ca3e80f --- /dev/null +++ b/common-1.20.1/src/main/resources/data/antarchy/worldgen/structure_set/underside.json @@ -0,0 +1,18 @@ +{ + "structures": [ + { + "structure": "antarchy:underside_ruins", + "weight": 2 + }, + { + "structure": "antarchy:underside_castle", + "weight": 1 + } + ], + "placement": { + "salt": 184736291, + "spacing": 24, + "separation": 12, + "type": "integrated_api:advanced_random_spread" + } +} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/config/AntarchySettings.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/config/AntarchySettings.java index 0b80bcbe..cff0ae71 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/config/AntarchySettings.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/config/AntarchySettings.java @@ -578,7 +578,7 @@ public final class AntarchySettings { private static double kingBeamTracking = 0.1D; private static double kingBeamTerrainRadius = 5.0D; private static int kingBeamTerrainCap = 192; - private static int royalDecreeCooldownTicks = 1200; + private static int royalDecreeCooldownTicks = 700; private static int kingRoyalMusterCooldownTicks = 500; private static int kingRoyalMusterWindupTicks = 30; private static int kingRoyalMusterDurationTicks = 600; diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/block/entity/QueenTrailSpawnMarkerBlockEntity.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/block/entity/QueenTrailSpawnMarkerBlockEntity.java index 22eb913e..fe5e04cc 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/block/entity/QueenTrailSpawnMarkerBlockEntity.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/block/entity/QueenTrailSpawnMarkerBlockEntity.java @@ -68,12 +68,32 @@ public final class QueenTrailSpawnMarkerBlockEntity extends BlockEntity { level.getChunk(chunkX, chunkZ); } } - QueenEntity queen = QueenEntity.spawnFromUndersideTrail(level, marker.spawnPos, marker.homePos, marker.siteId, marker.yaw); - if (queen != null) { - marker.consumed = true; - marker.queenUuid = queen.getUUID(); - marker.setChanged(); + marker.spawnNow(level); + } + + @Nullable + public QueenEntity spawnNow(ServerLevel level) { + if (!this.configured) { + return null; } + if (this.consumed) { + if (this.queenUuid != null && level.getEntity(this.queenUuid) instanceof QueenEntity queen) { + return queen; + } + return null; + } + for (int chunkX = (this.spawnPos.getX() - 16) >> 4; chunkX <= (this.spawnPos.getX() + 16) >> 4; chunkX++) { + for (int chunkZ = (this.spawnPos.getZ() - 16) >> 4; chunkZ <= (this.spawnPos.getZ() + 16) >> 4; chunkZ++) { + level.getChunk(chunkX, chunkZ); + } + } + QueenEntity queen = QueenEntity.spawnFromUndersideTrail(level, this.spawnPos, this.homePos, this.siteId, this.yaw); + if (queen != null) { + this.consumed = true; + this.queenUuid = queen.getUUID(); + this.setChanged(); + } + return queen; } @Override diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/block/entity/UndertrialSpawnerBlockEntity.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/block/entity/UndertrialSpawnerBlockEntity.java index ef33bce9..3f893500 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/block/entity/UndertrialSpawnerBlockEntity.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/block/entity/UndertrialSpawnerBlockEntity.java @@ -92,6 +92,10 @@ public final class UndertrialSpawnerBlockEntity extends BlockEntity implements T pos.getZ() + 0.5D + (level.random.nextDouble() - 0.5D) * 0.7D, 0.0D, 0.01D, 0.0D); } } else if (currentState == TrialSpawnerState.WAITING_FOR_PLAYERS && blockEntity.previousClientState != currentState) { + ResourceLocation detectionSound = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, + level.random.nextBoolean() ? "undertrial_detect_player1" : "undertrial_detect_player2"); + level.playSound(null, pos, BuiltInRegistries.SOUND_EVENT.get(detectionSound), + net.minecraft.sounds.SoundSource.BLOCKS, 1.0F, 1.0F); for (int i = 0; i < 12; i++) { level.addParticle(blockEntity.particle("undertrial_spawner_detection"), pos.getX() + 0.5D + (level.random.nextDouble() - 0.5D) * 1.4D, pos.getY() + 0.15D + level.random.nextDouble() * 0.7D, diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/command/QueenLocateCommand.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/command/QueenLocateCommand.java index 69ee13d2..fb5fba4a 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/command/QueenLocateCommand.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/command/QueenLocateCommand.java @@ -3,6 +3,7 @@ package com.craisinlord.antarchy.content.command; import com.craisinlord.antarchy.Antarchy; import com.craisinlord.antarchy.content.block.entity.QueenTrailSpawnMarkerBlockEntity; import com.craisinlord.antarchy.content.worldgen.thoraxis.QueenTrailGrid; +import com.craisinlord.antarchy.content.entity.royal.QueenEntity; import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.context.CommandContext; import java.util.ArrayList; @@ -67,7 +68,11 @@ public final class QueenLocateCommand { if (marker == null) { continue; } + QueenEntity queen = marker.spawnNow(thoraxis); BlockPos spawn = marker.getSpawnPos(); + if (queen != null) { + spawn = BlockPos.containing(queen.position()); + } int distance = Mth.floor(Math.sqrt(distanceSquared(originX, originZ, site))); String coordinates = "[" + spawn.getX() + ", " + spawn.getY() + ", " + spawn.getZ() + "]"; String teleportCommand = "/execute in " + THORAXIS.location() + " run tp @s " diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/MissileSquidEntity.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/MissileSquidEntity.java index 60262c9e..c18a37c0 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/MissileSquidEntity.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/MissileSquidEntity.java @@ -6,6 +6,7 @@ import com.craisinlord.antarchy.content.entity.kraken.KrakenEntity; import com.craisinlord.antarchy.content.gravity.AntarchyGravityApi; import com.craisinlord.antarchy.content.gravity.AntarchyGravityDirection; import com.craisinlord.antarchy.content.gravity.AntarchyGravityRotationUtil; +import com.craisinlord.antarchy.content.gravity.AntarchyGravityTransition; import net.minecraft.core.BlockPos; import net.minecraft.core.particles.ParticleTypes; import net.minecraft.nbt.CompoundTag; @@ -412,6 +413,9 @@ public class MissileSquidEntity extends Monster implements GeoEntity { this.launchOwnerUuid = owner != null ? owner.getUUID() : null; this.setTarget(null); this.setNoGravity(false); + if (owner != null && AntarchyGravityApi.isGravityInverted(owner)) { + AntarchyGravityApi.setAirborneGravityDirection(this, AntarchyGravityApi.getGravityDirection(owner), true, AntarchyGravityTransition.INSTANT); + } this.setDeltaMovement(velocity); this.hasImpulse = true; this.updateAnimationState(); diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/OctopusBombEntity.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/OctopusBombEntity.java index 94a7a9eb..82492bae 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/OctopusBombEntity.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/OctopusBombEntity.java @@ -357,6 +357,14 @@ public class OctopusBombEntity extends Monster implements GeoEntity { public void launchAsExplosiveProjectile(net.minecraft.world.entity.Entity launcher, Vec3 velocity) { this.explosiveImpact = true; this.explosiveLauncherId = launcher != null ? launcher.getUUID() : null; + if (launcher != null && com.craisinlord.antarchy.content.gravity.AntarchyGravityApi.isGravityInverted(launcher)) { + com.craisinlord.antarchy.content.gravity.AntarchyGravityApi.setAirborneGravityDirection( + this, + com.craisinlord.antarchy.content.gravity.AntarchyGravityApi.getGravityDirection(launcher), + true, + com.craisinlord.antarchy.content.gravity.AntarchyGravityTransition.INSTANT + ); + } this.setDeltaMovement(velocity); this.hasImpulse = true; } diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/ant/AntTeleportHelper.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/ant/AntTeleportHelper.java index b14d65c6..389ba6a5 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/ant/AntTeleportHelper.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/ant/AntTeleportHelper.java @@ -1,6 +1,7 @@ package com.craisinlord.antarchy.content.entity.ant; import com.craisinlord.antarchy.config.AntarchySettings; +import com.craisinlord.antarchy.content.portal.PermanentPortalType; import java.util.ArrayList; import java.util.LinkedHashSet; import java.util.List; @@ -9,8 +10,8 @@ import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.nbt.CompoundTag; import net.minecraft.resources.ResourceKey; -import net.minecraft.network.chat.Component; import net.minecraft.resources.ResourceLocation; +import net.minecraft.network.chat.Component; import net.minecraft.server.level.PlayerRespawnLogic; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; @@ -37,6 +38,8 @@ import org.jetbrains.annotations.Nullable; public final class AntTeleportHelper { private static final double COMPANION_FOLLOW_RADIUS = 16.0D; + private static final int ARRIVAL_SEARCH_RADIUS = 24; + private static final int ARRIVAL_VERTICAL_SEARCH = 24; private AntTeleportHelper() { } @@ -256,11 +259,12 @@ public final class AntTeleportHelper { return safeArrivalPos; } - return createEmergencyArrivalPlatform(destination, preferredPos); + return createEmergencyArrivalPlatform(destination, findEmergencyPlatformPosition(destination, preferredPos)); } @Nullable private static Vec3 findSafeArrivalPosition(ServerPlayer player, ServerLevel destination, BlockPos preferredPos) { + loadArrivalSearchArea(destination, preferredPos); int[] yRange = getDimensionYRange(destination); if (yRange != null) { return findSafeArrivalPositionInYRange(player, destination, preferredPos, yRange[0], yRange[1]); @@ -277,7 +281,7 @@ public final class AntTeleportHelper { addArrivalCandidate(candidates, PlayerRespawnLogic.getSpawnPosInChunk(destination, new ChunkPos(adjustedPreferredPos))); } - for (int radius = 0; radius <= 4; radius++) { + for (int radius = 0; radius <= ARRIVAL_SEARCH_RADIUS; radius++) { for (int xOffset = -radius; xOffset <= radius; xOffset++) { for (int zOffset = -radius; zOffset <= radius; zOffset++) { if (radius > 0 && Math.abs(xOffset) != radius && Math.abs(zOffset) != radius) { @@ -303,9 +307,27 @@ public final class AntTeleportHelper { return null; } + /** + * Arrival validation needs neighboring chunks for collision checks. Load a + * bounded area up front so unloaded terrain is not mistaken for an unsafe + * destination and sent straight to the emergency-platform path. + */ + private static void loadArrivalSearchArea(ServerLevel destination, BlockPos preferredPos) { + ChunkPos center = new ChunkPos(preferredPos); + int chunkRadius = (ARRIVAL_SEARCH_RADIUS >> 4) + 1; + for (int chunkX = center.x - chunkRadius; chunkX <= center.x + chunkRadius; chunkX++) { + for (int chunkZ = center.z - chunkRadius; chunkZ <= center.z + chunkRadius; chunkZ++) { + destination.getChunk(chunkX, chunkZ); + } + } + } + @Nullable private static int[] getDimensionYRange(ServerLevel destination) { ResourceKey dim = destination.dimension(); + if (dim == PermanentPortalType.ELYTHIA.primaryDimension()) { + return new int[]{64, 240}; + } if (dim == AntarchySettings.termiteDestinationDimension()) { return new int[]{100, 200}; } @@ -320,7 +342,48 @@ public final class AntTeleportHelper { @Nullable private static Vec3 findSafeArrivalPositionInYRange(ServerPlayer player, ServerLevel destination, BlockPos preferredPos, int minY, int maxY) { - for (int radius = 0; radius <= 8; radius++) { + Set surfaceCandidates = new LinkedHashSet<>(); + for (int radius = 0; radius <= ARRIVAL_SEARCH_RADIUS; radius++) { + for (int xOff = -radius; xOff <= radius; xOff++) { + for (int zOff = -radius; zOff <= radius; zOff++) { + if (radius > 0 && Math.abs(xOff) != radius && Math.abs(zOff) != radius) { + continue; + } + int x = preferredPos.getX() + xOff; + int z = preferredPos.getZ() + zOff; + BlockPos column = new BlockPos(x, preferredPos.getY(), z); + if (!destination.hasChunkAt(column)) { + continue; + } + + BlockPos surface = destination.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, column); + addArrivalCandidate(surfaceCandidates, surface); + addArrivalCandidate(surfaceCandidates, destination.getHeightmapPos(Heightmap.Types.WORLD_SURFACE, column)); + + for (int yOffset = 1; yOffset <= ARRIVAL_VERTICAL_SEARCH; yOffset++) { + surfaceCandidates.add(surface.above(yOffset)); + surfaceCandidates.add(surface.below(yOffset)); + } + } + } + } + + // Prefer actual generated surfaces. This is especially important in + // Elythia, where the configured spawn height is not necessarily the + // playable surface. + for (BlockPos candidate : surfaceCandidates) { + if (candidate.getY() < minY || candidate.getY() > maxY) { + continue; + } + Vec3 safePos = tryFindSafeDismount(player, destination, candidate); + if (safePos != null && safePos.y >= minY && safePos.y <= maxY) { + return safePos; + } + } + + // Heightmaps can be stale or unsuitable around structures, so retain a + // bounded exhaustive search as a final generated-terrain attempt. + for (int radius = 0; radius <= ARRIVAL_SEARCH_RADIUS; radius++) { for (int xOff = -radius; xOff <= radius; xOff++) { for (int zOff = -radius; zOff <= radius; zOff++) { if (radius > 0 && Math.abs(xOff) != radius && Math.abs(zOff) != radius) { @@ -412,6 +475,49 @@ public final class AntTeleportHelper { return new BlockPos(preferredPos.getX(), Mth.clamp(preferredPos.getY(), minY, maxY), preferredPos.getZ()); } + /** + * Keep the emergency platform grounded in generated terrain when possible. + * A configured/shared spawn can be an arbitrary coordinate in a custom + * dimension, so using its Y directly can strand the player in the sky. + */ + private static BlockPos findEmergencyPlatformPosition(ServerLevel destination, BlockPos preferredPos) { + BlockPos best = null; + int bestDistance = Integer.MAX_VALUE; + int minY = destination.getMinBuildHeight() + 1; + int maxY = destination.getMaxBuildHeight() - 2; + + for (int radius = 0; radius <= 8; radius++) { + for (int xOffset = -radius; xOffset <= radius; xOffset++) { + for (int zOffset = -radius; zOffset <= radius; zOffset++) { + if (radius > 0 && Math.abs(xOffset) != radius && Math.abs(zOffset) != radius) { + continue; + } + + BlockPos searchPos = preferredPos.offset(xOffset, 0, zOffset); + if (!destination.hasChunkAt(searchPos)) { + continue; + } + + BlockPos surface = destination.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, searchPos); + if (surface.getY() < minY || surface.getY() > maxY) { + continue; + } + + int distance = Math.abs(xOffset) + Math.abs(zOffset); + if (distance < bestDistance) { + best = surface; + bestDistance = distance; + } + } + } + if (best != null) { + break; + } + } + + return best != null ? best : getClampedFallbackPosition(destination, preferredPos); + } + /** * Last-resort landing location for custom dimensions whose generated spawn * area has no valid player-sized space. The normal search always runs first; @@ -424,7 +530,7 @@ public final class AntTeleportHelper { center = new BlockPos(center.getX(), Mth.clamp(center.getY(), minY, maxY), center.getZ()); destination.getChunkAt(center); - BlockState platformState = Blocks.STONE.defaultBlockState(); + BlockState platformState = emergencyPlatformState(destination); for (int xOffset = -2; xOffset <= 2; xOffset++) { for (int zOffset = -2; zOffset <= 2; zOffset++) { @@ -438,4 +544,23 @@ public final class AntTeleportHelper { return Vec3.atBottomCenterOf(center); } + + private static BlockState emergencyPlatformState(ServerLevel destination) { + if (destination.dimension() == PermanentPortalType.ELYTHIA.primaryDimension()) { + return PermanentPortalType.ELYTHIA.platformBlock().defaultBlockState(); + } + if (destination.dimension() == PermanentPortalType.THORAXIS.primaryDimension()) { + return PermanentPortalType.THORAXIS.platformBlock().defaultBlockState(); + } + if (destination.dimension() == PermanentPortalType.CAVARYN.primaryDimension()) { + return PermanentPortalType.CAVARYN.platformBlock().defaultBlockState(); + } + if (destination.dimension() == Level.NETHER) { + return Blocks.NETHERRACK.defaultBlockState(); + } + if (destination.dimension() == Level.END) { + return Blocks.END_STONE.defaultBlockState(); + } + return Blocks.DIRT.defaultBlockState(); + } } diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/KingEntity.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/KingEntity.java index 8ee3ae4e..16c7f2ef 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/KingEntity.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/KingEntity.java @@ -34,6 +34,7 @@ import net.minecraft.world.entity.projectile.AbstractArrow; import net.minecraft.world.entity.projectile.Arrow; import net.minecraft.commands.arguments.EntityAnchorArgument; import net.minecraft.world.entity.projectile.Projectile; +import net.minecraft.util.Mth; import net.minecraft.world.inventory.AbstractContainerMenu; import net.minecraft.world.MenuProvider; import net.minecraft.world.item.ArmorItem; @@ -93,6 +94,16 @@ public class KingEntity extends RoyalBossEntity { private static final double COME_NO_CLOSER_RADIUS = 16.0D; private static final int COME_NO_CLOSER_PARTICLE_INTERVAL = 4; private static final int COME_NO_CLOSER_PARTICLE_COUNT = 40; + private static final double CLOSE_QUARTERS_RADIUS = 16.0D; + private static final int CLOSE_QUARTERS_APPROACH_TICKS = 120; + private static final int CLOSE_QUARTERS_EXIT_GRACE_TICKS = 40; + private static final int PROJECTILE_PRESSURE_MAX = 60; + private static final int PROJECTILE_PRESSURE_PER_HIT = 10; + 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 COMBAT_WAYPOINT_MIN_TICKS = 45; + private static final int COMBAT_WAYPOINT_VARIANCE = 35; @Nullable private Vec3 patrolCenter; @@ -105,6 +116,10 @@ public class KingEntity extends RoyalBossEntity { private double treePatrolAngle; private boolean treePatrolBound; private int patrolCooldownTicks; + @Nullable + private Vec3 treePatrolProgressPosition; + private int treePatrolProgressTicks; + private int treePatrolFailures; private int decreeCooldownTicks; private int activeDecreeTicks; private int stompCooldownTicks; @@ -113,6 +128,23 @@ public class KingEntity extends RoyalBossEntity { private int iceballCooldownTicks = 120; private int iceSpikeCooldownTicks = 160; private boolean decreeRetreatPressure; + @Nullable + private String lastDecreeKey; + private boolean closeQuartersEntered; + private int closeQuartersOutsideTicks; + private int projectilePressure; + private int groundAssaultCooldownTicks = 400; + private int groundAssaultTicks; + private boolean kingLandingForCombat; + @Nullable + private Vec3 kingLandingWaypoint; + @Nullable + private Vec3 kingCombatWaypoint; + @Nullable + private Vec3 kingCombatProgressPosition; + private int kingCombatWaypointTicks; + private int kingCombatProgressTicks; + private int kingStrafeDirection = 1; private int royalBoundaryGraceTicks; @Nullable private ServerPlayer exileTarget; @@ -156,12 +188,20 @@ public class KingEntity extends RoyalBossEntity { return phase.maxConcurrentHeadAttacks(); } + @Override + protected boolean managesOwnCombatLocomotion() { + return true; + } + public void setTreePatrolHome(Vec3 center, double minimumY, double maximumY, double angle) { this.treePatrolCenter = center; this.treePatrolMinimumY = minimumY; this.treePatrolMaximumY = maximumY; this.treePatrolAngle = angle; this.treePatrolWaypoint = null; + this.treePatrolProgressPosition = null; + this.treePatrolProgressTicks = 0; + this.treePatrolFailures = 0; this.treePatrolBound = true; } @@ -383,11 +423,22 @@ public class KingEntity extends RoyalBossEntity { ServerLevel serverLevel = (ServerLevel) this.level(); this.iceBuildup.entrySet().removeIf(entry -> serverLevel.getEntity(entry.getKey()) == null); if (this.iceBuildup.size() > 16) this.iceBuildup.clear(); + if (this.tickCount % 10 == 0 && this.projectilePressure > 0) { + this.projectilePressure--; + } + if (this.tickCount % 40 == 0) { + this.bumpBehavior(Behavior.RANGED, -1); + } + if (this.tickCount % 20 == 0 && this.getArrowCount() > 6) { + this.setArrowCount(6); + } 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); + this.stabilizeKingRotation(); return; } @@ -395,6 +446,7 @@ public class KingEntity extends RoyalBossEntity { return; } this.setRoyalFlying(true); + this.resetKingCombatMovement(); this.decreeRetreatPressure = false; if (this.treePatrolBound && this.treePatrolCenter != null) { this.tickTreePatrol(); @@ -416,16 +468,20 @@ public class KingEntity extends RoyalBossEntity { } private void tickComeNoCloserIndicator() { - if (!(this.activeDecree instanceof ComeNoCloserDecree) + boolean keepOut = this.activeDecree instanceof ComeNoCloserDecree; + boolean closeQuarters = this.activeDecree instanceof CloseQuartersDecree; + if ((!keepOut && !closeQuarters) || this.tickCount % COME_NO_CLOSER_PARTICLE_INTERVAL != 0 || !(this.level() instanceof ServerLevel serverLevel)) { return; } + double radius = keepOut ? COME_NO_CLOSER_RADIUS : CLOSE_QUARTERS_RADIUS; for (int index = 0; index < COME_NO_CLOSER_PARTICLE_COUNT; index++) { double angle = (Math.PI * 2.0D * index) / COME_NO_CLOSER_PARTICLE_COUNT; - double x = this.getX() + Math.cos(angle) * COME_NO_CLOSER_RADIUS; - double z = this.getZ() + Math.sin(angle) * COME_NO_CLOSER_RADIUS; - serverLevel.sendParticles(ParticleTypes.END_ROD, x, this.getY() + 0.25D, z, + double x = this.getX() + Math.cos(angle) * radius; + double z = this.getZ() + Math.sin(angle) * radius; + serverLevel.sendParticles(keepOut ? ParticleTypes.END_ROD : ParticleTypes.ENCHANT, + x, this.getY() + 0.25D, z, 1, 0.0D, 0.0D, 0.0D, 0.0D); } } @@ -440,9 +496,25 @@ public class KingEntity extends RoyalBossEntity { this.treePatrolAngle -= Math.PI * 2.0D; } Vec3 waypoint = this.treePatrolWaypoint; + if (waypoint != null && ++this.treePatrolProgressTicks >= 40) { + if (this.treePatrolProgressPosition != null + && this.position().distanceToSqr(this.treePatrolProgressPosition) < 1.0D + && this.position().distanceToSqr(waypoint) > 36.0D) { + this.treePatrolWaypoint = null; + waypoint = null; + this.treePatrolFailures++; + this.treePatrolAngle += 0.35D; + } else { + this.treePatrolFailures = Math.max(0, this.treePatrolFailures - 1); + } + this.treePatrolProgressPosition = this.position(); + this.treePatrolProgressTicks = 0; + } if (waypoint == null || this.position().distanceToSqr(waypoint) < 36.0D) { waypoint = this.findTreePatrolWaypoint(center, this.treePatrolAngle); this.treePatrolWaypoint = waypoint; + this.treePatrolProgressPosition = this.position(); + this.treePatrolProgressTicks = 0; } if (waypoint != null) { this.getMoveControl().setWantedPosition(waypoint.x, waypoint.y, waypoint.z, 1.0D); @@ -503,18 +575,40 @@ public class KingEntity extends RoyalBossEntity { @Nullable private Vec3 findTreePatrolWaypoint(Vec3 center, double angle) { - for (int attempt = 0; attempt < 10; attempt++) { - double candidateAngle = angle + attempt * 0.22D; - double x = center.x + Math.cos(candidateAngle) * TREE_ORBIT_RADIUS; - double z = center.z + Math.sin(candidateAngle) * TREE_ORBIT_RADIUS; - double vertical = 0.5D + 0.5D * Math.sin(candidateAngle); - double y = this.treePatrolMinimumY - + (this.treePatrolMaximumY - this.treePatrolMinimumY) * vertical; - Vec3 candidate = new Vec3(x, y, z); - if (this.clearTreePatrolPath(candidate)) { - return candidate; + double currentAngle = Math.atan2(this.getZ() - center.z, this.getX() - center.x); + double baseAngle = Double.isFinite(currentAngle) ? currentAngle + 0.24D : angle; + double[] radii = {TREE_ORBIT_RADIUS, TREE_ORBIT_RADIUS + 20.0D, TREE_ORBIT_RADIUS - 20.0D}; + double[] heightOffsets = {0.0D, 16.0D, -12.0D, 30.0D}; + for (int angleAttempt = 0; angleAttempt < 8; angleAttempt++) { + double candidateAngle = baseAngle + angleAttempt * 0.16D; + for (double radius : radii) { + double x = center.x + Math.cos(candidateAngle) * radius; + double z = center.z + Math.sin(candidateAngle) * radius; + double vertical = 0.5D + 0.5D * Math.sin(candidateAngle); + double baseY = this.treePatrolMinimumY + + (this.treePatrolMaximumY - this.treePatrolMinimumY) * vertical; + for (double heightOffset : heightOffsets) { + double y = Mth.clamp(baseY + heightOffset, + this.treePatrolMinimumY + 8.0D, this.treePatrolMaximumY - 8.0D); + Vec3 candidate = new Vec3(x, y, z); + if (this.clearTreePatrolPath(candidate)) { + return candidate; + } + } } } + Vec3 outward = this.position().subtract(center).multiply(1.0D, 0.0D, 1.0D); + if (outward.lengthSqr() < 1.0E-4D) { + outward = new Vec3(Math.cos(angle), 0.0D, Math.sin(angle)); + } + Vec3 tangent = new Vec3(-outward.z, 0.0D, outward.x).normalize(); + Vec3 recovery = this.position().add(tangent.scale(24.0D)).add(0.0D, + this.treePatrolFailures >= 2 ? 18.0D : 8.0D, 0.0D); + recovery = new Vec3(recovery.x, + Mth.clamp(recovery.y, this.treePatrolMinimumY + 8.0D, this.treePatrolMaximumY - 8.0D), recovery.z); + if (this.clearTreePatrolPath(recovery)) { + return recovery; + } return null; } @@ -531,6 +625,179 @@ public class KingEntity extends RoyalBossEntity { return true; } + private void tickKingCombatMovement(LivingEntity target) { + boolean closeQuarters = this.activeDecree instanceof CloseQuartersDecree; + if (closeQuarters && this.isRoyalFlying() && !this.kingLandingForCombat) { + this.beginKingLanding(target); + } else if (!closeQuarters && this.isRoyalFlying() && !this.kingLandingForCombat + && this.groundAssaultCooldownTicks-- <= 0 && target.onGround()) { + this.beginKingLanding(target); + } + + if (this.kingLandingForCombat) { + if (!target.onGround() && !closeQuarters) { + this.kingLandingForCombat = false; + this.kingLandingWaypoint = null; + this.groundAssaultCooldownTicks = 160; + return; + } + if (this.kingLandingWaypoint == null) { + this.kingLandingWaypoint = this.findKingLandingPoint(target); + } + Vec3 landing = this.kingLandingWaypoint; + if (landing != null) { + this.setRoyalFlying(true); + this.getMoveControl().setWantedPosition(landing.x, landing.y, landing.z, 1.35D); + if (this.onGround() || this.getY() <= landing.y + 1.25D) { + this.setRoyalFlying(false); + this.kingLandingForCombat = false; + this.kingLandingWaypoint = null; + this.groundAssaultTicks = closeQuarters + ? Math.max(GROUND_ASSAULT_TICKS, this.activeDecreeTicks + 20) + : GROUND_ASSAULT_TICKS; + } + } + return; + } + + if (!this.isRoyalFlying()) { + if (closeQuarters) { + this.groundAssaultTicks = Math.max(this.groundAssaultTicks, this.activeDecreeTicks + 20); + } + 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; + } + return; + } + + if (this.kingCombatWaypoint != null && ++this.kingCombatProgressTicks >= 40) { + if (this.kingCombatProgressPosition != null + && this.position().distanceToSqr(this.kingCombatProgressPosition) < 1.0D + && this.position().distanceToSqr(this.kingCombatWaypoint) > 16.0D) { + this.kingCombatWaypoint = null; + this.kingStrafeDirection *= -1; + } + this.kingCombatProgressPosition = this.position(); + this.kingCombatProgressTicks = 0; + } + if (this.kingCombatWaypoint == null || --this.kingCombatWaypointTicks <= 0 + || this.position().distanceToSqr(this.kingCombatWaypoint) < 16.0D) { + this.selectKingCombatWaypoint(target); + } + if (this.kingCombatWaypoint != null) { + double speed = this.projectilePressure >= 30 ? 1.4D : 1.15D; + this.getMoveControl().setWantedPosition(this.kingCombatWaypoint.x, + this.kingCombatWaypoint.y, this.kingCombatWaypoint.z, speed); + } + } + + private void beginKingLanding(LivingEntity target) { + this.kingLandingWaypoint = this.findKingLandingPoint(target); + if (this.kingLandingWaypoint != null) { + this.kingLandingForCombat = true; + this.kingCombatWaypoint = null; + } else { + this.groundAssaultCooldownTicks = 100; + } + } + + @Nullable + private Vec3 findKingLandingPoint(LivingEntity target) { + Vec3 away = this.position().subtract(target.position()).multiply(1.0D, 0.0D, 1.0D); + double baseAngle = away.lengthSqr() > 1.0E-4D ? Math.atan2(away.z, away.x) : this.random.nextDouble() * Mth.TWO_PI; + double[] radii = {9.0D, 13.0D, 17.0D, 6.0D}; + 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 = 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 selectKingCombatWaypoint(LivingEntity target) { + Vec3 radial = this.position().subtract(target.position()).multiply(1.0D, 0.0D, 1.0D); + if (radial.lengthSqr() < 1.0E-4D) { + radial = new Vec3(1.0D, 0.0D, 0.0D); + } + radial = radial.normalize(); + Vec3 tangent = new Vec3(-radial.z * this.kingStrafeDirection, 0.0D, + radial.x * this.kingStrafeDirection); + double range = this.activeDecree instanceof ComeNoCloserDecree || this.projectilePressure >= 30 + ? 40.0D : 34.0D; + Vec3 horizontal = target.position().add(radial.scale(range)).add(tangent.scale(12.0D)); + double groundY = this.groundYBelow(horizontal.x, horizontal.z); + double y = Mth.clamp(target.getY() + 8.0D, groundY + FLYING_MIN_HOVER, + groundY + FLYING_MAX_HOVER_ABOVE_GROUND); + this.kingCombatWaypoint = new Vec3(horizontal.x, y, horizontal.z); + this.kingCombatWaypointTicks = COMBAT_WAYPOINT_MIN_TICKS + + this.random.nextInt(COMBAT_WAYPOINT_VARIANCE + 1); + this.kingCombatProgressPosition = this.position(); + this.kingCombatProgressTicks = 0; + if (this.random.nextInt(4) == 0) { + this.kingStrafeDirection *= -1; + } + } + + private void stabilizeKingRotation() { + Vec3 movement = this.getDeltaMovement().multiply(1.0D, 0.0D, 1.0D); + if (movement.lengthSqr() < 0.0025D) { + return; + } + float desiredYaw = (float) (Mth.atan2(movement.z, movement.x) * 180.0D / Math.PI) - 90.0F; + float turn = Mth.clamp(Mth.wrapDegrees(desiredYaw - this.getYRot()), -7.0F, 7.0F); + this.setYRot(this.getYRot() + turn); + this.yBodyRot = this.getYRot(); + } + + private void resetKingCombatMovement() { + this.kingLandingForCombat = false; + 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) { + this.beginKingLanding(target); + } + } + + public RoyalDecree.Evaluation evaluateCloseQuarters(LivingEntity target) { + double distanceSquared = target.distanceToSqr(this); + if (distanceSquared <= CLOSE_QUARTERS_RADIUS * CLOSE_QUARTERS_RADIUS) { + this.closeQuartersEntered = true; + this.closeQuartersOutsideTicks = 0; + return RoyalDecree.Evaluation.COMPLIANT; + } + if (!this.closeQuartersEntered) { + return 300 - this.activeDecreeTicks >= CLOSE_QUARTERS_APPROACH_TICKS + ? RoyalDecree.Evaluation.VIOLATED : RoyalDecree.Evaluation.COMPLIANT; + } + return ++this.closeQuartersOutsideTicks > CLOSE_QUARTERS_EXIT_GRACE_TICKS + ? RoyalDecree.Evaluation.VIOLATED : RoyalDecree.Evaluation.COMPLIANT; + } + private void tickRoyalPunishment() { if (this.exileTarget == null && this.titheTarget == null && this.targetShotPlayer == null && this.sealedTarget == null) { return; @@ -742,6 +1009,8 @@ public class KingEntity extends RoyalBossEntity { this.activeDecree = decree; this.activeDecreeTicks = 300; this.decreeCooldownTicks = 0; + this.closeQuartersEntered = false; + this.closeQuartersOutsideTicks = 0; this.projectRoyalSound(AntarchySoundEvents.KING_DECREE.get(), 4.0F, 1.0F, target); this.sendDecreeTitle(target); return true; @@ -1093,6 +1362,8 @@ public class KingEntity extends RoyalBossEntity { this.activeDecreeTicks = 300; this.activeDecree = this.pickDecree(target); this.decreeRetreatPressure = false; + this.closeQuartersEntered = false; + this.closeQuartersOutsideTicks = 0; 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); @@ -1115,10 +1386,19 @@ public class KingEntity extends RoyalBossEntity { } private RoyalDecree pickDecree(LivingEntity target) { + if ((this.projectilePressure >= 20 || this.behaviorScore(Behavior.RANGED) >= 6) + && !"decree.antarchy.close_quarters".equals(this.lastDecreeKey)) { + return DECREES.stream() + .filter(CloseQuartersDecree.class::isInstance) + .findFirst() + .orElse(DECREES.get(0)); + } int totalWeight = 0; int[] weights = new int[DECREES.size()]; for (int i = 0; i < DECREES.size(); i++) { - weights[i] = Math.max(1, DECREES.get(i).contextWeight(this, target)); + RoyalDecree decree = DECREES.get(i); + weights[i] = decree.translationKey().equals(this.lastDecreeKey) + ? 0 : Math.max(1, decree.contextWeight(this, target)); totalWeight += weights[i]; } int roll = this.random.nextInt(totalWeight); @@ -1145,11 +1425,14 @@ public class KingEntity extends RoyalBossEntity { player.connection.send(new ClientboundClearTitlesPacket(false)); } if (this.activeDecree != null) { + this.lastDecreeKey = this.activeDecree.translationKey(); this.activeDecree.onEnded(); } this.activeDecree = null; this.activeDecreeTicks = 0; this.decreeRetreatPressure = false; + this.closeQuartersEntered = false; + this.closeQuartersOutsideTicks = 0; this.decreeCooldownTicks = AntarchySettings.royalDecreeCooldownTicks(); this.startRoyalRecovery(40); } @@ -1166,13 +1449,63 @@ public class KingEntity extends RoyalBossEntity { if (this.activeDecree == null || target == null || !target.isAlive()) { return; } - if (target instanceof ServerPlayer player) { - this.projectRoyalSound(AntarchySoundEvents.KING_JUDGEMENT.get(), 4.0F, 1.0F, target); - this.debugTriggerPunishment(RoyalPunishmentType.ROYAL_EXILE, player); - } else { - this.invokeJudgment(target); - } + this.attackScheduler.cancel(RoyalAttackLane.DECREE); + this.attackScheduler.cancel(RoyalAttackLane.HAZARD); this.endDecree(target); + this.projectRoyalSound(AntarchySoundEvents.KING_JUDGEMENT.get(), 4.0F, 1.0F, target); + if (target instanceof ServerPlayer player) { + this.sendDecreeViolationTitle(player); + } + this.startRoyalJudgmentBarrage(target); + } + + private void sendDecreeViolationTitle(ServerPlayer player) { + player.connection.send(new ClientboundSetTitlesAnimationPacket(5, 45, 10)); + player.connection.send(new ClientboundSetTitleTextPacket(Component.translatable("decree.antarchy.violated") + .withStyle(style -> style.withColor(ChatFormatting.DARK_PURPLE).withBold(true)))); + player.connection.send(new ClientboundSetSubtitleTextPacket(Component.translatable("decree.antarchy.violated.subtitle") + .withStyle(style -> style.withColor(ChatFormatting.LIGHT_PURPLE)))); + } + + private void startRoyalJudgmentBarrage(LivingEntity target) { + if (!(this.level() instanceof ServerLevel level)) { + return; + } + this.beginRoyalAttack("royal_judgment", RoyalAttackLane.HAZARD, 80, + 20, 9, 12, new com.craisinlord.antarchy.content.entity.royal.attack.RoyalAttackScheduler.Action() { + @Override + public void onStart() { + for (RoyalHead.Slot slot : RoyalHead.Slot.values()) { + KingEntity.this.triggerAnim(slot.controllerName(), "shoot"); + } + level.sendParticles(ParticleTypes.WITCH, + KingEntity.this.getX(), KingEntity.this.getY() + KingEntity.this.getBbHeight() * 0.7D, + KingEntity.this.getZ(), 70, 4.0D, 3.0D, 4.0D, 0.12D); + } + + @Override + public void onActive(int elapsedTicks) { + if (elapsedTicks % 4 == 0 && target.isAlive() && target.level() == level) { + KingEntity.this.fireRoyalJudgmentWave(level, target); + } + } + }); + } + + private void fireRoyalJudgmentWave(ServerLevel level, LivingEntity target) { + Vec3 predicted = target.getEyePosition().add(target.getDeltaMovement().scale(4.0D)); + for (RoyalHead.Slot slot : RoyalHead.Slot.values()) { + RoyalHead head = this.royalHead(slot); + Vec3 origin = this.headAnchor(head); + Vec3 direction = predicted.subtract(origin).normalize(); + RoyalBoltEntity bolt = new RoyalBoltEntity(AntarchyObjects.ROYAL_BOLT.get(), level); + bolt.setOwner(this); + bolt.setElement(RoyalBeamElement.QUEEN_PURPLE); + bolt.setPos(origin.x, origin.y, origin.z); + bolt.shoot(direction.x, direction.y, direction.z, 1.55F, 1.0F); + level.addFreshEntity(bolt); + } + this.playRoyalSound(AntarchySoundEvents.KING_BEAM_SHOOT.get(), 1.15F); } public boolean blocksHealing() { @@ -1200,6 +1533,17 @@ public class KingEntity extends RoyalBossEntity { this.lastPlayerDamageTime.put(attacker.getUUID(), this.level().getGameTime()); boolean projectile = source.getDirectEntity() instanceof Projectile; this.bumpBehavior(projectile ? Behavior.RANGED : Behavior.HUGGING, 2); + if (projectile) { + this.projectilePressure = Math.min(PROJECTILE_PRESSURE_MAX, + this.projectilePressure + PROJECTILE_PRESSURE_PER_HIT); + if (this.isRoyalFlying() && !(this.activeDecree instanceof CloseQuartersDecree)) { + if (this.projectilePressure >= 40) { + amount *= 0.55F; + } else if (this.projectilePressure >= 20) { + amount *= 0.75F; + } + } + } if (this.activeDecree instanceof CloseQuartersDecree && projectile) { this.failActiveDecree(attacker); } else if (this.activeDecree instanceof ComeNoCloserDecree && !projectile) { diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/RoyalBossEntity.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/RoyalBossEntity.java index c6f46be2..ba279b3e 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/RoyalBossEntity.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/RoyalBossEntity.java @@ -284,6 +284,11 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult return 0.35D; } + /** Lets a boss provide a bespoke air/ground cadence without changing the other royal encounter. */ + protected boolean managesOwnCombatLocomotion() { + return false; + } + protected boolean shouldClearObstruction() { return true; } @@ -799,7 +804,7 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult } private void tickCombatLocomotionMode(LivingEntity target) { - if (!this.isFlyingBoss()) { + if (!this.isFlyingBoss() || this.managesOwnCombatLocomotion()) { return; } boolean inverted = AntarchyGravityApi.isGravityInverted(this); diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/decree/CloseQuartersDecree.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/decree/CloseQuartersDecree.java index 7c0f6d19..5474f2a8 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/decree/CloseQuartersDecree.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/decree/CloseQuartersDecree.java @@ -4,7 +4,13 @@ import net.minecraft.server.level.ServerLevel; import net.minecraft.world.entity.LivingEntity; public final class CloseQuartersDecree implements RoyalDecree { public String translationKey() { return "decree.antarchy.close_quarters"; } - public void apply(ServerLevel level, KingEntity king, LivingEntity target) { } + public void apply(ServerLevel level, KingEntity king, LivingEntity target) { + king.requestCloseQuartersAssault(target); + } + @Override + public Evaluation evaluate(ServerLevel level, KingEntity king, LivingEntity target) { + return king.evaluateCloseQuarters(target); + } @Override public int contextWeight(KingEntity king, LivingEntity target) { return 4 + king.behaviorScore(KingEntity.Behavior.RANGED); diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/item/AttitudeAdjusterSlamManager.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/item/AttitudeAdjusterSlamManager.java index 2fab071b..9cb53cf1 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/item/AttitudeAdjusterSlamManager.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/item/AttitudeAdjusterSlamManager.java @@ -23,6 +23,7 @@ import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.projectile.Projectile; +import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.gameevent.GameEvent; @@ -104,7 +105,7 @@ public final class AttitudeAdjusterSlamManager { if (player.onGround() || player.isInWaterOrBubble() || player.isInLava() || player.isFallFlying() || player.getAbilities().flying || PLAYER_SLAMS.containsKey(player.getUUID())) { return false; } - PLAYER_SLAMS.put(player.getUUID(), new PlayerSlamState(player.getUUID(), player.serverLevel().dimension().location().toString(), player.serverLevel().getGameTime() + SLAM_TIMEOUT_TICKS, player.fallDistance)); + PLAYER_SLAMS.put(player.getUUID(), new PlayerSlamState(player.getUUID(), hand, player.serverLevel().dimension().location().toString(), player.serverLevel().getGameTime() + SLAM_TIMEOUT_TICKS, player.fallDistance)); AntarchyGravityDirection gravityDirection = AntarchyGravityRotationUtil.getGravityDownDirection(player) == Direction.UP ? AntarchyGravityDirection.UP : AntarchyGravityDirection.DOWN; Vec3 motion = player.getDeltaMovement(); Vec3 localMotion = AntarchyGravityRotationUtil.vecWorldToPlayer(motion, gravityDirection); @@ -194,7 +195,8 @@ public final class AttitudeAdjusterSlamManager { if (AntarchySettings.attitudeAdjusterBreaksBlocks()) { breakBlocks(level, player, groundPos, gravityDown); } - if (player.getMainHandItem().getItem() instanceof AttitudeAdjusterItem item) { + ItemStack slamStack = player.getItemInHand(state.hand); + if (slamStack.getItem() instanceof AttitudeAdjusterItem item) { player.getCooldowns().addCooldown(item, PLAYER_SLAM_COOLDOWN_TICKS); } iterator.remove(); @@ -319,12 +321,14 @@ public final class AttitudeAdjusterSlamManager { private static final class PlayerSlamState { private final UUID playerId; + private final net.minecraft.world.InteractionHand hand; private final String dimensionId; private final long expiresAt; private final float startFallDistance; - private PlayerSlamState(UUID playerId, String dimensionId, long expiresAt, float startFallDistance) { + private PlayerSlamState(UUID playerId, net.minecraft.world.InteractionHand hand, String dimensionId, long expiresAt, float startFallDistance) { this.playerId = playerId; + this.hand = hand; this.dimensionId = dimensionId; this.expiresAt = expiresAt; this.startFallDistance = startFallDistance; diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/item/RpoLauncherItem.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/item/RpoLauncherItem.java index 634a928e..2b3b5cec 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/item/RpoLauncherItem.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/item/RpoLauncherItem.java @@ -6,6 +6,9 @@ import com.craisinlord.antarchy.content.AntarchySoundEvents; import com.craisinlord.antarchy.content.client.model.ResourceBackedGeoItemModel; import com.craisinlord.antarchy.content.client.renderer.AnimatedHeldItemRenderer; import com.craisinlord.antarchy.content.entity.OctopusBombEntity; +import com.craisinlord.antarchy.content.gravity.AntarchyGravityApi; +import com.craisinlord.antarchy.content.gravity.AntarchyGravityDirection; +import com.craisinlord.antarchy.content.gravity.AntarchyGravityRotationUtil; import java.util.function.Consumer; import java.util.function.Predicate; import net.minecraft.core.component.DataComponents; @@ -161,15 +164,12 @@ public class RpoLauncherItem extends ProjectileWeaponItem implements GeoItem { } float yaw = player.getYRot() + yawOffset; - Vec3 direction = Vec3.directionFromRotation(new Vec2(player.getXRot(), yaw)); - projectileBomb.moveTo( - player.getX(), - player.getEyeY() + 0.15D, - player.getZ(), - yaw, - player.getXRot() - ); - projectileBomb.launchAsExplosiveProjectile(player, direction.scale(AntarchySettings.rpoLauncherLaunchVelocity()).add(player.getDeltaMovement())); + Vec3 localDirection = Vec3.directionFromRotation(new Vec2(player.getXRot(), yaw)); + AntarchyGravityDirection gravity = AntarchyGravityApi.getGravityDirection(player); + Vec3 direction = AntarchyGravityRotationUtil.vecPlayerToWorld(localDirection, gravity).normalize(); + Vec3 spawn = player.getEyePosition().add(direction.scale(0.55D)); + projectileBomb.moveTo(spawn.x, spawn.y, spawn.z, yaw, player.getXRot()); + projectileBomb.launchAsExplosiveProjectile(player, direction.scale(AntarchySettings.rpoLauncherLaunchVelocity()).add(AntarchyGravityApi.deltaMovement(player))); level.addFreshEntity(projectileBomb); } diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/item/SquidzookaItem.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/item/SquidzookaItem.java index e907bdb1..6950d148 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/item/SquidzookaItem.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/item/SquidzookaItem.java @@ -5,6 +5,9 @@ import com.craisinlord.antarchy.content.AntarchyObjects; import com.craisinlord.antarchy.content.AntarchySoundEvents; import com.craisinlord.antarchy.content.client.model.ResourceBackedGeoItemModel; import com.craisinlord.antarchy.content.entity.MissileSquidEntity; +import com.craisinlord.antarchy.content.gravity.AntarchyGravityApi; +import com.craisinlord.antarchy.content.gravity.AntarchyGravityDirection; +import com.craisinlord.antarchy.content.gravity.AntarchyGravityRotationUtil; import java.util.function.Consumer; import java.util.function.Predicate; import net.minecraft.core.component.DataComponents; @@ -162,15 +165,12 @@ public class SquidzookaItem extends ProjectileWeaponItem implements GeoItem { } float yaw = player.getYRot() + yawOffset; - Vec3 direction = Vec3.directionFromRotation(new Vec2(player.getXRot(), yaw)); - projectileSquid.moveTo( - player.getX(), - player.getEyeY() - 0.2D, - player.getZ(), - yaw, - player.getXRot() - ); - projectileSquid.launchAsProjectile(player, direction.scale(AntarchySettings.squidzookaLaunchVelocity()).add(player.getDeltaMovement())); + Vec3 localDirection = Vec3.directionFromRotation(new Vec2(player.getXRot(), yaw)); + AntarchyGravityDirection gravity = AntarchyGravityApi.getGravityDirection(player); + Vec3 direction = AntarchyGravityRotationUtil.vecPlayerToWorld(localDirection, gravity).normalize(); + Vec3 spawn = player.getEyePosition().add(direction.scale(0.45D)); + projectileSquid.moveTo(spawn.x, spawn.y, spawn.z, yaw, player.getXRot()); + projectileSquid.launchAsProjectile(player, direction.scale(AntarchySettings.squidzookaLaunchVelocity()).add(AntarchyGravityApi.deltaMovement(player))); level.addFreshEntity(projectileSquid); } diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/worldgen/elythia/KingsTreeGridFeature.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/worldgen/elythia/KingsTreeGridFeature.java index 2015399e..5d4c7dbd 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/worldgen/elythia/KingsTreeGridFeature.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/worldgen/elythia/KingsTreeGridFeature.java @@ -147,14 +147,42 @@ public final class KingsTreeGridFeature extends Feature ANTIMETAL_PLACE = registerSoundEvent("antimetal_place"); public static final DeferredHolder ANTIMETAL_STEP = registerSoundEvent("antimetal_step"); public static final DeferredHolder ANTIMETAL_SCAFFOLD = registerSoundEvent("antimetal_scaffold"); + public static final DeferredHolder UNDERTRIAL_DETECT_PLAYER1 = registerSoundEvent("undertrial_detect_player1"); + public static final DeferredHolder UNDERTRIAL_DETECT_PLAYER2 = registerSoundEvent("undertrial_detect_player2"); public static final DeferredHolder KING_IDLE = registerSoundEvent("king_idle"); public static final DeferredHolder KING_WALK = registerSoundEvent("king_walk"); diff --git a/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/AntarchyMobsConfig.java b/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/AntarchyMobsConfig.java index ce216847..ca44e19c 100644 --- a/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/AntarchyMobsConfig.java +++ b/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/AntarchyMobsConfig.java @@ -676,7 +676,7 @@ public final class AntarchyMobsConfig { KING_BEAM_TRACKING = b.defineInRange("kingBeamTracking", 0.1D, 0.0D, 1.0D); KING_BEAM_TERRAIN_RADIUS = b.defineInRange("kingBeamTerrainRadius", 5.0D, 0.0D, 32.0D); KING_BEAM_TERRAIN_CAP = b.defineInRange("kingBeamTerrainCap", 192, 0, 4096); - ROYAL_DECREE_COOLDOWN_TICKS = b.comment("Cooldown in ticks between the end of one King decree and the next.").defineInRange("royalDecreeCooldownTicks", 1200, 1, 72000); + ROYAL_DECREE_COOLDOWN_TICKS = b.comment("Cooldown in ticks between the end of one King decree and the next.").defineInRange("royalDecreeCooldownTicks", 700, 1, 72000); KING_ROYAL_MUSTER_COOLDOWN_TICKS = b.comment("Cooldown in ticks between King's command pulses.").defineInRange("kingRoyalMusterCooldownTicks", 500, 1, 72000); KING_ROYAL_MUSTER_WINDUP_TICKS = b.comment("Warning time before the King commands nearby hostile mobs.").defineInRange("kingRoyalMusterWindupTicks", 30, 0, 1200); KING_ROYAL_MUSTER_DURATION_TICKS = b.comment("Duration of Commanded applied by the King's muster.").defineInRange("kingRoyalMusterDurationTicks", 600, 1, 72000); diff --git a/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/registry/AntarchyNeoforgeSounds.java b/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/registry/AntarchyNeoforgeSounds.java index 9dadc728..8e265d8d 100644 --- a/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/registry/AntarchyNeoforgeSounds.java +++ b/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/registry/AntarchyNeoforgeSounds.java @@ -213,6 +213,8 @@ public final class AntarchyNeoforgeSounds { public static final DeferredHolder ANTIMETAL_PLACE = registerSoundEvent("antimetal_place"); public static final DeferredHolder ANTIMETAL_STEP = registerSoundEvent("antimetal_step"); public static final DeferredHolder ANTIMETAL_SCAFFOLD = registerSoundEvent("antimetal_scaffold"); + public static final DeferredHolder UNDERTRIAL_DETECT_PLAYER1 = registerSoundEvent("undertrial_detect_player1"); + public static final DeferredHolder UNDERTRIAL_DETECT_PLAYER2 = registerSoundEvent("undertrial_detect_player2"); public static final DeferredHolder KING_IDLE = registerSoundEvent("king_idle"); public static final DeferredHolder KING_WALK = registerSoundEvent("king_walk");