fix king and queen attack anim bug

stabalize RainbowAntEntity dims
patch config inconsitency
fixed undervault
performance improvements
This commit is contained in:
craisinlord committed 2026-09-12 23:58:24 -05:00
1 parent 377f3785af
commit 37e941915d
47 files changed
+784 -231

No files matched your search

@@ -22,8 +22,8 @@ public interface InfinityCompatBridge {
return getRandomDimensionId(random); return getRandomDimensionId(random);
} }
default boolean requestWarp(ServerPlayer player, ResourceLocation dimensionId) { default InfinityWarpResult requestWarp(ServerPlayer player, ResourceLocation dimensionId) {
return false; return InfinityWarpResult.REJECTED;
} }
/** /**
@@ -0,0 +1,9 @@
package com.craisinlord.antarchy.compat.infinity;
public enum InfinityWarpResult {
REJECTED,
PENDING,
READY,
FAILED_GENERATION,
TIMEOUT
}
@@ -5,6 +5,9 @@ import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction; import net.minecraft.core.Direction;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.item.context.BlockPlaceContext; import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.ItemInteractionResult;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level; import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Mirror; import net.minecraft.world.level.block.Mirror;
import net.minecraft.world.level.block.Rotation; import net.minecraft.world.level.block.Rotation;
@@ -16,6 +19,7 @@ import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.StateDefinition; import net.minecraft.world.level.block.state.StateDefinition;
import net.minecraft.world.level.block.state.properties.DirectionProperty; import net.minecraft.world.level.block.state.properties.DirectionProperty;
import net.minecraft.world.phys.BlockHitResult;
import java.util.function.Supplier; import java.util.function.Supplier;
@@ -60,6 +64,25 @@ public final class UnderVaultBlock extends VaultBlock {
return new UnderVaultBlockEntity(pos, state, blockEntityType); return new UnderVaultBlockEntity(pos, state, blockEntityType);
} }
@Override
public ItemInteractionResult useItemOn(ItemStack stack, BlockState state, Level level, BlockPos pos,
net.minecraft.world.entity.player.Player player, InteractionHand hand,
BlockHitResult hitResult) {
if (stack.isEmpty() || state.getValue(STATE) != net.minecraft.world.level.block.entity.vault.VaultState.ACTIVE) {
return ItemInteractionResult.PASS_TO_DEFAULT_BLOCK_INTERACTION;
}
if (level instanceof ServerLevel serverLevel
&& level.getBlockEntity(pos) instanceof UnderVaultBlockEntity underVault) {
net.minecraft.world.level.block.entity.vault.VaultBlockEntity.Server.tryInsertKey(
serverLevel, pos, state, underVault.getVaultData().getConfig(),
underVault.getVaultData().getServerData(), underVault.getVaultData().getSharedData(),
player, stack);
return ItemInteractionResult.SUCCESS;
}
return level.isClientSide ? ItemInteractionResult.CONSUME
: ItemInteractionResult.PASS_TO_DEFAULT_BLOCK_INTERACTION;
}
@Override @Override
public <T extends BlockEntity> BlockEntityTicker<T> getTicker(Level level, BlockState state, BlockEntityType<T> type) { public <T extends BlockEntity> BlockEntityTicker<T> getTicker(Level level, BlockState state, BlockEntityType<T> type) {
if (this.blockEntityType == null || type != this.blockEntityType.get()) { if (this.blockEntityType == null || type != this.blockEntityType.get()) {
@@ -163,4 +163,9 @@ public final class ClientTimeDilationTicker {
public static void applyFields(List<TimeDilationFieldSnapshot> fields) { public static void applyFields(List<TimeDilationFieldSnapshot> fields) {
activeFields = List.copyOf(fields); activeFields = List.copyOf(fields);
} }
public static boolean hasActiveDilation() {
return !activeFields.isEmpty()
|| com.craisinlord.antarchy.content.time.TimeDilationApi.hasSyncedClientRates();
}
} }
@@ -33,19 +33,18 @@ public final class ContractionAfterimages {
iterator.remove(); iterator.remove();
} }
} }
if (holder == null) { }
public static void observe(LivingEntity living) {
Holder<MobEffect> holder = RoyalEffectHooks.contractedHolder();
if (holder == null || living.isInvisible() || !living.hasEffect(holder)) {
return; return;
} }
int now = (int) (living.level().getGameTime() & 0x7fffffffL);
for (Entity entity : level.entitiesForRendering()) { Deque<Sample> samples = HISTORY.computeIfAbsent(living, ignored -> new ArrayDeque<>());
if (!(entity instanceof LivingEntity living) || living.isInvisible() || !living.hasEffect(holder)) { samples.addFirst(new Sample(living, now));
continue; while (samples.size() > MAX_SAMPLES) {
} samples.removeLast();
Deque<Sample> samples = HISTORY.computeIfAbsent(living, ignored -> new ArrayDeque<>());
samples.addFirst(new Sample(living, now));
while (samples.size() > MAX_SAMPLES) {
samples.removeLast();
}
} }
} }
@@ -4,7 +4,6 @@ import com.craisinlord.antarchy.Antarchy;
import com.craisinlord.antarchy.content.entity.royal.RoyalBossEntity; import com.craisinlord.antarchy.content.entity.royal.RoyalBossEntity;
import com.craisinlord.antarchy.content.entity.royal.RoyalHead; import com.craisinlord.antarchy.content.entity.royal.RoyalHead;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.Mth;
import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import software.bernie.geckolib.animation.AnimationState; import software.bernie.geckolib.animation.AnimationState;
@@ -15,11 +14,6 @@ public class RoyalBossModel extends GeoModel<RoyalBossEntity> {
private static final String[] BEAM_ANCHORS = { private static final String[] BEAM_ANCHORS = {
"beam_anchor_left", "beam_anchor_center", "beam_anchor_right" "beam_anchor_left", "beam_anchor_center", "beam_anchor_right"
}; };
private static final String[][] AIM_CHAINS = {
{"L_neck", "L_neck2", "L_neck3"},
{"neck", "neck3", "neck4"},
{"R_neck", "R_neck2", "R_neck3"}
};
@Override @Override
public ResourceLocation getModelResource(RoyalBossEntity animatable) { public ResourceLocation getModelResource(RoyalBossEntity animatable) {
@@ -45,33 +39,6 @@ public class RoyalBossModel extends GeoModel<RoyalBossEntity> {
this.getBone(anchor).ifPresent(bone -> bone.setTrackingMatrices(true)); this.getBone(anchor).ifPresent(bone -> bone.setTrackingMatrices(true));
} }
float partialTick = animationState.getPartialTick();
for (RoyalHead.Slot slot : RoyalHead.Slot.values()) {
Vec3 beamEnd = animatable.getRoyalBeamEndPosition(slot);
if (!animatable.isFiringRoyalBeam(slot) || beamEnd == null) continue;
Vec3 origin = animatable.getRoyalBeamShootFrom(slot, partialTick);
Vec3 direction = com.craisinlord.antarchy.content.gravity.AntarchyGravityRotationUtil.vecWorldToPlayer(
beamEnd.subtract(origin),
com.craisinlord.antarchy.content.gravity.AntarchyGravityApi.getGravityDirection(animatable));
double horizontal = Math.sqrt(direction.x * direction.x + direction.z * direction.z);
if (direction.lengthSqr() < 1.0E-6D || horizontal < 1.0E-4D) continue;
float desiredYaw = (float) (Mth.atan2(-direction.x, direction.z) * Mth.RAD_TO_DEG);
float bodyYaw = Mth.rotLerp(partialTick, animatable.yRotO, animatable.getYRot());
float relativeYaw = Mth.clamp(Mth.wrapDegrees(desiredYaw - bodyYaw), -55.0F, 55.0F) * Mth.DEG_TO_RAD;
float pitch = Mth.clamp((float) -Mth.atan2(direction.y, horizontal) * Mth.RAD_TO_DEG,
-45.0F, 45.0F) * Mth.DEG_TO_RAD;
String[] chain = AIM_CHAINS[slot.ordinal()];
float yawPerBone = relativeYaw / chain.length;
float pitchPerBone = pitch / chain.length;
for (String boneName : chain) {
this.getBone(boneName).ifPresent(bone -> {
bone.setRotY(yawPerBone);
bone.setRotX(pitchPerBone);
});
}
}
} }
@Nullable @Nullable
@@ -3,9 +3,15 @@ package com.craisinlord.antarchy.content.client.model;
import com.craisinlord.antarchy.Antarchy; import com.craisinlord.antarchy.Antarchy;
import com.craisinlord.antarchy.content.entity.royal.RoyalMountEntity; import com.craisinlord.antarchy.content.entity.royal.RoyalMountEntity;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.Nullable;
import software.bernie.geckolib.animation.AnimationState;
import software.bernie.geckolib.cache.object.GeoBone;
import software.bernie.geckolib.model.GeoModel; import software.bernie.geckolib.model.GeoModel;
public class RoyalMountModel extends GeoModel<RoyalMountEntity> { public class RoyalMountModel extends GeoModel<RoyalMountEntity> {
private static final String[] HEADS = {"head_left", "head", "head_right"};
@Override @Override
public ResourceLocation getModelResource(RoyalMountEntity animatable) { public ResourceLocation getModelResource(RoyalMountEntity animatable) {
return ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "geo/" + animatable.geoNameForRender() + ".geo.json"); return ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "geo/" + animatable.geoNameForRender() + ".geo.json");
@@ -20,4 +26,22 @@ public class RoyalMountModel extends GeoModel<RoyalMountEntity> {
public ResourceLocation getAnimationResource(RoyalMountEntity animatable) { public ResourceLocation getAnimationResource(RoyalMountEntity animatable) {
return ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "animations/" + animatable.geoNameForRender() + ".animation.json"); return ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "animations/" + animatable.geoNameForRender() + ".animation.json");
} }
@Override
public void setCustomAnimations(RoyalMountEntity animatable, long instanceId,
AnimationState<RoyalMountEntity> animationState) {
super.setCustomAnimations(animatable, instanceId, animationState);
for (String head : HEADS) {
this.getBone(head).ifPresent(bone -> bone.setTrackingMatrices(true));
}
}
@Nullable
public Vec3 getTrackedBeamAnchor(int head) {
if (head < 0 || head >= HEADS.length) return null;
GeoBone bone = this.getBone(HEADS[head]).orElse(null);
if (bone == null || !bone.isTrackingMatrices()) return null;
org.joml.Vector3d position = bone.getWorldPosition();
return new Vec3(position.x, position.y, position.z);
}
} }
@@ -0,0 +1,26 @@
package com.craisinlord.antarchy.content.client.renderer;
import com.mojang.blaze3d.vertex.DefaultVertexFormat;
import com.mojang.blaze3d.vertex.VertexFormat;
import net.minecraft.client.renderer.RenderStateShard;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.resources.ResourceLocation;
public class RoyalBeamRenderTypes extends RenderType {
public RoyalBeamRenderTypes(String name, VertexFormat format, VertexFormat.Mode mode, int bufferSize,
boolean affectsCrumbling, boolean sortOnUpload, Runnable setupState, Runnable clearState) {
super(name, format, mode, bufferSize, affectsCrumbling, sortOnUpload, setupState, clearState);
}
public static RenderType beam(ResourceLocation texture) {
return create("royal_beam", DefaultVertexFormat.NEW_ENTITY, VertexFormat.Mode.QUADS, 256, true, true,
CompositeState.builder()
.setTextureState(new TextureStateShard(texture, false, false))
.setShaderState(RenderStateShard.RENDERTYPE_ENERGY_SWIRL_SHADER)
.setTransparencyState(RenderStateShard.TRANSLUCENT_TRANSPARENCY)
.setCullState(RenderStateShard.NO_CULL)
.setLightmapState(RenderStateShard.LIGHTMAP)
.setOutputState(RenderStateShard.ITEM_ENTITY_TARGET)
.createCompositeState(false));
}
}
@@ -25,22 +25,28 @@ import software.bernie.geckolib.util.Color;
public class RoyalBossRenderer extends GeoEntityRenderer<RoyalBossEntity> { public class RoyalBossRenderer extends GeoEntityRenderer<RoyalBossEntity> {
private static final ResourceLocation QUEEN_PURPLE_BEAM_OUTER = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/queen/queen_purple_beam_outer.png"); private static final ResourceLocation QUEEN_PURPLE_BEAM_OUTER = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/queen/queen_purple_beam_outer.png");
private static final ResourceLocation QUEEN_PURPLE_BEAM_INNER = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/queen/queen_purple_beam_inner.png"); private static final ResourceLocation QUEEN_PURPLE_BEAM_INNER = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/queen/queen_purple_beam_inner.png");
private static final ResourceLocation QUEEN_PURPLE_BEAM_END = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/queen/queen_purple_beam_end_1.png"); private static final ResourceLocation QUEEN_PURPLE_BEAM_END_1 = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/queen/queen_purple_beam_end_1.png");
private static final ResourceLocation QUEEN_PURPLE_BEAM_END_2 = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/queen/queen_purple_beam_end_2.png");
private static final ResourceLocation QUEEN_RED_BEAM_OUTER = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/queen/queen_red_beam_outer.png"); private static final ResourceLocation QUEEN_RED_BEAM_OUTER = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/queen/queen_red_beam_outer.png");
private static final ResourceLocation QUEEN_RED_BEAM_INNER = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/queen/queen_red_beam_inner.png"); private static final ResourceLocation QUEEN_RED_BEAM_INNER = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/queen/queen_red_beam_inner.png");
private static final ResourceLocation QUEEN_RED_BEAM_END = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/queen/queen_red_beam_end_1.png"); private static final ResourceLocation QUEEN_RED_BEAM_END_1 = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/queen/queen_red_beam_end_1.png");
private static final ResourceLocation QUEEN_RED_BEAM_END_2 = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/queen/queen_red_beam_end_2.png");
private static final ResourceLocation QUEEN_BLACK_BEAM_OUTER = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/queen/queen_black_beam_outer.png"); private static final ResourceLocation QUEEN_BLACK_BEAM_OUTER = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/queen/queen_black_beam_outer.png");
private static final ResourceLocation QUEEN_BLACK_BEAM_INNER = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/queen/queen_black_beam_inner.png"); private static final ResourceLocation QUEEN_BLACK_BEAM_INNER = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/queen/queen_black_beam_inner.png");
private static final ResourceLocation QUEEN_BLACK_BEAM_END = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/queen/queen_black_beam_end_1.png"); private static final ResourceLocation QUEEN_BLACK_BEAM_END_1 = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/queen/queen_black_beam_end_1.png");
private static final ResourceLocation QUEEN_BLACK_BEAM_END_2 = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/queen/queen_black_beam_end_2.png");
private static final ResourceLocation KING_FIRE_BEAM_OUTER = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/king/fire_beam_outer.png"); private static final ResourceLocation KING_FIRE_BEAM_OUTER = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/king/fire_beam_outer.png");
private static final ResourceLocation KING_FIRE_BEAM_INNER = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/king/fire_beam_inner.png"); private static final ResourceLocation KING_FIRE_BEAM_INNER = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/king/fire_beam_inner.png");
private static final ResourceLocation KING_FIRE_BEAM_END = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/king/fire_beam_end_1.png"); private static final ResourceLocation KING_FIRE_BEAM_END_1 = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/king/fire_beam_end_1.png");
private static final ResourceLocation KING_FIRE_BEAM_END_2 = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/king/fire_beam_end_2.png");
private static final ResourceLocation KING_LIGHTNING_BEAM_OUTER = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/king/lightning_beam_outer.png"); private static final ResourceLocation KING_LIGHTNING_BEAM_OUTER = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/king/lightning_beam_outer.png");
private static final ResourceLocation KING_LIGHTNING_BEAM_INNER = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/king/lightning_beam_inner.png"); private static final ResourceLocation KING_LIGHTNING_BEAM_INNER = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/king/lightning_beam_inner.png");
private static final ResourceLocation KING_LIGHTNING_BEAM_END = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/king/lightning_beam_end_1.png"); private static final ResourceLocation KING_LIGHTNING_BEAM_END_1 = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/king/lightning_beam_end_1.png");
private static final ResourceLocation KING_LIGHTNING_BEAM_END_2 = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/king/lightning_beam_end_2.png");
private static final ResourceLocation KING_ICE_BEAM_OUTER = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/king/ice_beam_outer.png"); private static final ResourceLocation KING_ICE_BEAM_OUTER = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/king/ice_beam_outer.png");
private static final ResourceLocation KING_ICE_BEAM_INNER = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/king/ice_beam_inner.png"); private static final ResourceLocation KING_ICE_BEAM_INNER = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/king/ice_beam_inner.png");
private static final ResourceLocation KING_ICE_BEAM_END = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/king/ice_beam_end_1.png"); private static final ResourceLocation KING_ICE_BEAM_END_1 = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/king/ice_beam_end_1.png");
private static final ResourceLocation KING_ICE_BEAM_END_2 = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/king/ice_beam_end_2.png");
public RoyalBossRenderer(EntityRendererProvider.Context context) { public RoyalBossRenderer(EntityRendererProvider.Context context) {
super(context, new RoyalBossModel()); super(context, new RoyalBossModel());
this.shadowRadius = 8.0F; this.shadowRadius = 8.0F;
@@ -110,10 +116,8 @@ public class RoyalBossRenderer extends GeoEntityRenderer<RoyalBossEntity> {
this.shadowRadius = 8.0F * RoyalBossEntity.MODEL_RENDER_SCALE; this.shadowRadius = 8.0F * RoyalBossEntity.MODEL_RENDER_SCALE;
} }
private static final float BEAM_OUTER_RADIUS = 0.55F; private static final float BEAM_OUTER_RADIUS = 1.75F;
private static final float BEAM_INNER_RADIUS = 0.32F; private static final float BEAM_INNER_RADIUS = 1.5F;
private static final float BEAM_TILE_BLOCKS = 3.0F;
private static final float BEAM_SCROLL_SPEED = 0.32F;
@Override @Override
public void render(RoyalBossEntity entity, float entityYaw, float partialTick, PoseStack poseStack, public void render(RoyalBossEntity entity, float entityYaw, float partialTick, PoseStack poseStack,
@@ -152,23 +156,23 @@ public class RoyalBossRenderer extends GeoEntityRenderer<RoyalBossEntity> {
ResourceLocation outer = QUEEN_PURPLE_BEAM_OUTER; ResourceLocation outer = QUEEN_PURPLE_BEAM_OUTER;
ResourceLocation inner = QUEEN_PURPLE_BEAM_INNER; ResourceLocation inner = QUEEN_PURPLE_BEAM_INNER;
ResourceLocation endTexture = QUEEN_PURPLE_BEAM_END; ResourceLocation[] endTextures = {QUEEN_PURPLE_BEAM_END_1, QUEEN_PURPLE_BEAM_END_2};
if (entity instanceof KingEntity) { if (entity instanceof KingEntity) {
switch (entity.getRoyalBeamElement()) { switch (entity.getRoyalBeamElement()) {
case FIRE -> { case FIRE -> {
outer = KING_FIRE_BEAM_OUTER; outer = KING_FIRE_BEAM_OUTER;
inner = KING_FIRE_BEAM_INNER; inner = KING_FIRE_BEAM_INNER;
endTexture = KING_FIRE_BEAM_END; endTextures = new ResourceLocation[]{KING_FIRE_BEAM_END_1, KING_FIRE_BEAM_END_2};
} }
case LIGHTNING -> { case LIGHTNING -> {
outer = KING_LIGHTNING_BEAM_OUTER; outer = KING_LIGHTNING_BEAM_OUTER;
inner = KING_LIGHTNING_BEAM_INNER; inner = KING_LIGHTNING_BEAM_INNER;
endTexture = KING_LIGHTNING_BEAM_END; endTextures = new ResourceLocation[]{KING_LIGHTNING_BEAM_END_1, KING_LIGHTNING_BEAM_END_2};
} }
case ICE -> { case ICE -> {
outer = KING_ICE_BEAM_OUTER; outer = KING_ICE_BEAM_OUTER;
inner = KING_ICE_BEAM_INNER; inner = KING_ICE_BEAM_INNER;
endTexture = KING_ICE_BEAM_END; endTextures = new ResourceLocation[]{KING_ICE_BEAM_END_1, KING_ICE_BEAM_END_2};
} }
case GENERIC -> { case GENERIC -> {
} }
@@ -178,12 +182,12 @@ public class RoyalBossRenderer extends GeoEntityRenderer<RoyalBossEntity> {
case QUEEN_RED -> { case QUEEN_RED -> {
outer = QUEEN_RED_BEAM_OUTER; outer = QUEEN_RED_BEAM_OUTER;
inner = QUEEN_RED_BEAM_INNER; inner = QUEEN_RED_BEAM_INNER;
endTexture = QUEEN_RED_BEAM_END; endTextures = new ResourceLocation[]{QUEEN_RED_BEAM_END_1, QUEEN_RED_BEAM_END_2};
} }
case QUEEN_BLACK -> { case QUEEN_BLACK -> {
outer = QUEEN_BLACK_BEAM_OUTER; outer = QUEEN_BLACK_BEAM_OUTER;
inner = QUEEN_BLACK_BEAM_INNER; inner = QUEEN_BLACK_BEAM_INNER;
endTexture = QUEEN_BLACK_BEAM_END; endTextures = new ResourceLocation[]{QUEEN_BLACK_BEAM_END_1, QUEEN_BLACK_BEAM_END_2};
} }
default -> { default -> {
} }
@@ -191,34 +195,53 @@ public class RoyalBossRenderer extends GeoEntityRenderer<RoyalBossEntity> {
} }
float time = entity.tickCount + partialTick; float time = entity.tickCount + partialTick;
float tiles = (float) (length / BEAM_TILE_BLOCKS); float outerV = -time * 0.25F;
float scroll = -time * BEAM_SCROLL_SPEED;
var pose = poseStack.last().pose(); var pose = poseStack.last().pose();
VertexConsumer outerBuffer = bufferSource.getBuffer(RenderType.entityTranslucentEmissive(outer)); float innerV = -time * 0.25F * 0.5F;
drawBeamPlane(outerBuffer, pose, start, finish, side1.scale(BEAM_OUTER_RADIUS), scroll, scroll + tiles); VertexConsumer innerBuffer = bufferSource.getBuffer(RoyalBeamRenderTypes.beam(inner));
drawBeamPlane(outerBuffer, pose, start, finish, side2.scale(BEAM_OUTER_RADIUS), scroll, scroll + tiles); drawBeamTube(innerBuffer, pose, start, finish, side1, side2, BEAM_INNER_RADIUS, 4, innerV, 0.0F, 0.55F, 0.5F, 255);
float innerScroll = -time * BEAM_SCROLL_SPEED * 1.6F; VertexConsumer outerBuffer = bufferSource.getBuffer(RoyalBeamRenderTypes.beam(outer));
VertexConsumer innerBuffer = bufferSource.getBuffer(RenderType.entityTranslucentEmissive(inner)); // Tremorzilla's outer shell stays fully visible all the way to the impact;
drawBeamPlane(innerBuffer, pose, start, finish, side1.scale(BEAM_INNER_RADIUS), innerScroll, innerScroll + tiles); // fading the far ring makes the beam look like a translucent cone.
drawBeamPlane(innerBuffer, pose, start, finish, side2.scale(BEAM_INNER_RADIUS), innerScroll, innerScroll + tiles); drawBeamTube(outerBuffer, pose, start, finish, side1, side2, BEAM_OUTER_RADIUS, 8, outerV, 0.0F, 0.55F, 0.15F, 255);
float impactSize = 1.1F + 0.35F * Mth.sin(time * 0.6F); float impactSize = 1.5F + 0.35F * Mth.sin(time * 0.6F);
Vec3 camLeft = new Vec3(this.entityRenderDispatcher.camera.getLeftVector()); ResourceLocation endTexture = endTextures[(entity.tickCount / 2) % endTextures.length];
Vec3 camUp = new Vec3(this.entityRenderDispatcher.camera.getUpVector()); VertexConsumer endBuffer = bufferSource.getBuffer(RoyalBeamRenderTypes.beam(endTexture));
VertexConsumer endBuffer = bufferSource.getBuffer(RenderType.entityTranslucentEmissive(endTexture)); drawBeamImpact(endBuffer, pose, finish.subtract(dir.scale(1.5D)), dir, side1, side2, impactSize);
drawBillboard(endBuffer, pose, finish, camLeft.scale(impactSize), camUp.scale(impactSize));
} }
private static void drawBeamPlane(VertexConsumer vertices, org.joml.Matrix4f pose, Vec3 start, Vec3 end, Vec3 half, private static void drawBeamTube(VertexConsumer vertices, org.joml.Matrix4f pose, Vec3 start, Vec3 end,
float startV, float endV) { Vec3 side1, Vec3 side2, float radius, int sections, float scroll, float phase,
emitQuad(vertices, pose, float startScale, float uvLengthScale, int endAlpha) {
start.subtract(half), start.add(half), end.add(half), end.subtract(half), Vec3[] startRing = new Vec3[sections];
0.0F, startV, 1.0F, endV); Vec3[] endRing = new Vec3[sections];
emitQuad(vertices, pose, for (int i = 0; i < sections; i++) {
start.add(half), start.subtract(half), end.subtract(half), end.add(half), double angle = Math.PI * 2.0D * i / sections + phase;
0.0F, startV, 1.0F, endV); Vec3 offset = side1.scale(Math.cos(angle) * radius).add(side2.scale(Math.sin(angle) * radius));
startRing[i] = start.add(offset.scale(startScale));
endRing[i] = end.add(offset);
}
float endV = scroll + (float) start.distanceTo(end) * uvLengthScale;
for (int i = 0; i < sections; i++) {
int next = (i + 1) % sections;
// Alex's beam renderer intentionally uses section indices for U rather
// than normalizing them. This is part of the authored 16x16 texture
// pattern and keeps the strip continuous around the tube.
emitBeamQuad(vertices, pose, startRing[i], endRing[i], endRing[next], startRing[next],
i, scroll, next, endV, endAlpha);
}
}
private static void emitBeamQuad(VertexConsumer vertices, org.joml.Matrix4f pose,
Vec3 a, Vec3 b, Vec3 c, Vec3 d,
float minU, float minV, float maxU, float maxV, int endAlpha) {
beamVertex(vertices, pose, a, minU, minV, 255);
beamVertex(vertices, pose, b, maxU, minV, endAlpha);
beamVertex(vertices, pose, c, maxU, maxV, endAlpha);
beamVertex(vertices, pose, d, minU, maxV, 255);
} }
private static void drawBillboard(VertexConsumer vertices, org.joml.Matrix4f pose, Vec3 center, Vec3 left, Vec3 up) { private static void drawBillboard(VertexConsumer vertices, org.joml.Matrix4f pose, Vec3 center, Vec3 left, Vec3 up) {
@@ -232,6 +255,17 @@ public class RoyalBossRenderer extends GeoEntityRenderer<RoyalBossEntity> {
0.0F, 0.0F, 1.0F, 1.0F); 0.0F, 0.0F, 1.0F, 1.0F);
} }
private static void drawBeamImpact(VertexConsumer vertices, org.joml.Matrix4f pose, Vec3 center, Vec3 direction,
Vec3 side1, Vec3 side2, float size) {
// The end textures are atlas textures used by Tremorzilla's crossed flame
// planes, not a texture meant to be repeated on eight radial billboards.
// Two crossed planes preserve the same readable four-point impact without
// stretching the atlas into the noisy starburst the old code produced.
drawBillboard(vertices, pose, center, side1.scale(size), side2.scale(size));
drawBillboard(vertices, pose, center.subtract(direction.scale(size * 0.35D)),
side1.scale(size * 0.82D), direction.scale(size * 0.82D));
}
private static void emitQuad(VertexConsumer vertices, org.joml.Matrix4f pose, private static void emitQuad(VertexConsumer vertices, org.joml.Matrix4f pose,
Vec3 a, Vec3 b, Vec3 c, Vec3 d, Vec3 a, Vec3 b, Vec3 c, Vec3 d,
float minU, float minV, float maxU, float maxV) { float minU, float minV, float maxU, float maxV) {
@@ -248,4 +282,12 @@ public class RoyalBossRenderer extends GeoEntityRenderer<RoyalBossEntity> {
.setLight(0x00F000F0) .setLight(0x00F000F0)
.setNormal(0.0F, 1.0F, 0.0F); .setNormal(0.0F, 1.0F, 0.0F);
} }
private static void beamVertex(VertexConsumer vertices, org.joml.Matrix4f pose, Vec3 pos, float u, float v, int alpha) {
vertices.addVertex(pose, (float) pos.x, (float) pos.y, (float) pos.z)
.setColor(255, 255, 255, alpha).setUv(u, v)
.setOverlay(OverlayTexture.NO_OVERLAY)
.setLight(0x00F000F0)
.setNormal(0.0F, -1.0F, 0.0F);
}
} }
@@ -19,16 +19,20 @@ import software.bernie.geckolib.renderer.GeoEntityRenderer;
public class RoyalMountRenderer extends GeoEntityRenderer<RoyalMountEntity> { public class RoyalMountRenderer extends GeoEntityRenderer<RoyalMountEntity> {
private static final ResourceLocation QUEEN_OUTER = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/queen/queen_purple_beam_outer.png"); private static final ResourceLocation QUEEN_OUTER = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/queen/queen_purple_beam_outer.png");
private static final ResourceLocation QUEEN_INNER = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/queen/queen_purple_beam_inner.png"); private static final ResourceLocation QUEEN_INNER = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/queen/queen_purple_beam_inner.png");
private static final ResourceLocation QUEEN_END = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/queen/queen_purple_beam_end_1.png"); private static final ResourceLocation QUEEN_END_1 = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/queen/queen_purple_beam_end_1.png");
private static final ResourceLocation QUEEN_END_2 = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/queen/queen_purple_beam_end_2.png");
private static final ResourceLocation KING_FIRE_OUTER = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/king/fire_beam_outer.png"); private static final ResourceLocation KING_FIRE_OUTER = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/king/fire_beam_outer.png");
private static final ResourceLocation KING_FIRE_INNER = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/king/fire_beam_inner.png"); private static final ResourceLocation KING_FIRE_INNER = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/king/fire_beam_inner.png");
private static final ResourceLocation KING_FIRE_END = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/king/fire_beam_end_1.png"); private static final ResourceLocation KING_FIRE_END_1 = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/king/fire_beam_end_1.png");
private static final ResourceLocation KING_FIRE_END_2 = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/king/fire_beam_end_2.png");
private static final ResourceLocation KING_LIGHTNING_OUTER = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/king/lightning_beam_outer.png"); private static final ResourceLocation KING_LIGHTNING_OUTER = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/king/lightning_beam_outer.png");
private static final ResourceLocation KING_LIGHTNING_INNER = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/king/lightning_beam_inner.png"); private static final ResourceLocation KING_LIGHTNING_INNER = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/king/lightning_beam_inner.png");
private static final ResourceLocation KING_LIGHTNING_END = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/king/lightning_beam_end_1.png"); private static final ResourceLocation KING_LIGHTNING_END_1 = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/king/lightning_beam_end_1.png");
private static final ResourceLocation KING_LIGHTNING_END_2 = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/king/lightning_beam_end_2.png");
private static final ResourceLocation KING_ICE_OUTER = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/king/ice_beam_outer.png"); private static final ResourceLocation KING_ICE_OUTER = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/king/ice_beam_outer.png");
private static final ResourceLocation KING_ICE_INNER = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/king/ice_beam_inner.png"); private static final ResourceLocation KING_ICE_INNER = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/king/ice_beam_inner.png");
private static final ResourceLocation KING_ICE_END = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/king/ice_beam_end_1.png"); private static final ResourceLocation KING_ICE_END_1 = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/king/ice_beam_end_1.png");
private static final ResourceLocation KING_ICE_END_2 = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/king/ice_beam_end_2.png");
public RoyalMountRenderer(EntityRendererProvider.Context context) { public RoyalMountRenderer(EntityRendererProvider.Context context) {
super(context, new RoyalMountModel()); super(context, new RoyalMountModel());
this.shadowRadius = 1.1F; this.shadowRadius = 1.1F;
@@ -58,7 +62,9 @@ public class RoyalMountRenderer extends GeoEntityRenderer<RoyalMountEntity> {
double x = Mth.lerp(partialTick, entity.xo, entity.getX()); double x = Mth.lerp(partialTick, entity.xo, entity.getX());
double y = Mth.lerp(partialTick, entity.yo, entity.getY()); double y = Mth.lerp(partialTick, entity.yo, entity.getY());
double z = Mth.lerp(partialTick, entity.zo, entity.getZ()); double z = Mth.lerp(partialTick, entity.zo, entity.getZ());
Vec3 start = entity.beamShootFrom().subtract(x, y, z); int beamHead = entity.getBeamHead();
Vec3 trackedAnchor = ((RoyalMountModel) this.getGeoModel()).getTrackedBeamAnchor(beamHead);
Vec3 start = (trackedAnchor != null ? trackedAnchor : entity.beamShootFrom()).subtract(x, y, z);
Vec3 finish = end.subtract(x, y, z); Vec3 finish = end.subtract(x, y, z);
Vec3 axis = finish.subtract(start); Vec3 axis = finish.subtract(start);
double length = axis.length(); double length = axis.length();
@@ -69,39 +75,90 @@ public class RoyalMountRenderer extends GeoEntityRenderer<RoyalMountEntity> {
side = side.normalize(); side = side.normalize();
ResourceLocation outer = QUEEN_OUTER; ResourceLocation outer = QUEEN_OUTER;
ResourceLocation inner = QUEEN_INNER; ResourceLocation inner = QUEEN_INNER;
ResourceLocation endTexture = QUEEN_END; ResourceLocation[] endTextures = {QUEEN_END_1, QUEEN_END_2};
if (entity instanceof PrinceEntity) { if (entity instanceof PrinceEntity) {
switch (entity.getBeamElement()) { switch (entity.getBeamElement()) {
case FIRE -> { outer = KING_FIRE_OUTER; inner = KING_FIRE_INNER; endTexture = KING_FIRE_END; } case FIRE -> { outer = KING_FIRE_OUTER; inner = KING_FIRE_INNER; endTextures = new ResourceLocation[]{KING_FIRE_END_1, KING_FIRE_END_2}; }
case ICE -> { outer = KING_ICE_OUTER; inner = KING_ICE_INNER; endTexture = KING_ICE_END; } case ICE -> { outer = KING_ICE_OUTER; inner = KING_ICE_INNER; endTextures = new ResourceLocation[]{KING_ICE_END_1, KING_ICE_END_2}; }
default -> { outer = KING_LIGHTNING_OUTER; inner = KING_LIGHTNING_INNER; endTexture = KING_LIGHTNING_END; } default -> { outer = KING_LIGHTNING_OUTER; inner = KING_LIGHTNING_INNER; endTextures = new ResourceLocation[]{KING_LIGHTNING_END_1, KING_LIGHTNING_END_2}; }
} }
} }
float time = entity.tickCount + partialTick; float time = entity.tickCount + partialTick;
float tiles = (float) (length / 3.0D); float outerV = -time * 0.25F;
org.joml.Matrix4f pose = poseStack.last().pose(); org.joml.Matrix4f pose = poseStack.last().pose();
drawBeam(bufferSource.getBuffer(RenderType.entityTranslucentEmissive(outer)), pose, start, finish, side.scale(0.24D), -time * 0.32F, -time * 0.32F + tiles); Vec3 side2 = dir.cross(side).normalize();
drawBeam(bufferSource.getBuffer(RenderType.entityTranslucentEmissive(inner)), pose, start, finish, side.scale(0.13D), -time * 0.5F, -time * 0.5F + tiles); VertexConsumer outerBuffer = bufferSource.getBuffer(RoyalBeamRenderTypes.beam(outer));
Vec3 up = new Vec3(this.entityRenderDispatcher.camera.getUpVector()).scale(0.55D); drawBeamTube(outerBuffer, pose, start, finish, side, side2, 0.44F, 8, outerV, 0.0F, 0.55F, 0.15F, 0);
Vec3 left = new Vec3(this.entityRenderDispatcher.camera.getLeftVector()).scale(0.55D); float innerV = -time * 0.25F * 0.5F;
drawQuad(bufferSource.getBuffer(RenderType.entityTranslucentEmissive(endTexture)), pose, finish.add(left).add(up), finish.subtract(left).add(up), finish.subtract(left).subtract(up), finish.add(left).subtract(up)); VertexConsumer innerBuffer = bufferSource.getBuffer(RoyalBeamRenderTypes.beam(inner));
drawBeamTube(innerBuffer, pose, start, finish, side, side2, 0.375F, 4, innerV, 1.0F, 0.55F, 0.5F, 255);
float impactSize = 0.375F + 0.09F * Mth.sin(time * 0.6F);
ResourceLocation endTexture = endTextures[(entity.tickCount / 2) % endTextures.length];
VertexConsumer endBuffer = bufferSource.getBuffer(RoyalBeamRenderTypes.beam(endTexture));
drawBeamImpact(endBuffer, pose, finish.subtract(dir.scale(0.375D)), dir, side, side2, impactSize);
} }
private static void drawBeam(VertexConsumer vertices, org.joml.Matrix4f pose, Vec3 start, Vec3 end, Vec3 half, float startV, float endV) { private static void drawBeamTube(VertexConsumer vertices, org.joml.Matrix4f pose, Vec3 start, Vec3 end,
drawQuad(vertices, pose, start.subtract(half), start.add(half), end.add(half), end.subtract(half), startV, endV); Vec3 side1, Vec3 side2, float radius, int sections, float scroll, float phase,
drawQuad(vertices, pose, start.add(half), start.subtract(half), end.subtract(half), end.add(half), startV, endV); float startScale, float uvLengthScale, int endAlpha) {
Vec3[] startRing = new Vec3[sections];
Vec3[] endRing = new Vec3[sections];
for (int i = 0; i < sections; i++) {
double angle = Math.PI * 2.0D * i / sections + phase;
Vec3 offset = side1.scale(Math.cos(angle) * radius).add(side2.scale(Math.sin(angle) * radius));
startRing[i] = start.add(offset.scale(startScale));
endRing[i] = end.add(offset);
}
float endV = scroll + (float) start.distanceTo(end) * uvLengthScale;
for (int i = 0; i < sections; i++) {
int next = (i + 1) % sections;
emitBeamQuad(vertices, pose, startRing[i], endRing[i], endRing[next], startRing[next],
i / (float) sections, scroll, next / (float) sections, endV, endAlpha);
}
} }
private static void drawQuad(VertexConsumer vertices, org.joml.Matrix4f pose, Vec3 a, Vec3 b, Vec3 c, Vec3 d) { private static void emitBeamQuad(VertexConsumer vertices, org.joml.Matrix4f pose,
drawQuad(vertices, pose, a, b, c, d, 0.0F, 1.0F); Vec3 a, Vec3 b, Vec3 c, Vec3 d,
float minU, float minV, float maxU, float maxV, int endAlpha) {
beamVertex(vertices, pose, a, minU, minV, 255);
beamVertex(vertices, pose, b, maxU, minV, endAlpha);
beamVertex(vertices, pose, c, maxU, maxV, endAlpha);
beamVertex(vertices, pose, d, minU, maxV, 255);
} }
private static void drawQuad(VertexConsumer vertices, org.joml.Matrix4f pose, Vec3 a, Vec3 b, Vec3 c, Vec3 d, float startV, float endV) { private static void drawBeamImpact(VertexConsumer vertices, org.joml.Matrix4f pose, Vec3 center, Vec3 direction,
vertex(vertices, pose, a, 0.0F, startV); vertex(vertices, pose, b, 1.0F, startV); vertex(vertices, pose, c, 1.0F, endV); vertex(vertices, pose, d, 0.0F, endV); Vec3 side1, Vec3 side2, float size) {
drawBillboard(vertices, pose, center, side1.scale(size), side2.scale(size));
for (int i = 0; i < 8; i++) {
double angle = (i + 0.5D) * Math.PI * 0.25D;
Vec3 radial = side1.scale(Math.cos(angle)).add(side2.scale(Math.sin(angle))).scale(size);
Vec3 back = direction.scale(size * (i % 2 == 0 ? 1.5D : 1.0D));
emitQuad(vertices, pose, center.add(radial), center.subtract(radial),
center.subtract(radial).subtract(back), center.add(radial).subtract(back));
}
}
private static void drawBillboard(VertexConsumer vertices, org.joml.Matrix4f pose, Vec3 center, Vec3 left, Vec3 up) {
emitQuad(vertices, pose, center.add(left).add(up), center.subtract(left).add(up),
center.subtract(left).subtract(up), center.add(left).subtract(up));
emitQuad(vertices, pose, center.subtract(left).add(up), center.add(left).add(up),
center.add(left).subtract(up), center.subtract(left).subtract(up));
}
private static void emitQuad(VertexConsumer vertices, org.joml.Matrix4f pose, Vec3 a, Vec3 b, Vec3 c, Vec3 d) {
vertex(vertices, pose, a, 0.0F, 0.0F);
vertex(vertices, pose, b, 1.0F, 0.0F);
vertex(vertices, pose, c, 1.0F, 1.0F);
vertex(vertices, pose, d, 0.0F, 1.0F);
} }
private static void vertex(VertexConsumer vertices, org.joml.Matrix4f pose, Vec3 pos, float u, float v) { private static void vertex(VertexConsumer vertices, org.joml.Matrix4f pose, Vec3 pos, float u, float v) {
vertices.addVertex(pose, (float) pos.x, (float) pos.y, (float) pos.z).setColor(255, 255, 255, 255).setUv(u, v) vertices.addVertex(pose, (float) pos.x, (float) pos.y, (float) pos.z).setColor(255, 255, 255, 255).setUv(u, v)
.setOverlay(net.minecraft.client.renderer.texture.OverlayTexture.NO_OVERLAY).setLight(0x00F000F0).setNormal(0.0F, 1.0F, 0.0F); .setOverlay(net.minecraft.client.renderer.texture.OverlayTexture.NO_OVERLAY).setLight(0x00F000F0).setNormal(0.0F, 1.0F, 0.0F);
} }
private static void beamVertex(VertexConsumer vertices, org.joml.Matrix4f pose, Vec3 pos, float u, float v, int alpha) {
vertices.addVertex(pose, (float) pos.x, (float) pos.y, (float) pos.z).setColor(255, 255, 255, alpha).setUv(u, v)
.setOverlay(net.minecraft.client.renderer.texture.OverlayTexture.NO_OVERLAY).setLight(0x00F000F0).setNormal(0.0F, -1.0F, 0.0F);
}
} }
@@ -2,6 +2,7 @@ package com.craisinlord.antarchy.content.entity.ant;
import com.craisinlord.antarchy.Antarchy; import com.craisinlord.antarchy.Antarchy;
import com.craisinlord.antarchy.compat.infinity.InfinityCompat; import com.craisinlord.antarchy.compat.infinity.InfinityCompat;
import com.craisinlord.antarchy.compat.infinity.InfinityWarpResult;
import com.craisinlord.antarchy.config.AntarchySettings; import com.craisinlord.antarchy.config.AntarchySettings;
import com.craisinlord.antarchy.content.AntarchyTags; import com.craisinlord.antarchy.content.AntarchyTags;
import com.craisinlord.antarchy.content.portal.RainbowPortalIgniter; import com.craisinlord.antarchy.content.portal.RainbowPortalIgniter;
@@ -62,7 +63,8 @@ public class RainbowAntEntity extends BaseAntEntity implements GeoEntity {
return AntTeleportHelper.teleportPlayerToDimension(player, AntarchySettings.rainbowAntNonInfinityFallbackDimension()); return AntTeleportHelper.teleportPlayerToDimension(player, AntarchySettings.rainbowAntNonInfinityFallbackDimension());
} }
if (!InfinityCompat.get().requestWarp(player, dimensionId)) { InfinityWarpResult result = InfinityCompat.get().requestWarp(player, dimensionId);
if (result == InfinityWarpResult.REJECTED) {
player.displayClientMessage(Component.translatable("message.antarchy.rainbow_ant_failed_dimension_create"), true); player.displayClientMessage(Component.translatable("message.antarchy.rainbow_ant_failed_dimension_create"), true);
return InteractionResult.CONSUME; return InteractionResult.CONSUME;
} }
@@ -356,9 +358,13 @@ public class RainbowAntEntity extends BaseAntEntity implements GeoEntity {
} }
private boolean teleportToInfinity(ServerPlayer player, ResourceLocation dimensionId) { private boolean teleportToInfinity(ServerPlayer player, ResourceLocation dimensionId) {
if (!InfinityCompat.get().requestWarp(player, dimensionId)) { InfinityWarpResult result = InfinityCompat.get().requestWarp(player, dimensionId);
if (result == InfinityWarpResult.FAILED_GENERATION) {
this.deleteGeneratedArtifacts(player.server, dimensionId); this.deleteGeneratedArtifacts(player.server, dimensionId);
this.markForRerollOnNextReagent(); player.displayClientMessage(Component.translatable("message.antarchy.rainbow_ant_failed_dimension_create"), true);
return false;
}
if (result == InfinityWarpResult.REJECTED) {
player.displayClientMessage(Component.translatable("message.antarchy.rainbow_ant_failed_dimension_create"), true); player.displayClientMessage(Component.translatable("message.antarchy.rainbow_ant_failed_dimension_create"), true);
return false; return false;
} }
@@ -377,9 +383,7 @@ public class RainbowAntEntity extends BaseAntEntity implements GeoEntity {
.resolve(dimensionId.getPath()); .resolve(dimensionId.getPath());
this.deletePathRecursively(datapackPath); this.deletePathRecursively(datapackPath);
if (dimensionPath != null) { this.deletePathRecursively(dimensionPath);
this.deletePathRecursively(dimensionPath);
}
} }
private void deletePathRecursively(Path path) { private void deletePathRecursively(Path path) {
@@ -33,6 +33,7 @@ import net.minecraft.world.entity.monster.Monster;
import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.Level; import net.minecraft.world.level.Level;
import net.minecraft.world.level.ServerLevelAccessor; import net.minecraft.world.level.ServerLevelAccessor;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import software.bernie.geckolib.animatable.GeoEntity; import software.bernie.geckolib.animatable.GeoEntity;
@@ -75,6 +76,8 @@ public class LucidEntity extends Monster implements GeoEntity {
private static final double MIN_HOVER_RANGE = 6.5D; private static final double MIN_HOVER_RANGE = 6.5D;
private static final double CLOSE_RANGE = 4.5D; private static final double CLOSE_RANGE = 4.5D;
private static final double DEFAULT_HOVER_HEIGHT = 2.75D; private static final double DEFAULT_HOVER_HEIGHT = 2.75D;
private static final double NATURAL_SPAWN_LIMIT_RADIUS = 64.0D;
private static final int NATURAL_SPAWN_LIMIT = 5;
private static final RawAnimation IDLE_ANIM = RawAnimation.begin().thenLoop("idle"); private static final RawAnimation IDLE_ANIM = RawAnimation.begin().thenLoop("idle");
private static final RawAnimation FLY_ANIM = RawAnimation.begin().thenLoop("fly"); private static final RawAnimation FLY_ANIM = RawAnimation.begin().thenLoop("fly");
@@ -118,6 +121,7 @@ public class LucidEntity extends Monster implements GeoEntity {
return level.getDifficulty() != Difficulty.PEACEFUL; return level.getDifficulty() != Difficulty.PEACEFUL;
} }
return level.getDifficulty() != Difficulty.PEACEFUL return level.getDifficulty() != Difficulty.PEACEFUL
&& level.getEntitiesOfClass(LucidEntity.class, new AABB(pos).inflate(NATURAL_SPAWN_LIMIT_RADIUS), lucid -> lucid.isAlive()).size() < NATURAL_SPAWN_LIMIT
&& pos.getY() <= 250 && pos.getY() <= 250
&& level.isEmptyBlock(pos) && level.isEmptyBlock(pos)
&& level.isEmptyBlock(pos.above()) && level.isEmptyBlock(pos.above())
@@ -571,6 +575,3 @@ public class LucidEntity extends Monster implements GeoEntity {
} }
@@ -1,6 +1,5 @@
package com.craisinlord.antarchy.content.entity.portal; package com.craisinlord.antarchy.content.entity.portal;
import com.craisinlord.antarchy.Antarchy;
import com.craisinlord.antarchy.config.AntarchySettings; import com.craisinlord.antarchy.config.AntarchySettings;
import com.craisinlord.antarchy.content.AntarchyObjects; import com.craisinlord.antarchy.content.AntarchyObjects;
import com.craisinlord.antarchy.content.AntarchySoundEvents; import com.craisinlord.antarchy.content.AntarchySoundEvents;
@@ -181,13 +180,6 @@ public class DimensionalTearEntity extends Entity implements GeoEntity {
DimensionalTearEntity linked = this.findLinkedTear(); DimensionalTearEntity linked = this.findLinkedTear();
if (linked == null || !linked.isAlive()) { if (linked == null || !linked.isAlive()) {
Antarchy.LOGGER.warn(
"[antarchy-dimensional-tear] discarding unlinked tear uuid={} pos=({}, {}, {}) dim={} linkedTearId={} fallback=({}, {}, {}) ageTicks={} lifetimeTicks={}",
this.getUUID(), this.getX(), this.getY(), this.getZ(), this.level().dimension().location(),
this.linkedTearId,
this.linkedFallbackPos.x, this.linkedFallbackPos.y, this.linkedFallbackPos.z,
this.ageTicks, this.lifetimeTicks
);
this.discard(); this.discard();
return; return;
} }
@@ -360,7 +352,6 @@ public class DimensionalTearEntity extends Entity implements GeoEntity {
private void teleportEntity(Entity entity, DimensionalTearEntity destination) { private void teleportEntity(Entity entity, DimensionalTearEntity destination) {
Vec3 motion = entity.getDeltaMovement(); Vec3 motion = entity.getDeltaMovement();
Vec3 startPos = entity.position();
Vec3 exitPos = destination.exitPosition(); Vec3 exitPos = destination.exitPosition();
TELEPORT_COOLDOWNS.put(entity.getUUID(), this.level().getGameTime() + TELEPORT_COOLDOWN_TICKS); TELEPORT_COOLDOWNS.put(entity.getUUID(), this.level().getGameTime() + TELEPORT_COOLDOWN_TICKS);
@@ -374,13 +365,6 @@ public class DimensionalTearEntity extends Entity implements GeoEntity {
entity.hasImpulse = true; entity.hasImpulse = true;
if (entity instanceof LivingEntity living) { if (entity instanceof LivingEntity living) {
int invertedDuration = AntarchySettings.dimensionalTearInvertedDurationTicks(); int invertedDuration = AntarchySettings.dimensionalTearInvertedDurationTicks();
Antarchy.LOGGER.debug(
"[antarchy-dimensional-tear] applying inverted effect targetType={} uuid={} fromTear={} toTear={} startPos=({}, {}, {}) exitPos=({}, {}, {}) dim={} durationTicks={}",
living.getType(), living.getUUID(), this.getUUID(), destination.getUUID(),
startPos.x, startPos.y, startPos.z,
exitPos.x, exitPos.y, exitPos.z,
this.level().dimension().location(), invertedDuration
);
living.addEffect(new MobEffectInstance(AntarchyObjects.INVERTED_EFFECT.get(), invertedDuration, 0)); living.addEffect(new MobEffectInstance(AntarchyObjects.INVERTED_EFFECT.get(), invertedDuration, 0));
} }
this.level().playSound(null, this.blockPosition(), SoundEvents.ENDERMAN_TELEPORT, SoundSource.NEUTRAL, 0.7F, 0.65F + this.random.nextFloat() * 0.2F); this.level().playSound(null, this.blockPosition(), SoundEvents.ENDERMAN_TELEPORT, SoundSource.NEUTRAL, 0.7F, 0.65F + this.random.nextFloat() * 0.2F);
@@ -550,16 +534,6 @@ public class DimensionalTearEntity extends Entity implements GeoEntity {
this.queenManticoreCount = tag.getInt("QueenManticoreCount"); this.queenManticoreCount = tag.getInt("QueenManticoreCount");
this.queenManticoreSpawned = tag.getBoolean("QueenManticoreSpawned"); this.queenManticoreSpawned = tag.getBoolean("QueenManticoreSpawned");
} }
if (!this.level().isClientSide) {
Antarchy.LOGGER.info(
"[antarchy-dimensional-tear] loaded tear uuid={} pos=({}, {}, {}) dim={} linkedTearId={} fallback=({}, {}, {}) ageTicks={} lifetimeTicks={} eventTicks={} nextEventTicks={} pendingEvent={} queenOwner={} queenManticoreCount={} queenManticoreSpawned={}",
this.getUUID(), this.getX(), this.getY(), this.getZ(), this.level().dimension().location(),
this.linkedTearId,
this.linkedFallbackPos.x, this.linkedFallbackPos.y, this.linkedFallbackPos.z,
this.ageTicks, this.lifetimeTicks, this.eventTicks, this.nextEventTicks, this.pendingEvent,
this.queenOwnerId, this.queenManticoreCount, this.queenManticoreSpawned
);
}
} }
@Override @Override
@@ -114,6 +114,7 @@ public class KingEntity extends RoyalBossEntity {
@Nullable @Nullable
private ServerPlayer exileTarget; private ServerPlayer exileTarget;
private int exileTicks; private int exileTicks;
private boolean exileWingAnimationPending;
@Nullable @Nullable
private ServerPlayer titheTarget; private ServerPlayer titheTarget;
private int titheTicks; private int titheTicks;
@@ -364,6 +365,7 @@ public class KingEntity extends RoyalBossEntity {
return; return;
} }
this.tickRoyalPunishment(); this.tickRoyalPunishment();
this.tickPendingExileAnimation();
long gameTime = this.level().getGameTime(); long gameTime = this.level().getGameTime();
this.judgmentCooldowns.entrySet().removeIf(entry -> entry.getValue() <= gameTime); this.judgmentCooldowns.entrySet().removeIf(entry -> entry.getValue() <= gameTime);
this.lastPlayerDamageTime.entrySet().removeIf(entry -> gameTime - entry.getValue() > 600L); this.lastPlayerDamageTime.entrySet().removeIf(entry -> gameTime - entry.getValue() > 600L);
@@ -489,8 +491,25 @@ public class KingEntity extends RoyalBossEntity {
away = away.normalize(); away = away.normalize();
this.exileTarget.setDeltaMovement(away.x * 2.6D, 1.35D, away.z * 2.6D); this.exileTarget.setDeltaMovement(away.x * 2.6D, 1.35D, away.z * 2.6D);
this.exileTarget.hasImpulse = true; this.exileTarget.hasImpulse = true;
this.triggerAnim("wing_action", "wing_gust");
this.playRoyalSound(AntarchySoundEvents.KING_WING_FLAP.get(), 0.82F); this.playRoyalSound(AntarchySoundEvents.KING_WING_FLAP.get(), 0.82F);
this.exileWingAnimationPending = true;
}
}
private void tickPendingExileAnimation() {
if (!this.exileWingAnimationPending
|| !this.attackScheduler.ready("exile_wing_animation", RoyalAttackLane.BODY)) {
return;
}
if (this.beginRoyalAttack("exile_wing_animation", RoyalAttackLane.BODY, 1, 0, 1,
this.animationRecovery(55, 0, 1, 0), new com.craisinlord.antarchy.content.entity.royal.attack.RoyalAttackScheduler.Action() {
@Override
public void onStart() {
KingEntity.this.exileWingAnimationPending = false;
KingEntity.this.triggerAnim("wing_action", "wing_gust");
}
})) {
this.exileWingAnimationPending = false;
} }
} }
@@ -551,6 +570,7 @@ public class KingEntity extends RoyalBossEntity {
} }
this.exileTarget = null; this.exileTarget = null;
this.exileTicks = 0; this.exileTicks = 0;
this.exileWingAnimationPending = false;
} }
private void clearTithe() { private void clearTithe() {
@@ -763,7 +783,7 @@ public class KingEntity extends RoyalBossEntity {
} else if (this.attackScheduler.ready("wing_gust", RoyalAttackLane.BODY) } else if (this.attackScheduler.ready("wing_gust", RoyalAttackLane.BODY)
&& distance <= WING_GUST_RADIUS && (forceFar || this.random.nextInt(3) == 0) && distance <= WING_GUST_RADIUS && (forceFar || this.random.nextInt(3) == 0)
&& this.beginRoyalAttack("wing_gust", RoyalAttackLane.BODY, WING_GUST_COOLDOWN_TICKS, && this.beginRoyalAttack("wing_gust", RoyalAttackLane.BODY, WING_GUST_COOLDOWN_TICKS,
WING_GUST_WINDUP_TICKS, 1, 16, new com.craisinlord.antarchy.content.entity.royal.attack.RoyalAttackScheduler.Action() { WING_GUST_WINDUP_TICKS, 1, this.animationRecovery(55, WING_GUST_WINDUP_TICKS, 1, 16), new com.craisinlord.antarchy.content.entity.royal.attack.RoyalAttackScheduler.Action() {
@Override public void onStart() { @Override public void onStart() {
KingEntity.this.triggerAnim("wing_action", "wing_gust"); KingEntity.this.triggerAnim("wing_action", "wing_gust");
KingEntity.this.playRoyalSound(AntarchySoundEvents.KING_WING_FLAP.get(), 0.85F); KingEntity.this.playRoyalSound(AntarchySoundEvents.KING_WING_FLAP.get(), 0.85F);
@@ -777,7 +797,8 @@ public class KingEntity extends RoyalBossEntity {
&& (forceFar || this.random.nextInt(4) == 0) && (forceFar || this.random.nextInt(4) == 0)
&& this.beginRoyalAttack("royal_muster", RoyalAttackLane.BODY, && this.beginRoyalAttack("royal_muster", RoyalAttackLane.BODY,
AntarchySettings.kingRoyalMusterCooldownTicks(), AntarchySettings.kingRoyalMusterCooldownTicks(),
AntarchySettings.kingRoyalMusterWindupTicks(), 1, 20, AntarchySettings.kingRoyalMusterWindupTicks(), 1,
this.animationRecovery(59, AntarchySettings.kingRoyalMusterWindupTicks(), 1, 20),
new com.craisinlord.antarchy.content.entity.royal.attack.RoyalAttackScheduler.Action() { new com.craisinlord.antarchy.content.entity.royal.attack.RoyalAttackScheduler.Action() {
@Override public void onStart() { @Override public void onStart() {
KingEntity.this.triggerAnim("body_action", "minion_spawn"); KingEntity.this.triggerAnim("body_action", "minion_spawn");
@@ -971,6 +992,15 @@ public class KingEntity extends RoyalBossEntity {
} }
private void tickDecree(ServerLevel level, LivingEntity target) { private void tickDecree(ServerLevel level, LivingEntity target) {
if (!(target instanceof ServerPlayer player) || !target.isAlive() || target.level() != level) {
if (this.activeDecree != null) {
this.endDecree(target);
}
this.activeDecree = null;
this.activeDecreeTicks = 0;
this.decreeRetreatPressure = false;
return;
}
if (this.activeDecree == null) { if (this.activeDecree == null) {
if (this.decreeCooldownTicks-- > 0) { if (this.decreeCooldownTicks-- > 0) {
return; return;
@@ -985,7 +1015,7 @@ public class KingEntity extends RoyalBossEntity {
this.playRoyalSound(AntarchySoundEvents.KING_DECREE_CAST.get(), 0.9F + this.random.nextFloat() * 0.15F); 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.playRoyalSound(AntarchySoundEvents.KING_ROAR.get(), 0.8F + this.random.nextFloat() * 0.12F);
this.projectRoyalSound(AntarchySoundEvents.KING_DECREE.get(), 4.0F, 1.0F, target); this.projectRoyalSound(AntarchySoundEvents.KING_DECREE.get(), 4.0F, 1.0F, target);
if (target instanceof ServerPlayer player) this.sendDecreeTitle(player); this.sendDecreeTitle(player);
} }
int countdown = this.activeDecree.countdownTicks(target); int countdown = this.activeDecree.countdownTicks(target);
if (countdown > 0 && countdown <= 100 && this.tickCount % 20 == 0) { if (countdown > 0 && countdown <= 100 && this.tickCount % 20 == 0) {
@@ -76,6 +76,7 @@ public class QueenEntity extends RoyalBossEntity {
private int blackHoleCooldownTicks; private int blackHoleCooldownTicks;
private final int[] dreamFireballCooldownTicks = new int[3]; private final int[] dreamFireballCooldownTicks = new int[3];
private int idleWanderCooldownTicks; private int idleWanderCooldownTicks;
private boolean manticoreAnimationPending;
private final RoyalEffectController royalEffects = new RoyalEffectController(); private final RoyalEffectController royalEffects = new RoyalEffectController();
private final Map<UUID, Vec3> frozenVelocities = new HashMap<>(); private final Map<UUID, Vec3> frozenVelocities = new HashMap<>();
private long naturalTrailSiteId = Long.MIN_VALUE; private long naturalTrailSiteId = Long.MIN_VALUE;
@@ -229,6 +230,7 @@ public class QueenEntity extends RoyalBossEntity {
this.tickIdleWander(); this.tickIdleWander();
} }
this.tickManticoreSummon(); this.tickManticoreSummon();
this.tickPendingManticoreAnimation();
this.tickQueenAbilities(); this.tickQueenAbilities();
this.tickDreamFireballs(); this.tickDreamFireballs();
this.tickFinalAcceleration(); this.tickFinalAcceleration();
@@ -370,13 +372,30 @@ public class QueenEntity extends RoyalBossEntity {
? this.cooldown(Math.max(20, AntarchySettings.queenManticoreSummonCooldownTicks())) ? this.cooldown(Math.max(20, AntarchySettings.queenManticoreSummonCooldownTicks()))
: FAILED_SUMMON_RETRY_TICKS; : FAILED_SUMMON_RETRY_TICKS;
if (spawned > 0) { if (spawned > 0) {
this.triggerAnim("body_action", "minion_spawn"); this.manticoreAnimationPending = true;
this.playRoyalSound(AntarchySoundEvents.QUEEN_ROAR.get(), 0.8F + this.random.nextFloat() * 0.12F); this.playRoyalSound(AntarchySoundEvents.QUEEN_ROAR.get(), 0.8F + this.random.nextFloat() * 0.12F);
serverLevel.sendParticles(ParticleTypes.PORTAL, this.getX(), this.getY() + 2.0D, this.getZ(), serverLevel.sendParticles(ParticleTypes.PORTAL, this.getX(), this.getY() + 2.0D, this.getZ(),
36, 3.0D, 2.0D, 3.0D, 0.15D); 36, 3.0D, 2.0D, 3.0D, 0.15D);
} }
} }
private void tickPendingManticoreAnimation() {
if (!this.manticoreAnimationPending
|| !this.attackScheduler.ready("manticore_summon_animation", RoyalAttackLane.BODY)) {
return;
}
if (this.beginRoyalAttack("manticore_summon_animation", RoyalAttackLane.BODY, 1, 0, 1,
this.animationRecovery(59, 0, 1, 0),
new com.craisinlord.antarchy.content.entity.royal.attack.RoyalAttackScheduler.Action() {
@Override public void onStart() {
QueenEntity.this.manticoreAnimationPending = false;
QueenEntity.this.triggerAnim("body_action", "minion_spawn");
}
})) {
this.manticoreAnimationPending = false;
}
}
private int spawnManticoreTears(ServerLevel level, LivingEntity target, int count) { private int spawnManticoreTears(ServerLevel level, LivingEntity target, int count) {
int pairs = Math.max(1, Math.min(3, (count + 1) / 2)); int pairs = Math.max(1, Math.min(3, (count + 1) / 2));
int created = 0; int created = 0;
@@ -387,18 +406,26 @@ public class QueenEntity extends RoyalBossEntity {
towardTarget = towardTarget.normalize(); towardTarget = towardTarget.normalize();
Vec3 behindTarget = target.position().add(towardTarget.scale(8.0D)); Vec3 behindTarget = target.position().add(towardTarget.scale(8.0D));
Vec3 lateral = new Vec3(-towardTarget.z, 0.0D, towardTarget.x); Vec3 lateral = new Vec3(-towardTarget.z, 0.0D, towardTarget.x);
float tearYaw = (float) Math.toDegrees(Math.atan2(-towardTarget.x, towardTarget.z));
double vertical = AntarchyGravityApi.isGravityInverted(this) ? -2.0D : 2.0D; double vertical = AntarchyGravityApi.isGravityInverted(this) ? -2.0D : 2.0D;
for (int i = 0; i < pairs; i++) { for (int i = 0; i < pairs; i++) {
double angle = this.random.nextDouble() * Mth.TWO_PI; double forwardScatter = 3.0D + this.random.nextDouble() * 9.0D;
double distance = Math.max(8.0D, AntarchySettings.queenManticoreSummonRange() * 0.65D); double lateralScatter = (this.random.nextDouble() * 2.0D - 1.0D) * 11.0D;
double spread = (i - (pairs - 1) * 0.5D) * 5.0D; double heightScatter = (this.random.nextDouble() * 2.0D - 1.0D) * 3.0D;
Vec3 pairCenter = behindTarget.add(lateral.scale(spread)).add(0.0D, vertical, 0.0D); Vec3 pairCenter = behindTarget
Vec3 firstPos = pairCenter.add(lateral.scale(3.0D)); .add(towardTarget.scale(forwardScatter))
Vec3 secondPos = pairCenter.add(lateral.scale(-3.0D)); .add(lateral.scale(lateralScatter))
.add(0.0D, vertical + heightScatter, 0.0D);
double pairAngle = this.random.nextDouble() * Mth.TWO_PI;
Vec3 pairDirection = towardTarget.scale(Math.cos(pairAngle))
.add(lateral.scale(Math.sin(pairAngle)));
double pairSeparation = 3.5D + this.random.nextDouble() * 3.0D;
Vec3 firstPos = pairCenter.add(pairDirection.scale(pairSeparation));
Vec3 secondPos = pairCenter.add(pairDirection.scale(-pairSeparation));
DimensionalTearEntity first = DimensionalTearEntity.createQueenManticoreTear(level, firstPos, DimensionalTearEntity first = DimensionalTearEntity.createQueenManticoreTear(level, firstPos,
(float) Math.toDegrees(angle), 240, this.getUUID(), Math.min(3, count)); tearYaw, 240, this.getUUID(), Math.min(3, count));
DimensionalTearEntity second = DimensionalTearEntity.createQueenManticoreTear(level, secondPos, DimensionalTearEntity second = DimensionalTearEntity.createQueenManticoreTear(level, secondPos,
(float) Math.toDegrees(angle + Math.PI), 240, this.getUUID(), Math.min(3, count)); tearYaw, 240, this.getUUID(), Math.min(3, count));
first.linkTo(second); first.linkTo(second);
second.linkTo(first); second.linkTo(first);
level.addFreshEntity(first); level.addFreshEntity(first);
@@ -446,8 +473,12 @@ public class QueenEntity extends RoyalBossEntity {
default -> ACCELERATION_COOLDOWN; default -> ACCELERATION_COOLDOWN;
}; };
RoyalAttackLane lane = "black_hole".equals(selected) ? RoyalAttackLane.CENTER_HEAD : RoyalAttackLane.HAZARD; RoyalAttackLane lane = "black_hole".equals(selected) ? RoyalAttackLane.CENTER_HEAD : RoyalAttackLane.HAZARD;
int actionWindup = 10;
int actionRecovery = "momentum_lock".equals(selected)
? this.animationRecovery(55, actionWindup, 1, 14)
: this.animationRecovery(25, actionWindup, 1, 14);
if (!this.beginRoyalAttack(selected, lane, this.cooldown(cooldown), if (!this.beginRoyalAttack(selected, lane, this.cooldown(cooldown),
10, 1, 14, new com.craisinlord.antarchy.content.entity.royal.attack.RoyalAttackScheduler.Action() { actionWindup, 1, actionRecovery, new com.craisinlord.antarchy.content.entity.royal.attack.RoyalAttackScheduler.Action() {
@Override public void onStart() { @Override public void onStart() {
if ("gravity_stomp".equals(selected)) { if ("gravity_stomp".equals(selected)) {
QueenEntity.this.triggerAnim("body_action", "stomp"); QueenEntity.this.triggerAnim("body_action", "stomp");
@@ -93,6 +93,10 @@ public class RoyalBoltEntity extends AbstractHurtingProjectile {
if (target == owner || (owner != null && target == owner.getVehicle())) { if (target == owner || (owner != null && target == owner.getVehicle())) {
return; return;
} }
if (owner instanceof RoyalMountEntity royal && target instanceof RoyalMountEntity other
&& royal.isTame() && other.isTame()) {
return;
}
float damage = (float) AntarchySettings.royalBoltDamage(); float damage = (float) AntarchySettings.royalBoltDamage();
target.hurt(this.damageSources().mobProjectile(this, owner instanceof LivingEntity le ? le : null), damage); target.hurt(this.damageSources().mobProjectile(this, owner instanceof LivingEntity le ? le : null), damage);
if (target instanceof LivingEntity living) { if (target instanceof LivingEntity living) {
@@ -64,7 +64,7 @@ import software.bernie.geckolib.util.GeckoLibUtil;
public abstract class RoyalBossEntity extends Monster implements GeoEntity, MultipartEntityOwner { public abstract class RoyalBossEntity extends Monster implements GeoEntity, MultipartEntityOwner {
public static final float MODEL_RENDER_SCALE = 2.0F; public static final float MODEL_RENDER_SCALE = 2.0F;
public static final float GAMEPLAY_WIDTH = 30.0F; public static final float GAMEPLAY_WIDTH = 28.0F;
public static final float GAMEPLAY_HEIGHT = 15.0F; public static final float GAMEPLAY_HEIGHT = 15.0F;
public enum Phase { public enum Phase {
@@ -515,6 +515,7 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
} }
controller.tick( controller.tick(
this.beamAnchor(head), this.beamAnchor(head),
this.beamDirection(head),
beamTarget, beamTarget,
this.royalBeamSettings(), this.royalBeamSettings(),
this.royalBeamTerrainMode(head), this.royalBeamTerrainMode(head),
@@ -535,7 +536,8 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
if (!head.readyToAttack() || this.attackScheduler.laneBusy(this.headLane(head)) if (!head.readyToAttack() || this.attackScheduler.laneBusy(this.headLane(head))
|| target == null || !this.canAttack(target) || target == null || !this.canAttack(target)
|| this.distanceTo(target) < this.royalBeamMinimumRange() || this.distanceTo(target) < this.royalBeamMinimumRange()
|| this.headWithinBiteReach(head, target)) { || this.headWithinBiteReach(head, target)
|| !this.targetInBeamLine(head, target)) {
continue; continue;
} }
RoyalBeamSettings beamSettings = this.royalBeamSettings(); RoyalBeamSettings beamSettings = this.royalBeamSettings();
@@ -588,7 +590,6 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
int index = head.slot().ordinal(); int index = head.slot().ordinal();
head.setBeamActive(false); head.setBeamActive(false);
head.stopShoot(); head.stopShoot();
this.stopTriggeredAnim(head.slot().controllerName(), null);
this.beamControllers[index].stop(); this.beamControllers[index].stop();
this.entityData.set(BEAM_ACTIVE[index], false); this.entityData.set(BEAM_ACTIVE[index], false);
this.entityData.set(BEAM_ELEMENT[index], RoyalBeamElement.GENERIC.ordinal()); this.entityData.set(BEAM_ELEMENT[index], RoyalBeamElement.GENERIC.ordinal());
@@ -656,7 +657,7 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
? new Vec3(-dx / horizontalDistance, 0.0D, -dz / horizontalDistance) ? new Vec3(-dx / horizontalDistance, 0.0D, -dz / horizontalDistance)
: this.getViewVector(1.0F).multiply(-1.0D, 0.0D, -1.0D).normalize(); : this.getViewVector(1.0F).multiply(-1.0D, 0.0D, -1.0D).normalize();
double standoff = Math.max(this.getBbWidth() * 0.55D, double standoff = Math.max(this.getBbWidth() * 0.55D,
BEAM_MUZZLE_FORWARD - this.biteReach() * 0.65D); BEAM_MUZZLE_FORWARD - this.biteReach() * 0.65D + 1.0D);
Vec3 wanted = target.position().add(awayFromTarget.scale(standoff)); Vec3 wanted = target.position().add(awayFromTarget.scale(standoff));
boolean atStandoff = this.position().multiply(1.0D, 0.0D, 1.0D) boolean atStandoff = this.position().multiply(1.0D, 0.0D, 1.0D)
.distanceToSqr(wanted.multiply(1.0D, 0.0D, 1.0D)) <= 4.0D; .distanceToSqr(wanted.multiply(1.0D, 0.0D, 1.0D)) <= 4.0D;
@@ -827,6 +828,10 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
return this.attackScheduler.start(id, lane, cooldownTicks, windupTicks, activeTicks, protectedRecovery, action); return this.attackScheduler.start(id, lane, cooldownTicks, windupTicks, activeTicks, protectedRecovery, action);
} }
protected int animationRecovery(int animationTicks, int windupTicks, int activeTicks, int recoveryTicks) {
return Math.max(recoveryTicks, animationTicks - Math.max(0, windupTicks) - Math.max(1, activeTicks));
}
private void applyBiteHit(RoyalHead head) { private void applyBiteHit(RoyalHead head) {
if (this.level().isClientSide) { if (this.level().isClientSide) {
return; return;
@@ -889,6 +894,26 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
return beamAnchor(head.slot(), this.getX(), this.getY(), this.getZ(), this.getYRot()); return beamAnchor(head.slot(), this.getX(), this.getY(), this.getZ(), this.getYRot());
} }
protected Vec3 beamDirection(RoyalHead head) {
float yaw = this.getYRot() * Mth.DEG_TO_RAD;
Vec3 localDirection = new Vec3(-Math.sin(yaw), 0.0D, Math.cos(yaw));
return AntarchyGravityRotationUtil.vecPlayerToWorld(localDirection,
AntarchyGravityApi.getGravityDirection(this)).normalize();
}
private boolean targetInBeamLine(RoyalHead head, LivingEntity target) {
Vec3 origin = this.beamAnchor(head);
Vec3 direction = this.beamDirection(head);
Vec3 toTarget = target.getEyePosition().subtract(origin);
double forwardDistance = toTarget.dot(direction);
if (forwardDistance <= 0.0D) {
return false;
}
Vec3 perpendicular = toTarget.subtract(direction.scale(forwardDistance));
double tolerance = Math.max(1.5D, target.getBbWidth() * 1.5D);
return perpendicular.lengthSqr() <= tolerance * tolerance;
}
private Vec3 beamAnchor(RoyalHead.Slot slot, double x, double y, double z, float yaw) { private Vec3 beamAnchor(RoyalHead.Slot slot, double x, double y, double z, float yaw) {
double yawRadians = yaw * Mth.DEG_TO_RAD; double yawRadians = yaw * Mth.DEG_TO_RAD;
Vec3 forward = new Vec3(-Math.sin(yawRadians), 0.0D, Math.cos(yawRadians)); Vec3 forward = new Vec3(-Math.sin(yawRadians), 0.0D, Math.cos(yawRadians));
@@ -936,6 +961,9 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
this.setRoyalFlying(false); this.setRoyalFlying(false);
this.getNavigation().stop(); this.getNavigation().stop();
this.setDeltaMovement(Vec3.ZERO); this.setDeltaMovement(Vec3.ZERO);
this.setXRot(0.0F);
this.setYHeadRot(this.getYRot());
this.yBodyRot = this.getYRot();
} }
super.die(damageSource); super.die(damageSource);
} }
@@ -967,6 +995,9 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
protected void tickDeath() { protected void tickDeath() {
this.deathTime++; this.deathTime++;
this.setNoGravity(false); this.setNoGravity(false);
this.setXRot(0.0F);
this.setYHeadRot(this.getYRot());
this.yBodyRot = this.getYRot();
Vec3 movement = this.getDeltaMovement(); Vec3 movement = this.getDeltaMovement();
if (this.onGround()) { if (this.onGround()) {
this.setDeltaMovement(movement.x * 0.6D, 0.0D, movement.z * 0.6D); this.setDeltaMovement(movement.x * 0.6D, 0.0D, movement.z * 0.6D);
@@ -445,6 +445,11 @@ public abstract class RoyalMountEntity extends TamableAnimal implements GeoEntit
return; return;
} }
LivingEntity target = this.getTarget();
if (target != null && !this.canAttack(target)) {
this.setTarget(null);
}
if (this.spitCooldown > 0) { if (this.spitCooldown > 0) {
this.spitCooldown--; this.spitCooldown--;
} }
@@ -625,11 +630,17 @@ public abstract class RoyalMountEntity extends TamableAnimal implements GeoEntit
if (target == null && this.getControllingPassenger() instanceof Player) { if (target == null && this.getControllingPassenger() instanceof Player) {
target = this.frontTarget(); target = this.frontTarget();
} }
if (target != null && target.distanceTo(this) < 5.0D) { if (target != null && this.canAttack(target) && target.distanceTo(this) < 5.0D) {
target.hurt(this.damageSources().mobAttack(this), (float) this.getAttributeValue(Attributes.ATTACK_DAMAGE)); target.hurt(this.damageSources().mobAttack(this), (float) this.getAttributeValue(Attributes.ATTACK_DAMAGE));
} }
} }
@Override
public boolean canAttack(LivingEntity target) {
return !(target instanceof RoyalMountEntity other && this.isTame() && other.isTame())
&& super.canAttack(target);
}
@Nullable @Nullable
private LivingEntity frontTarget() { private LivingEntity frontTarget() {
Vec3 look = this.getLookAngle(); Vec3 look = this.getLookAngle();
@@ -640,6 +651,9 @@ public abstract class RoyalMountEntity extends TamableAnimal implements GeoEntit
if (candidate == this || candidate == this.getControllingPassenger() || candidate == this.getOwner()) { if (candidate == this || candidate == this.getControllingPassenger() || candidate == this.getOwner()) {
continue; continue;
} }
if (!this.canAttack(candidate)) {
continue;
}
Vec3 to = candidate.getEyePosition().subtract(eye); Vec3 to = candidate.getEyePosition().subtract(eye);
double dist = to.length(); double dist = to.length();
if (dist > 6.0D || dist < 1.0E-3D) { if (dist > 6.0D || dist < 1.0E-3D) {
@@ -17,7 +17,7 @@ public class RoyalMountSpitGoal extends Goal {
return false; return false;
} }
LivingEntity target = this.mount.getTarget(); LivingEntity target = this.mount.getTarget();
if (target == null || !target.isAlive()) { if (target == null || !target.isAlive() || !this.mount.canAttack(target)) {
return false; return false;
} }
double distance = this.mount.distanceTo(target); double distance = this.mount.distanceTo(target);
@@ -32,7 +32,7 @@ public class RoyalMountSpitGoal extends Goal {
@Override @Override
public void tick() { public void tick() {
LivingEntity target = this.mount.getTarget(); LivingEntity target = this.mount.getTarget();
if (target == null) { if (target == null || !this.mount.canAttack(target)) {
return; return;
} }
this.mount.getLookControl().setLookAt(target, 30.0F, 30.0F); this.mount.getLookControl().setLookAt(target, 30.0F, 30.0F);
@@ -77,6 +77,17 @@ public final class RoyalBeamController {
RoyalBeamSettings settings, RoyalBeamSettings settings,
RoyalBeamTerrainMode terrainMode, RoyalBeamTerrainMode terrainMode,
Consumer<@Nullable Vec3> syncedEndPosition Consumer<@Nullable Vec3> syncedEndPosition
) {
this.tick(shootFrom, owner.getLookAngle(), target, settings, terrainMode, syncedEndPosition);
}
public void tick(
Vec3 shootFrom,
Vec3 shootDirection,
@Nullable LivingEntity target,
RoyalBeamSettings settings,
RoyalBeamTerrainMode terrainMode,
Consumer<@Nullable Vec3> syncedEndPosition
) { ) {
if (this.beamTicks <= 0) { if (this.beamTicks <= 0) {
syncedEndPosition.accept(null); syncedEndPosition.accept(null);
@@ -100,11 +111,11 @@ public final class RoyalBeamController {
return; return;
} }
Vec3 direction = this.serverTarget.subtract(shootFrom).normalize(); Vec3 direction = shootDirection.lengthSqr() < 1.0E-7D
if (direction.lengthSqr() < 1.0E-7D) { ? this.owner.getLookAngle().normalize()
direction = this.owner.getLookAngle(); : shootDirection.normalize();
} double committedDistance = Math.min(settings.range(), Math.max(settings.pathStep(),
double committedDistance = Math.min(settings.range(), shootFrom.distanceTo(this.serverTarget)); this.serverTarget.subtract(shootFrom).dot(direction)));
double travelProgress = Mth.clamp(this.beamAgeTicks / (double) settings.travelTicks(), 0.0D, 1.0D); double travelProgress = Mth.clamp(this.beamAgeTicks / (double) settings.travelTicks(), 0.0D, 1.0D);
double currentRange = Math.max(settings.pathStep(), committedDistance * travelProgress); double currentRange = Math.max(settings.pathStep(), committedDistance * travelProgress);
Vec3 clipEnd = shootFrom.add(direction.scale(currentRange)); Vec3 clipEnd = shootFrom.add(direction.scale(currentRange));
@@ -106,7 +106,6 @@ public class WindVortexEntity extends Entity {
public WindVortexEntity(EntityType<? extends WindVortexEntity> entityType, Level level) { public WindVortexEntity(EntityType<? extends WindVortexEntity> entityType, Level level) {
super(entityType, level); super(entityType, level);
this.noCulling = true;
} }
public static WindVortexEntity create(Level level, EntityType<? extends WindVortexEntity> type, Vec3 position, public static WindVortexEntity create(Level level, EntityType<? extends WindVortexEntity> type, Vec3 position,
@@ -379,12 +378,6 @@ public class WindVortexEntity extends Entity {
return; return;
} }
for (ServerPlayer player : serverLevel.players()) {
if (area.intersects(player.getBoundingBox())) {
this.captureEntity(player, height, basis, mode);
}
}
Iterator<Map.Entry<UUID, Double>> iterator = this.carriedProgress.entrySet().iterator(); Iterator<Map.Entry<UUID, Double>> iterator = this.carriedProgress.entrySet().iterator();
while (iterator.hasNext()) { while (iterator.hasNext()) {
Map.Entry<UUID, Double> entry = iterator.next(); Map.Entry<UUID, Double> entry = iterator.next();
@@ -62,6 +62,11 @@ public final class TimeDilationApi {
return getRate(entity) < TimeDilationMath.NORMAL_RATE; return getRate(entity) < TimeDilationMath.NORMAL_RATE;
} }
public static double getVehicleRate(Entity entity) {
Entity vehicle = entity.getVehicle();
return vehicle == null ? TimeDilationMath.NORMAL_RATE : getRate(vehicle);
}
public static void enterRotationBypass() { public static void enterRotationBypass() {
ROTATION_BYPASS_DEPTH.set(ROTATION_BYPASS_DEPTH.get() + 1); ROTATION_BYPASS_DEPTH.set(ROTATION_BYPASS_DEPTH.get() + 1);
} }
@@ -112,6 +117,10 @@ public final class TimeDilationApi {
SYNCED_CLIENT_RATES.clear(); SYNCED_CLIENT_RATES.clear();
} }
public static boolean hasSyncedClientRates() {
return !SYNCED_CLIENT_RATES.isEmpty();
}
public static void syncEntityRate(Entity entity, double rate) { public static void syncEntityRate(Entity entity, double rate) {
syncDispatcher.accept(entity, TimeDilationMath.clampRate(rate)); syncDispatcher.accept(entity, TimeDilationMath.clampRate(rate));
} }
@@ -114,6 +114,7 @@ public class TimeDilationFieldEntity extends Entity {
if (this.level().isClientSide) { if (this.level().isClientSide) {
return; return;
} }
TimeDilationManager.registerActiveField(this);
if (this.anchorId != null && this.level() instanceof ServerLevel level) { if (this.anchorId != null && this.level() instanceof ServerLevel level) {
Entity anchor = level.getEntity(this.anchorId); Entity anchor = level.getEntity(this.anchorId);
if (anchor != null && anchor.isAlive()) { if (anchor != null && anchor.isAlive()) {
@@ -19,14 +19,29 @@ import com.craisinlord.antarchy.content.effect.RoyalEffectHooks;
public final class TimeDilationManager { public final class TimeDilationManager {
private static final Map<ServerLevel, TrackingState> TRACKING = new WeakHashMap<>(); private static final Map<ServerLevel, TrackingState> TRACKING = new WeakHashMap<>();
private static final Map<ServerLevel, Set<TimeDilationFieldEntity>> ACTIVE_FIELDS = new WeakHashMap<>();
private TimeDilationManager() { private TimeDilationManager() {
} }
public static void tickServer(MinecraftServer server) { public static void tickServer(MinecraftServer server) {
for (ServerLevel level : server.getAllLevels()) { for (ServerLevel level : server.getAllLevels()) {
List<TimeDilationFieldEntity> fields = collectFields(level); Set<TimeDilationFieldEntity> activeFields = ACTIVE_FIELDS.get(level);
TrackingState tracking = TRACKING.computeIfAbsent(level, ignored -> new TrackingState()); if (activeFields != null) {
activeFields.removeIf(field -> !field.isAlive() || field.isRemoved());
if (activeFields.isEmpty()) {
ACTIVE_FIELDS.remove(level);
activeFields = null;
}
}
TrackingState tracking = TRACKING.get(level);
if (activeFields == null && (tracking == null || tracking.entities.isEmpty())) {
continue;
}
List<TimeDilationFieldEntity> fields = activeFields == null ? List.of() : List.copyOf(activeFields);
if (tracking == null) {
tracking = TRACKING.computeIfAbsent(level, ignored -> new TrackingState());
}
discoverFieldEntities(level, fields, tracking); discoverFieldEntities(level, fields, tracking);
syncFieldSnapshots(level, fields, tracking); syncFieldSnapshots(level, fields, tracking);
updateEntities(level, fields, tracking); updateEntities(level, fields, tracking);
@@ -39,6 +54,12 @@ public final class TimeDilationManager {
} }
} }
public static void registerActiveField(TimeDilationFieldEntity field) {
if (field.level() instanceof ServerLevel level) {
ACTIVE_FIELDS.computeIfAbsent(level, ignored -> Collections.newSetFromMap(new IdentityHashMap<>())).add(field);
}
}
public static void trackPotentiallyAffected(Entity entity) { public static void trackPotentiallyAffected(Entity entity) {
if (!(entity.level() instanceof ServerLevel level) || entity instanceof TimeDilationFieldEntity) { if (!(entity.level() instanceof ServerLevel level) || entity instanceof TimeDilationFieldEntity) {
return; return;
@@ -46,6 +67,7 @@ public final class TimeDilationManager {
if (entity instanceof TimeDilationEntityAccess access if (entity instanceof TimeDilationEntityAccess access
&& Math.abs(access.antarchy$getTimeDilationRate() - TimeDilationMath.NORMAL_RATE) < 0.001D && Math.abs(access.antarchy$getTimeDilationRate() - TimeDilationMath.NORMAL_RATE) < 0.001D
&& Math.abs(access.antarchy$getInheritedTimeDilationRate() - TimeDilationMath.NORMAL_RATE) < 0.001D && Math.abs(access.antarchy$getInheritedTimeDilationRate() - TimeDilationMath.NORMAL_RATE) < 0.001D
&& Math.abs(TimeDilationApi.getVehicleRate(entity) - TimeDilationMath.NORMAL_RATE) < 0.001D
&& (!(entity instanceof net.minecraft.world.entity.LivingEntity living) && (!(entity instanceof net.minecraft.world.entity.LivingEntity living)
|| ((RoyalEffectHooks.dilatedHolder() == null || !living.hasEffect(RoyalEffectHooks.dilatedHolder())) || ((RoyalEffectHooks.dilatedHolder() == null || !living.hasEffect(RoyalEffectHooks.dilatedHolder()))
&& (RoyalEffectHooks.contractedHolder() == null || !living.hasEffect(RoyalEffectHooks.contractedHolder()))))) { && (RoyalEffectHooks.contractedHolder() == null || !living.hasEffect(RoyalEffectHooks.contractedHolder()))))) {
@@ -54,6 +76,32 @@ public final class TimeDilationManager {
TRACKING.computeIfAbsent(level, ignored -> new TrackingState()).entities.add(entity); TRACKING.computeIfAbsent(level, ignored -> new TrackingState()).entities.add(entity);
} }
public static void resyncPersistentEffects(ServerPlayer player) {
if (!(player instanceof TimeDilationEntityAccess access)) {
return;
}
double targetRate = TimeDilationMath.NORMAL_RATE;
if (RoyalEffectHooks.dilatedHolder() != null) {
var effect = player.getEffect(RoyalEffectHooks.dilatedHolder());
if (effect != null) {
targetRate = Math.min(targetRate, DilatedMobEffect.rateForAmplifier(effect.getAmplifier()));
}
}
if (RoyalEffectHooks.contractedHolder() != null) {
var effect = player.getEffect(RoyalEffectHooks.contractedHolder());
if (effect != null && targetRate >= TimeDilationMath.NORMAL_RATE) {
targetRate = Math.max(targetRate, ContractedMobEffect.rateForAmplifier(effect.getAmplifier()));
}
}
if (Math.abs(targetRate - TimeDilationMath.NORMAL_RATE) >= 0.001D) {
access.antarchy$setTimeDilationRate(targetRate);
TimeDilationApi.syncEntityRate(player, targetRate);
}
trackPotentiallyAffected(player);
}
private static void syncFieldSnapshots(ServerLevel level, List<TimeDilationFieldEntity> fields, TrackingState tracking) { private static void syncFieldSnapshots(ServerLevel level, List<TimeDilationFieldEntity> fields, TrackingState tracking) {
List<TimeDilationFieldSnapshot> snapshots = fields.stream() List<TimeDilationFieldSnapshot> snapshots = fields.stream()
.filter(field -> !field.isChronosphere()) .filter(field -> !field.isChronosphere())
@@ -71,16 +119,6 @@ public final class TimeDilationManager {
} }
} }
private static List<TimeDilationFieldEntity> collectFields(ServerLevel level) {
List<TimeDilationFieldEntity> fields = new ArrayList<>();
for (Entity entity : level.getAllEntities()) {
if (entity instanceof TimeDilationFieldEntity field && field.isAlive()) {
fields.add(field);
}
}
return fields;
}
private static void discoverFieldEntities(ServerLevel level, List<TimeDilationFieldEntity> fields, TrackingState tracking) { private static void discoverFieldEntities(ServerLevel level, List<TimeDilationFieldEntity> fields, TrackingState tracking) {
for (TimeDilationFieldEntity field : fields) { for (TimeDilationFieldEntity field : fields) {
double radius = field.influenceRadius(); double radius = field.influenceRadius();
@@ -128,6 +166,12 @@ public final class TimeDilationManager {
} }
} }
double inheritedRate = access.antarchy$getInheritedTimeDilationRate(); double inheritedRate = access.antarchy$getInheritedTimeDilationRate();
double vehicleRate = TimeDilationApi.getVehicleRate(entity);
if (vehicleRate < TimeDilationMath.NORMAL_RATE) {
inheritedRate = Math.min(inheritedRate, vehicleRate);
} else {
inheritedRate = Math.max(inheritedRate, vehicleRate);
}
if (entity instanceof Projectile projectile && projectile.getOwner() != null) { if (entity instanceof Projectile projectile && projectile.getOwner() != null) {
double ownerRate = TimeDilationApi.getRate(projectile.getOwner()); double ownerRate = TimeDilationApi.getRate(projectile.getOwner());
inheritedRate = ownerRate < TimeDilationMath.NORMAL_RATE inheritedRate = ownerRate < TimeDilationMath.NORMAL_RATE
@@ -18,6 +18,8 @@ import net.minecraft.world.level.material.FluidState;
import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.Vec3;
import net.minecraft.world.phys.shapes.VoxelShape; import net.minecraft.world.phys.shapes.VoxelShape;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
@@ -35,6 +37,14 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
* Fixes move/landing logic that assumes down is always down. * Fixes move/landing logic that assumes down is always down.
*/ */
public abstract class EntityMoveMixin { public abstract class EntityMoveMixin {
@Unique
private static final Logger ANTARCHY_STEP_LOGGER = LoggerFactory.getLogger("Antarchy/GravityStep");
@Unique
private boolean antarchy$loggedInvertedCollide;
@Unique
private boolean antarchy$loggedHorizontalBlock;
@Unique
private boolean antarchy$loggedInvertedMove;
@Unique @Unique
private double antarchy$lastWorldMoveY = 0.0; private double antarchy$lastWorldMoveY = 0.0;
@Unique @Unique
@@ -49,6 +59,12 @@ public abstract class EntityMoveMixin {
Entity entity = (Entity) (Object) this; Entity entity = (Entity) (Object) this;
this.antarchy$moveStartBox = entity.getBoundingBox(); this.antarchy$moveStartBox = entity.getBoundingBox();
this.antarchy$lastPlayerMoveY = movement.y; this.antarchy$lastPlayerMoveY = movement.y;
if (AntarchyGravityApi.isGravityInverted(entity) && !this.antarchy$loggedInvertedMove) {
this.antarchy$loggedInvertedMove = true;
ANTARCHY_STEP_LOGGER.warn(
"Inverted move entered: entity={} movement={} noPhysics={} onGround={} box={}",
entity.getClass().getSimpleName(), movement, entity.noPhysics, entity.onGround(), entity.getBoundingBox());
}
} }
@ModifyVariable(method = "move", at = @At("HEAD"), ordinal = 0, argsOnly = true) @ModifyVariable(method = "move", at = @At("HEAD"), ordinal = 0, argsOnly = true)
@@ -149,6 +165,13 @@ public abstract class EntityMoveMixin {
@Inject(method = "collide", at = @At("HEAD")) @Inject(method = "collide", at = @At("HEAD"))
private void antarchy$captureCollideWorldMovement(Vec3 movement, CallbackInfoReturnable<Vec3> cir) { private void antarchy$captureCollideWorldMovement(Vec3 movement, CallbackInfoReturnable<Vec3> cir) {
this.antarchy$collideWorldMovement = movement; this.antarchy$collideWorldMovement = movement;
Entity entity = (Entity) (Object) this;
if (AntarchyGravityApi.isGravityInverted(entity) && !this.antarchy$loggedInvertedCollide) {
this.antarchy$loggedInvertedCollide = true;
ANTARCHY_STEP_LOGGER.warn(
"Inverted collide entered: entity={} movement={} onGround={} maxUpStep={} box={}",
entity.getClass().getSimpleName(), movement, entity.onGround(), entity.maxUpStep(), entity.getBoundingBox());
}
} }
/* /*
@@ -239,6 +262,36 @@ public abstract class EntityMoveMixin {
return AntarchyGravityRotationUtil.vecWorldToPlayer(worldResult, direction); return AntarchyGravityRotationUtil.vecWorldToPlayer(worldResult, direction);
} }
/*
* Vanilla uses collideWithShapes directly for each step-height candidate,
* bypassing collideBoundingBox. At this point collide() is operating in the
* gravity-local frame, so leaving this call untouched makes an inverted
* entity test the candidate in the opposite horizontal and vertical world
* directions. The candidate consequently never beats the blocked movement.
*/
@WrapOperation(
method = "collide",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/world/entity/Entity;collideWithShapes(Lnet/minecraft/world/phys/Vec3;Lnet/minecraft/world/phys/AABB;Ljava/util/List;)Lnet/minecraft/world/phys/Vec3;"
)
)
private Vec3 antarchy$collideStepCandidateInLocalFrame(
Vec3 localMovement,
AABB box,
List<VoxelShape> collisions,
Operation<Vec3> original
) {
Entity entity = (Entity) (Object) this;
AntarchyGravityDirection direction = AntarchyGravityApi.getGravityDirection(entity);
if (!direction.isInverted()) {
return original.call(localMovement, box, collisions);
}
Vec3 worldMovement = AntarchyGravityRotationUtil.vecPlayerToWorld(localMovement, direction);
Vec3 worldResult = original.call(worldMovement, box, collisions);
return AntarchyGravityRotationUtil.vecWorldToPlayer(worldResult, direction);
}
@Redirect( @Redirect(
method = "collide", method = "collide",
at = @At( at = @At(
@@ -287,6 +340,15 @@ public abstract class EntityMoveMixin {
return; return;
} }
Vec3 worldResult = AntarchyGravityRotationUtil.vecPlayerToWorld(cir.getReturnValue(), direction); Vec3 worldResult = AntarchyGravityRotationUtil.vecPlayerToWorld(cir.getReturnValue(), direction);
Vec3 localMovement = AntarchyGravityRotationUtil.vecWorldToPlayer(this.antarchy$collideWorldMovement, direction);
if (!this.antarchy$loggedHorizontalBlock
&& (localMovement.x != cir.getReturnValue().x || localMovement.z != cir.getReturnValue().z)) {
this.antarchy$loggedHorizontalBlock = true;
ANTARCHY_STEP_LOGGER.warn(
"Inverted horizontal block: localMovement={} localResult={} worldMovement={} worldResult={} onGround={} maxUpStep={} box={}",
localMovement, cir.getReturnValue(), this.antarchy$collideWorldMovement, worldResult,
entity.onGround(), entity.maxUpStep(), entity.getBoundingBox());
}
cir.setReturnValue(PortalGunCollisionHelper.resolveCollision( cir.setReturnValue(PortalGunCollisionHelper.resolveCollision(
entity, entity,
entity.getBoundingBox(), entity.getBoundingBox(),
@@ -2,6 +2,8 @@ package com.craisinlord.antarchy.mixins.time;
import com.craisinlord.antarchy.content.time.TimeDilationApi; import com.craisinlord.antarchy.content.time.TimeDilationApi;
import com.craisinlord.antarchy.content.time.TimeDilationManager; import com.craisinlord.antarchy.content.time.TimeDilationManager;
import com.craisinlord.antarchy.content.client.ContractionAfterimages;
import com.craisinlord.antarchy.content.effect.RoyalEffectHooks;
import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionHand;
import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.LivingEntity;
@@ -14,7 +16,11 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
public abstract class LivingEntityTimeDilationMixin { public abstract class LivingEntityTimeDilationMixin {
@Inject(method = "tickEffects", at = @At("HEAD")) @Inject(method = "tickEffects", at = @At("HEAD"))
private void antarchy$trackTemporalEffects(CallbackInfo ci) { private void antarchy$trackTemporalEffects(CallbackInfo ci) {
TimeDilationManager.trackPotentiallyAffected((Entity) (Object) this); Entity entity = (Entity) (Object) this;
TimeDilationManager.trackPotentiallyAffected(entity);
if (entity.level().isClientSide && RoyalEffectHooks.contractedHolder() != null) {
ContractionAfterimages.observe((LivingEntity) entity);
}
} }
@Inject(method = "updateSwingTime", at = @At("HEAD"), cancellable = true) @Inject(method = "updateSwingTime", at = @At("HEAD"), cancellable = true)
@@ -9,15 +9,33 @@ import com.craisinlord.antarchy.content.effect.RoyalEffectHooks;
import com.craisinlord.antarchy.mixins.client.EntityBoundSoundInstanceAccessor; import com.craisinlord.antarchy.mixins.client.EntityBoundSoundInstanceAccessor;
import com.llamalad7.mixinextras.injector.ModifyReturnValue; import com.llamalad7.mixinextras.injector.ModifyReturnValue;
import net.minecraft.client.resources.sounds.SoundInstance; import net.minecraft.client.resources.sounds.SoundInstance;
import net.minecraft.client.sounds.ChannelAccess;
import net.minecraft.client.sounds.SoundEngine; import net.minecraft.client.sounds.SoundEngine;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.Entity;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.Map;
@Mixin(SoundEngine.class) @Mixin(SoundEngine.class)
public abstract class SoundEngineTimeDilationMixin { public abstract class SoundEngineTimeDilationMixin {
@Shadow @Final private Map<SoundInstance, ChannelAccess.ChannelHandle> instanceToChannel;
@Inject(method = "tickNonPaused", at = @At("TAIL"))
private void antarchy$refreshActiveSoundPitches(CallbackInfo ci) {
if (!ClientTimeDilationTicker.hasActiveDilation()) {
return;
}
this.instanceToChannel.forEach((sound, channel) ->
channel.execute(audioChannel -> audioChannel.setPitch(this.antarchy$dilatePitch(sound.getPitch(), sound))));
}
@ModifyReturnValue(method = "calculatePitch", at = @At("RETURN")) @ModifyReturnValue(method = "calculatePitch", at = @At("RETURN"))
private float antarchy$dilatePitch(float pitch, SoundInstance sound) { private float antarchy$dilatePitch(float pitch, SoundInstance sound) {
double rate = ClientTimeDilationTicker.rateAt(sound.getX(), sound.getY(), sound.getZ()); double rate = ClientTimeDilationTicker.rateAt(sound.getX(), sound.getY(), sound.getZ());
@@ -26,7 +26,7 @@
}, },
{ {
"type": "minecraft:item", "type": "minecraft:item",
"name": "antarchy:cornea_stalk", "name": "antarchy:cornea_ear",
"weight": 8, "weight": 8,
"functions": [ "functions": [
{ {
@@ -10,7 +10,12 @@
"biomes": "#antarchy:has_structure/underside_castle_biomes", "biomes": "#antarchy:has_structure/underside_castle_biomes",
"step": "underground_structures", "step": "underground_structures",
"enhanced_terrain_adaptation": { "enhanced_terrain_adaptation": {
"type": "integrated_api:none" "type": "integrated_api:custom",
"carves": true,
"beards": true,
"kernel_size": 35,
"kernel_distance": 35,
"direction": "up"
}, },
"start_height": { "start_height": {
"type": "minecraft:constant", "type": "minecraft:constant",
@@ -10,7 +10,12 @@
"biomes": "#antarchy:has_structure/underside_ruins_biomes", "biomes": "#antarchy:has_structure/underside_ruins_biomes",
"step": "underground_structures", "step": "underground_structures",
"enhanced_terrain_adaptation": { "enhanced_terrain_adaptation": {
"type": "integrated_api:none" "type": "integrated_api:custom",
"carves": true,
"beards": true,
"kernel_size": 35,
"kernel_distance": 35,
"direction": "up"
}, },
"start_height": { "start_height": {
"type": "minecraft:constant", "type": "minecraft:constant",
@@ -1,7 +1,6 @@
{ {
"replace": false, "replace": false,
"values": [ "values": [
"minecraft:slime_ball", "minecraft:slime_ball"
"antarchy:flytrap_goo"
] ]
} }
@@ -72,8 +72,14 @@ public final class AntarchyFabricEvents {
} }
public static void register() { public static void register() {
ServerPlayConnectionEvents.JOIN.register((handler, sender, server) -> ChronosphereManager.refresh(handler.player)); ServerPlayConnectionEvents.JOIN.register((handler, sender, server) -> {
ServerPlayerEvents.AFTER_RESPAWN.register((oldPlayer, newPlayer, alive) -> ChronosphereManager.refresh(newPlayer)); ChronosphereManager.refresh(handler.player);
TimeDilationManager.resyncPersistentEffects(handler.player);
});
ServerPlayerEvents.AFTER_RESPAWN.register((oldPlayer, newPlayer, alive) -> {
ChronosphereManager.refresh(newPlayer);
TimeDilationManager.resyncPersistentEffects(newPlayer);
});
ServerEntityWorldChangeEvents.AFTER_PLAYER_CHANGE_WORLD.register((player, origin, destination) -> ChronosphereManager.refresh(player)); ServerEntityWorldChangeEvents.AFTER_PLAYER_CHANGE_WORLD.register((player, origin, destination) -> ChronosphereManager.refresh(player));
net.fabricmc.fabric.api.networking.v1.EntityTrackingEvents.START_TRACKING.register((trackedEntity, player) -> { net.fabricmc.fabric.api.networking.v1.EntityTrackingEvents.START_TRACKING.register((trackedEntity, player) -> {
if (trackedEntity instanceof com.craisinlord.antarchy.content.time.TimeDilationEntityAccess access if (trackedEntity instanceof com.craisinlord.antarchy.content.time.TimeDilationEntityAccess access
@@ -2,6 +2,7 @@ package com.craisinlord.antarchy.fabric;
import com.craisinlord.antarchy.Antarchy; import com.craisinlord.antarchy.Antarchy;
import com.craisinlord.antarchy.compat.infinity.InfinityCompatBridge; import com.craisinlord.antarchy.compat.infinity.InfinityCompatBridge;
import com.craisinlord.antarchy.compat.infinity.InfinityWarpResult;
import net.lerariemann.infinity.util.InfinityMethods; import net.lerariemann.infinity.util.InfinityMethods;
import net.lerariemann.infinity.util.teleport.PortalCreator; import net.lerariemann.infinity.util.teleport.PortalCreator;
import net.lerariemann.infinity.util.teleport.WarpLogic; import net.lerariemann.infinity.util.teleport.WarpLogic;
@@ -38,13 +39,15 @@ public final class FabricInfinityCompat implements InfinityCompatBridge {
} }
@Override @Override
public boolean requestWarp(ServerPlayer player, ResourceLocation dimensionId) { public InfinityWarpResult requestWarp(ServerPlayer player, ResourceLocation dimensionId) {
try { try {
WarpLogic.requestWarp(player, dimensionId, false); WarpLogic.requestWarp(player, dimensionId, false);
return true; return InfinityWarpResult.PENDING;
} catch (Throwable throwable) { } catch (Throwable throwable) {
Antarchy.LOGGER.error("[Antarchy] Failed to request an Infinity warp to {}", dimensionId, throwable); Antarchy.LOGGER.error("[Antarchy] Failed to request an Infinity warp to {}", dimensionId, throwable);
return false; return InfinityGenerationFailure.consume(dimensionId)
? InfinityWarpResult.FAILED_GENERATION
: InfinityWarpResult.REJECTED;
} }
} }
@@ -0,0 +1,20 @@
package com.craisinlord.antarchy.fabric;
import net.minecraft.resources.ResourceLocation;
public final class InfinityGenerationFailure {
private static final ThreadLocal<ResourceLocation> FAILURE = new ThreadLocal<>();
private InfinityGenerationFailure() {
}
public static void mark(ResourceLocation dimensionId) {
FAILURE.set(dimensionId);
}
public static boolean consume(ResourceLocation dimensionId) {
ResourceLocation failed = FAILURE.get();
FAILURE.remove();
return dimensionId.equals(failed);
}
}
@@ -4,6 +4,8 @@ import com.craisinlord.antarchy.content.entity.multipart.MultipartEntityOwner;
import com.craisinlord.antarchy.content.entity.multipart.MultipartFramework; import com.craisinlord.antarchy.content.entity.multipart.MultipartFramework;
import com.craisinlord.antarchy.content.entity.multipart.MultipartPartAccess; import com.craisinlord.antarchy.content.entity.multipart.MultipartPartAccess;
import com.craisinlord.antarchy.content.entity.multipart.MultipartPartDefinition; import com.craisinlord.antarchy.content.entity.multipart.MultipartPartDefinition;
import com.craisinlord.antarchy.content.gravity.AntarchyGravityApi;
import com.craisinlord.antarchy.content.gravity.AntarchyGravityRotationUtil;
import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.CompoundTag;
import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult; import net.minecraft.world.InteractionResult;
@@ -64,17 +66,27 @@ public class MultipartPartEntity extends Entity implements MultipartPartAccess {
return; return;
} }
AntarchyGravityApi.applySyncedState(
this,
AntarchyGravityApi.getGravityDirection(parent),
AntarchyGravityApi.getPrevGravityDirection(parent),
AntarchyGravityApi.isGravityForced(parent),
AntarchyGravityApi.getTransitionDuration(parent),
AntarchyGravityApi.getTransitionRemaining(parent)
);
double oldX = this.getX(); double oldX = this.getX();
double oldY = this.getY(); double oldY = this.getY();
double oldZ = this.getZ(); double oldZ = this.getZ();
double yawRadians = Math.toRadians(parent.getYRot()); double yawRadians = Math.toRadians(parent.getYRot());
Vec3 forward = new Vec3(-Math.sin(yawRadians), 0.0D, Math.cos(yawRadians)); Vec3 forward = new Vec3(-Math.sin(yawRadians), 0.0D, Math.cos(yawRadians));
Vec3 right = new Vec3(forward.z, 0.0D, -forward.x); Vec3 right = new Vec3(forward.z, 0.0D, -forward.x);
Vec3 position = new Vec3( Vec3 localOffset = new Vec3(
parent.getX() + forward.x * definition.forwardOffset() + right.x * definition.lateralOffset(), forward.x * definition.forwardOffset() + right.x * definition.lateralOffset(),
parent.getY() + definition.yOffset(), definition.yOffset() + (AntarchyGravityApi.isGravityInverted(parent) ? definition.height() : 0.0D),
parent.getZ() + forward.z * definition.forwardOffset() + right.z * definition.lateralOffset() forward.z * definition.forwardOffset() + right.z * definition.lateralOffset());
); Vec3 offset = AntarchyGravityRotationUtil.vecPlayerToWorld(localOffset, AntarchyGravityApi.getGravityDirection(parent));
Vec3 position = parent.position().add(offset);
this.setPos(position.x, position.y, position.z); this.setPos(position.x, position.y, position.z);
this.setYRot(parent.getYRot()); this.setYRot(parent.getYRot());
this.setXRot(parent.getXRot()); this.setXRot(parent.getXRot());
@@ -0,0 +1,34 @@
package com.craisinlord.antarchy.fabric.mixin.infinity;
import com.craisinlord.antarchy.fabric.InfinityGenerationFailure;
import net.lerariemann.infinity.access.MinecraftServerAccess;
import net.lerariemann.infinity.util.teleport.PortalCreator;
import net.lerariemann.infinity.util.teleport.WarpLogic;
import net.minecraft.core.registries.Registries;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.MinecraftServer;
import net.minecraft.world.level.Level;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
@Mixin(WarpLogic.class)
public interface WarpLogicMixin {
@Redirect(
method = "requestWarp(Lnet/minecraft/server/level/ServerPlayer;Lnet/minecraft/resources/ResourceLocation;Z)V",
at = @At(value = "INVOKE", target = "Lnet/lerariemann/infinity/util/teleport/PortalCreator;tryAddInfinityDimension(Lnet/minecraft/server/MinecraftServer;Lnet/minecraft/resources/ResourceLocation;)Z")
)
private static boolean antarchy$queuedDimensionCountsAsNew(MinecraftServer server, ResourceLocation dimensionId) throws Throwable {
ResourceKey<Level> key = ResourceKey.create(Registries.DIMENSION, dimensionId);
if (server instanceof MinecraftServerAccess access && access.infinity$hasToAdd(key)) {
return true;
}
try {
return PortalCreator.tryAddInfinityDimension(server, dimensionId);
} catch (Throwable throwable) {
InfinityGenerationFailure.mark(dimensionId);
throw throwable;
}
}
}
@@ -1036,10 +1036,10 @@ public final class AntarchyFabricItems {
public static final DeferredItem<Item> HERCULES_HORN = ITEMS.registerSimpleItem("hercules_horn", new Item.Properties().rarity(Rarity.UNCOMMON)); public static final DeferredItem<Item> HERCULES_HORN = ITEMS.registerSimpleItem("hercules_horn", new Item.Properties().rarity(Rarity.UNCOMMON));
public static final DeferredItem<Item> KING_SCALE = ITEMS.registerSimpleItem("king_scale", new Item.Properties().rarity(Rarity.RARE)); public static final DeferredItem<Item> KING_SCALE = ITEMS.registerSimpleItem("king_scale", new Item.Properties().rarity(Rarity.RARE).fireResistant());
public static final DeferredItem<Item> QUEEN_SCALE = ITEMS.registerSimpleItem("queen_scale", new Item.Properties().rarity(Rarity.RARE)); public static final DeferredItem<Item> QUEEN_SCALE = ITEMS.registerSimpleItem("queen_scale", new Item.Properties().rarity(Rarity.RARE).fireResistant());
public static final DeferredItem<Item> CHITIN = ITEMS.registerSimpleItem("chitin", new Item.Properties().rarity(Rarity.UNCOMMON)); public static final DeferredItem<Item> CHITIN = ITEMS.registerSimpleItem("chitin", new Item.Properties().rarity(Rarity.UNCOMMON));
@@ -21,6 +21,7 @@
"entity.MultipartEntityLevelRegistryMixin", "entity.MultipartEntityLevelRegistryMixin",
"WoodTypeInvoker", "WoodTypeInvoker",
"infinity.PortalCreatorMixin", "infinity.PortalCreatorMixin",
"infinity.WarpLogicMixin",
"infinity.AmendmentRareOresMixin" "infinity.AmendmentRareOresMixin"
], ],
"client": [ "client": [
@@ -0,0 +1,20 @@
package com.craisinlord.antarchy.neoforge;
import net.minecraft.resources.ResourceLocation;
public final class InfinityGenerationFailure {
private static final ThreadLocal<ResourceLocation> FAILURE = new ThreadLocal<>();
private InfinityGenerationFailure() {
}
public static void mark(ResourceLocation dimensionId) {
FAILURE.set(dimensionId);
}
public static boolean consume(ResourceLocation dimensionId) {
ResourceLocation failed = FAILURE.get();
FAILURE.remove();
return dimensionId.equals(failed);
}
}
@@ -2,6 +2,7 @@ package com.craisinlord.antarchy.neoforge;
import com.craisinlord.antarchy.Antarchy; import com.craisinlord.antarchy.Antarchy;
import com.craisinlord.antarchy.compat.infinity.InfinityCompatBridge; import com.craisinlord.antarchy.compat.infinity.InfinityCompatBridge;
import com.craisinlord.antarchy.compat.infinity.InfinityWarpResult;
import net.lerariemann.infinity.InfinityMod; import net.lerariemann.infinity.InfinityMod;
import net.lerariemann.infinity.util.InfinityMethods; import net.lerariemann.infinity.util.InfinityMethods;
import net.lerariemann.infinity.util.teleport.PortalCreator; import net.lerariemann.infinity.util.teleport.PortalCreator;
@@ -69,13 +70,15 @@ public final class NeoForgeInfinityCompat implements InfinityCompatBridge {
} }
@Override @Override
public boolean requestWarp(ServerPlayer player, ResourceLocation dimensionId) { public InfinityWarpResult requestWarp(ServerPlayer player, ResourceLocation dimensionId) {
try { try {
WarpLogic.requestWarp(player, dimensionId, false); WarpLogic.requestWarp(player, dimensionId, false);
return true; return InfinityWarpResult.PENDING;
} catch (Throwable throwable) { } catch (Throwable throwable) {
Antarchy.LOGGER.error("[Antarchy] Failed to request an Infinity warp to {}", dimensionId, throwable); Antarchy.LOGGER.error("[Antarchy] Failed to request an Infinity warp to {}", dimensionId, throwable);
return false; return InfinityGenerationFailure.consume(dimensionId)
? InfinityWarpResult.FAILED_GENERATION
: InfinityWarpResult.REJECTED;
} }
} }
@@ -4,6 +4,8 @@ import com.craisinlord.antarchy.content.entity.multipart.MultipartEntityOwner;
import com.craisinlord.antarchy.content.entity.multipart.MultipartFramework; import com.craisinlord.antarchy.content.entity.multipart.MultipartFramework;
import com.craisinlord.antarchy.content.entity.multipart.MultipartPartAccess; import com.craisinlord.antarchy.content.entity.multipart.MultipartPartAccess;
import com.craisinlord.antarchy.content.entity.multipart.MultipartPartDefinition; import com.craisinlord.antarchy.content.entity.multipart.MultipartPartDefinition;
import com.craisinlord.antarchy.content.gravity.AntarchyGravityApi;
import com.craisinlord.antarchy.content.gravity.AntarchyGravityRotationUtil;
import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.CompoundTag;
import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult; import net.minecraft.world.InteractionResult;
@@ -67,17 +69,27 @@ public class MultipartPartEntity extends PartEntity<Entity> implements OwnableEn
return; return;
} }
AntarchyGravityApi.applySyncedState(
this,
AntarchyGravityApi.getGravityDirection(parent),
AntarchyGravityApi.getPrevGravityDirection(parent),
AntarchyGravityApi.isGravityForced(parent),
AntarchyGravityApi.getTransitionDuration(parent),
AntarchyGravityApi.getTransitionRemaining(parent)
);
double oldX = this.getX(); double oldX = this.getX();
double oldY = this.getY(); double oldY = this.getY();
double oldZ = this.getZ(); double oldZ = this.getZ();
double yawRadians = Math.toRadians(parent.getYRot()); double yawRadians = Math.toRadians(parent.getYRot());
Vec3 forward = new Vec3(-Math.sin(yawRadians), 0.0D, Math.cos(yawRadians)); Vec3 forward = new Vec3(-Math.sin(yawRadians), 0.0D, Math.cos(yawRadians));
Vec3 right = new Vec3(forward.z, 0.0D, -forward.x); Vec3 right = new Vec3(forward.z, 0.0D, -forward.x);
Vec3 position = new Vec3( Vec3 localOffset = new Vec3(
parent.getX() + forward.x * this.definition.forwardOffset() + right.x * this.definition.lateralOffset(), forward.x * this.definition.forwardOffset() + right.x * this.definition.lateralOffset(),
parent.getY() + this.definition.yOffset(), this.definition.yOffset() + (AntarchyGravityApi.isGravityInverted(parent) ? this.definition.height() : 0.0D),
parent.getZ() + forward.z * this.definition.forwardOffset() + right.z * this.definition.lateralOffset() forward.z * this.definition.forwardOffset() + right.z * this.definition.lateralOffset());
); Vec3 offset = AntarchyGravityRotationUtil.vecPlayerToWorld(localOffset, AntarchyGravityApi.getGravityDirection(parent));
Vec3 position = parent.position().add(offset);
this.setPos(position.x, position.y, position.z); this.setPos(position.x, position.y, position.z);
this.setRot(parent.getYRot(), parent.getXRot()); this.setRot(parent.getYRot(), parent.getXRot());
this.xo = oldX; this.xo = oldX;
@@ -9,11 +9,6 @@ import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.Redirect;
@Mixin(value = ConfigGenInvocation.class, remap = false) @Mixin(value = ConfigGenInvocation.class, remap = false)
/*
* Guards against a NPE in Infinite Dimensions' SurfaceRuleScanner when another mod
* registers a surface rule with a missing "biome_is" field. Without this, the crash
* propagates up through PlayerList.placeNewPlayer and kicks the player on login
*/
public class ConfigGenInvocationMixin { public class ConfigGenInvocationMixin {
@Redirect( @Redirect(
@@ -0,0 +1,35 @@
package com.craisinlord.antarchy.neoforge.mixins.infinity;
import com.craisinlord.antarchy.neoforge.InfinityGenerationFailure;
import net.lerariemann.infinity.access.MinecraftServerAccess;
import net.lerariemann.infinity.util.teleport.PortalCreator;
import net.lerariemann.infinity.util.teleport.WarpLogic;
import net.minecraft.core.registries.Registries;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.MinecraftServer;
import net.minecraft.world.level.Level;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
@Mixin(value = WarpLogic.class, remap = false)
public interface WarpLogicMixin {
@Redirect(
method = "requestWarp(Lnet/minecraft/server/level/ServerPlayer;Lnet/minecraft/resources/ResourceLocation;Z)V",
at = @At(value = "INVOKE", target = "Lnet/lerariemann/infinity/util/teleport/PortalCreator;tryAddInfinityDimension(Lnet/minecraft/server/MinecraftServer;Lnet/minecraft/resources/ResourceLocation;)Z"),
remap = false
)
private static boolean antarchy$queuedDimensionCountsAsNew(MinecraftServer server, ResourceLocation dimensionId) throws Throwable {
ResourceKey<Level> key = ResourceKey.create(Registries.DIMENSION, dimensionId);
if (server instanceof MinecraftServerAccess access && access.infinity$hasToAdd(key)) {
return true;
}
try {
return PortalCreator.tryAddInfinityDimension(server, dimensionId);
} catch (Throwable throwable) {
InfinityGenerationFailure.mark(dimensionId);
throw throwable;
}
}
}
@@ -57,8 +57,7 @@ public final class MultipartNetworking {
return; return;
} }
DamageSource source = serverPlayer.level().damageSources().playerAttack(serverPlayer); serverPlayer.attack(part);
owner.antarchy$hurtMultipartPart(part, source, payload.damage());
}); });
} }
@@ -143,6 +143,8 @@ public final class AntarchyNeoForgeEvents {
NeoForge.EVENT_BUS.addListener(AntarchyNeoForgeEvents::handleBloodglassPlayerDeath); NeoForge.EVENT_BUS.addListener(AntarchyNeoForgeEvents::handleBloodglassPlayerDeath);
NeoForge.EVENT_BUS.addListener(AntarchyNeoForgeEvents::handleBloodglassRespawn); NeoForge.EVENT_BUS.addListener(AntarchyNeoForgeEvents::handleBloodglassRespawn);
NeoForge.EVENT_BUS.addListener(AntarchyNeoForgeEvents::handleBloodglassLogin); NeoForge.EVENT_BUS.addListener(AntarchyNeoForgeEvents::handleBloodglassLogin);
NeoForge.EVENT_BUS.addListener(AntarchyNeoForgeEvents::handleTimeDilationLogin);
NeoForge.EVENT_BUS.addListener(AntarchyNeoForgeEvents::handleTimeDilationRespawn);
NeoForge.EVENT_BUS.addListener(AntarchyNeoForgeEvents::handleCavarynHordeKill); NeoForge.EVENT_BUS.addListener(AntarchyNeoForgeEvents::handleCavarynHordeKill);
NeoForge.EVENT_BUS.addListener(AntarchyNeoForgeEvents::handleCavarynHordeBlockBreak); NeoForge.EVENT_BUS.addListener(AntarchyNeoForgeEvents::handleCavarynHordeBlockBreak);
NeoForge.EVENT_BUS.addListener(AntarchyNeoForgeEvents::tickCavarynHordes); NeoForge.EVENT_BUS.addListener(AntarchyNeoForgeEvents::tickCavarynHordes);
@@ -213,7 +215,8 @@ public final class AntarchyNeoForgeEvents {
&& tickEnd > 0L && tickEnd > 0L
&& now - tickEnd >= SERVER_IDLE_BUSY_WARN_NANOS && now - tickEnd >= SERVER_IDLE_BUSY_WARN_NANOS
&& state != Thread.State.WAITING && state != Thread.State.WAITING
&& state != Thread.State.TIMED_WAITING; && state != Thread.State.TIMED_WAITING
&& !antarchy$isServerThreadIdleWait(tickThread);
if (idleBusy) { if (idleBusy) {
antarchy$dumpServerThreadWatchdog( antarchy$dumpServerThreadWatchdog(
"server thread busy outside active tick", "server thread busy outside active tick",
@@ -238,6 +241,18 @@ public final class AntarchyNeoForgeEvents {
} }
} }
private static boolean antarchy$isServerThreadIdleWait(Thread tickThread) {
for (StackTraceElement element : tickThread.getStackTrace()) {
String method = element.getMethodName();
if (method.equals("waitUntilNextTick")
|| method.equals("waitForTasks")
|| method.equals("managedBlock")) {
return true;
}
}
return false;
}
static void onVillagerTrades(VillagerTradesEvent event) { static void onVillagerTrades(VillagerTradesEvent event) {
if (event.getType() == VillagerProfession.FARMER) { if (event.getType() == VillagerProfession.FARMER) {
event.getTrades().get(1).add(emeraldForItems(AntarchyObjects.CORN.get(), 20, 16, 2)); event.getTrades().get(1).add(emeraldForItems(AntarchyObjects.CORN.get(), 20, 16, 2));
@@ -384,6 +399,18 @@ public final class AntarchyNeoForgeEvents {
} }
} }
static void handleTimeDilationLogin(PlayerEvent.PlayerLoggedInEvent event) {
if (event.getEntity() instanceof ServerPlayer player) {
TimeDilationManager.resyncPersistentEffects(player);
}
}
static void handleTimeDilationRespawn(PlayerEvent.PlayerRespawnEvent event) {
if (event.getEntity() instanceof ServerPlayer player) {
TimeDilationManager.resyncPersistentEffects(player);
}
}
static void handleTigerEyeLogout(PlayerEvent.PlayerLoggedOutEvent event) { static void handleTigerEyeLogout(PlayerEvent.PlayerLoggedOutEvent event) {
if (event.getEntity() instanceof ServerPlayer player) { if (event.getEntity() instanceof ServerPlayer player) {
TigerEyeCamouflageController.deactivate(player, false); TigerEyeCamouflageController.deactivate(player, false);
@@ -561,15 +588,6 @@ public final class AntarchyNeoForgeEvents {
} }
if (existingInverted == null || existingInverted.getDuration() <= ANTIMETAL_INVERTED_REFRESH_THRESHOLD_TICKS) { if (existingInverted == null || existingInverted.getDuration() <= ANTIMETAL_INVERTED_REFRESH_THRESHOLD_TICKS) {
if (livingEntity instanceof Player || livingEntity.level().dimension().location().toString().equals("antarchy:thoraxis")) {
Antarchy.LOGGER.info(
"[antarchy-gravity] overhead antimetal applying inverted effect targetType={} uuid={} pos=({}, {}, {}) dim={} existingDuration={} gameTime={}",
livingEntity.getType(), livingEntity.getUUID(), livingEntity.getX(), livingEntity.getY(), livingEntity.getZ(),
livingEntity.level().dimension().location(),
existingInverted != null ? existingInverted.getDuration() : -1,
gameTime
);
}
livingEntity.addEffect(new MobEffectInstance(AntarchyNeoforgeMisc.INVERTED, ANTIMETAL_INVERTED_REFRESH_TICKS, 0, false, false, false)); livingEntity.addEffect(new MobEffectInstance(AntarchyNeoforgeMisc.INVERTED, ANTIMETAL_INVERTED_REFRESH_TICKS, 0, false, false, false));
spawnAntimetalInversionParticles(livingEntity); spawnAntimetalInversionParticles(livingEntity);
} }
@@ -717,8 +717,8 @@ public final class AntarchyNeoforgeItems {
public static final DeferredItem<MantisClawItem> MANTIS_CLAW = ITEMS.register("mantis_claw", public static final DeferredItem<MantisClawItem> MANTIS_CLAW = ITEMS.register("mantis_claw",
() -> new MantisClawItem(Tiers.IRON, new Item.Properties().stacksTo(1).durability(50).rarity(Rarity.UNCOMMON))); () -> new MantisClawItem(Tiers.IRON, new Item.Properties().stacksTo(1).durability(50).rarity(Rarity.UNCOMMON)));
public static final DeferredItem<Item> HERCULES_HORN = ITEMS.registerSimpleItem("hercules_horn", new Item.Properties().rarity(Rarity.UNCOMMON)); public static final DeferredItem<Item> HERCULES_HORN = ITEMS.registerSimpleItem("hercules_horn", new Item.Properties().rarity(Rarity.UNCOMMON));
public static final DeferredItem<Item> KING_SCALE = ITEMS.registerSimpleItem("king_scale", new Item.Properties().rarity(Rarity.RARE)); public static final DeferredItem<Item> KING_SCALE = ITEMS.registerSimpleItem("king_scale", new Item.Properties().rarity(Rarity.RARE).fireResistant());
public static final DeferredItem<Item> QUEEN_SCALE = ITEMS.registerSimpleItem("queen_scale", new Item.Properties().rarity(Rarity.RARE)); public static final DeferredItem<Item> QUEEN_SCALE = ITEMS.registerSimpleItem("queen_scale", new Item.Properties().rarity(Rarity.RARE).fireResistant());
public static final DeferredItem<Item> CHITIN = ITEMS.registerSimpleItem("chitin", new Item.Properties().rarity(Rarity.UNCOMMON)); public static final DeferredItem<Item> CHITIN = ITEMS.registerSimpleItem("chitin", new Item.Properties().rarity(Rarity.UNCOMMON));
public static final DeferredItem<Item> JERRY_NUCLEUS = ITEMS.register("jerry_nucleus", public static final DeferredItem<Item> JERRY_NUCLEUS = ITEMS.register("jerry_nucleus",
() -> new com.craisinlord.antarchy.content.item.JerryNucleusItem(new Item.Properties().rarity(Rarity.UNCOMMON) () -> new com.craisinlord.antarchy.content.item.JerryNucleusItem(new Item.Properties().rarity(Rarity.UNCOMMON)
@@ -12,6 +12,7 @@
"infinity.AmendmentRareOresMixin", "infinity.AmendmentRareOresMixin",
"infinity.ConfigGenInvocationMixin", "infinity.ConfigGenInvocationMixin",
"infinity.PortalCreatorMixin", "infinity.PortalCreatorMixin",
"infinity.WarpLogicMixin",
"level.ClientLevelPartEntitiesAccessor", "level.ClientLevelPartEntitiesAccessor",
"level.ServerLevelPartEntitiesAccessor" "level.ServerLevelPartEntitiesAccessor"
], ],