finish ant portals

optional flint and steel functionality
dorrie
This commit is contained in:
craisinlord committed 2026-07-08 13:33:22 -05:00
1 parent 4e67661448
commit f23cf63f4f
15 files changed
+598 -53

No files matched your search

@@ -448,6 +448,7 @@ public final class AntarchySettings {
private static int fallenKingCrownArmorValue = 2;
private static double fallenKingCrownArmorToughness = 0.0D;
private static boolean permanentPortalsEnabled = true;
private static boolean permanentPortalsFlintAndSteelEnabled = false;
private static boolean elythiaPortalEnabled = true;
private static boolean thoraxisPortalEnabled = true;
private static boolean cavarynPortalEnabled = true;
@@ -965,6 +966,10 @@ public final class AntarchySettings {
permanentPortalsEnabled = value;
}
public static void setPermanentPortalsFlintAndSteelEnabled(boolean value) {
permanentPortalsFlintAndSteelEnabled = value;
}
public static void setElythiaPortalEnabled(boolean value) {
elythiaPortalEnabled = value;
}
@@ -977,6 +982,12 @@ public final class AntarchySettings {
cavarynPortalEnabled = value;
}
public static boolean permanentPortalsEnabled() { return permanentPortalsEnabled; }
public static boolean permanentPortalsFlintAndSteelEnabled() { return permanentPortalsFlintAndSteelEnabled; }
public static boolean elythiaPortalEnabled() { return elythiaPortalEnabled; }
public static boolean thoraxisPortalEnabled() { return thoraxisPortalEnabled; }
public static boolean cavarynPortalEnabled() { return cavarynPortalEnabled; }
public static void setBrownAntRequiresReagent(boolean value) {
brownAntRequiresReagent = value;
}
@@ -2454,11 +2465,6 @@ public final class AntarchySettings {
public static double fallenKingCrownArmorToughness() { return fallenKingCrownArmorToughness; }
public static void setFallenKingCrownArmorToughness(double value) { fallenKingCrownArmorToughness = value; }
public static boolean permanentPortalsEnabled() { return permanentPortalsEnabled; }
public static boolean elythiaPortalEnabled() { return elythiaPortalEnabled; }
public static boolean thoraxisPortalEnabled() { return thoraxisPortalEnabled; }
public static boolean cavarynPortalEnabled() { return cavarynPortalEnabled; }
public static boolean elythiaFireflyParticlesEnabled() { return elythiaFireflyParticlesEnabled; }
public static void setElythiaFireflyParticlesEnabled(boolean value) { elythiaFireflyParticlesEnabled = value; }
@@ -2,6 +2,7 @@ package com.craisinlord.antarchy.content.block;
import com.craisinlord.antarchy.content.portal.PermanentPortalManager;
import com.craisinlord.antarchy.content.portal.PermanentPortalType;
import com.craisinlord.antarchy.content.portal.PermanentPortalTeleporter;
import com.mojang.serialization.MapCodec;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
@@ -11,6 +12,7 @@ import net.minecraft.server.level.ServerLevel;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.sounds.SoundSource;
import net.minecraft.util.RandomSource;
import net.minecraft.world.level.portal.DimensionTransition;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.Level;
@@ -27,7 +29,7 @@ import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.Shapes;
import net.minecraft.world.phys.shapes.VoxelShape;
public final class AntDimensionPortalBlock extends Block {
public final class AntDimensionPortalBlock extends Block implements net.minecraft.world.level.block.Portal {
public static final EnumProperty<Direction.Axis> AXIS = BlockStateProperties.HORIZONTAL_AXIS;
private static final VoxelShape X_SHAPE = Block.box(0.0D, 0.0D, 6.0D, 16.0D, 16.0D, 10.0D);
private static final VoxelShape Z_SHAPE = Block.box(6.0D, 0.0D, 0.0D, 10.0D, 16.0D, 16.0D);
@@ -85,10 +87,25 @@ public final class AntDimensionPortalBlock extends Block {
@Override
public void entityInside(BlockState state, Level level, BlockPos pos, Entity entity) {
if (!level.isClientSide()) {
PermanentPortalManager.handleEntityInsidePortal(entity, this.type);
entity.setAsInsidePortal(this, pos);
}
}
@Override
public net.minecraft.world.level.block.Portal.Transition getLocalTransition() {
return net.minecraft.world.level.block.Portal.Transition.CONFUSION;
}
@Override
public int getPortalTransitionTime(ServerLevel level, Entity entity) {
return 20;
}
@Override
public DimensionTransition getPortalDestination(ServerLevel level, Entity entity, BlockPos pos) {
return PermanentPortalTeleporter.createTransition(level, entity, pos, this.type);
}
@Override
protected void onPlace(BlockState state, Level level, BlockPos pos, BlockState oldState, boolean movedByPiston) {
super.onPlace(state, level, pos, oldState, movedByPiston);
@@ -3,6 +3,7 @@ package com.craisinlord.antarchy.content.entity;
import com.craisinlord.antarchy.config.AntarchySettings;
import java.util.EnumSet;
import java.util.List;
import net.minecraft.core.BlockPos;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.syncher.EntityDataAccessor;
@@ -12,6 +13,8 @@ import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.tags.FluidTags;
import net.minecraft.util.Mth;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.damagesource.DamageSource;
@@ -26,8 +29,6 @@ import net.minecraft.world.entity.ai.attributes.Attributes;
import net.minecraft.world.entity.ai.control.MoveControl;
import net.minecraft.world.entity.ai.control.SmoothSwimmingLookControl;
import net.minecraft.world.entity.ai.control.SmoothSwimmingMoveControl;
import net.minecraft.world.entity.ai.goal.BreathAirGoal;
import net.minecraft.world.entity.ai.goal.FloatGoal;
import net.minecraft.world.entity.ai.goal.Goal;
import net.minecraft.world.entity.ai.goal.LookAtPlayerGoal;
import net.minecraft.world.entity.ai.goal.RandomLookAroundGoal;
@@ -67,11 +68,11 @@ public class DorrieEntity extends Animal implements GeoEntity {
SynchedEntityData.defineId(DorrieEntity.class, EntityDataSerializers.INT);
private static final int MAX_CHARGE_TICKS = 40;
private static final float WATER_RIDE_SPEED = 1.26F;
private static final float LAND_BEACH_SPEED = 0.04F;
private static final float WATER_RIDE_SPEED = 1.84F;
private static final float LAND_BEACH_SPEED = 0.10F;
private static final int QUIRK_DURATION_TICKS = 65;
private static final int MIN_QUIRK_COOLDOWN_TICKS = 180;
private static final int MAX_QUIRK_COOLDOWN_TICKS = 320;
private static final int MIN_QUIRK_COOLDOWN_TICKS = 140;
private static final int MAX_QUIRK_COOLDOWN_TICKS = 260;
private static final double MOVING_ANIM_THRESHOLD_SQ = 0.0025D;
private static final RawAnimation IDLE_GROUND_ANIM = RawAnimation.begin().thenLoop("idle_ground");
@@ -127,20 +128,19 @@ public class DorrieEntity extends Animal implements GeoEntity {
@Override
protected void registerGoals() {
this.goalSelector.addGoal(0, new FloatGoal(this));
this.goalSelector.addGoal(1, new BreathAirGoal(this));
this.goalSelector.addGoal(2, new TryFindWaterGoal(this));
this.goalSelector.addGoal(3, new HuntCheepGoal(this));
this.goalSelector.addGoal(4, new RandomSwimmingGoal(this, 1.0D, 10));
this.goalSelector.addGoal(5, new WaterAvoidingRandomStrollGoal(this, 0.45D, 60));
this.goalSelector.addGoal(6, new LookAtPlayerGoal(this, Player.class, 6.0F));
this.goalSelector.addGoal(7, new RandomLookAroundGoal(this));
this.goalSelector.addGoal(0, new TryFindWaterGoal(this));
this.goalSelector.addGoal(1, new HuntCheepGoal(this));
this.goalSelector.addGoal(2, new DolphinSwimGoal());
this.goalSelector.addGoal(3, new RandomSwimmingGoal(this, 1.35D, 8));
this.goalSelector.addGoal(4, new WaterAvoidingRandomStrollGoal(this, 0.72D, 40));
this.goalSelector.addGoal(5, new LookAtPlayerGoal(this, Player.class, 6.0F));
this.goalSelector.addGoal(6, new RandomLookAroundGoal(this));
}
public static AttributeSupplier.Builder createAttributes() {
return Mob.createMobAttributes()
.add(Attributes.MAX_HEALTH, AntarchySettings.dorrieHealth())
.add(Attributes.MOVEMENT_SPEED, 1.0D)
.add(Attributes.MOVEMENT_SPEED, 1.15D)
.add(Attributes.FOLLOW_RANGE, 16.0D)
.add(Attributes.ATTACK_DAMAGE, 4.0D);
}
@@ -255,13 +255,20 @@ public class DorrieEntity extends Animal implements GeoEntity {
protected Vec3 getRiddenInput(Player player, Vec3 travelVector) {
float forward = player.zza;
float strafe = player.xxa * 0.5F;
return new Vec3(strafe, 0, forward);
float pitch = Mth.clamp(player.getXRot(), -60.0F, 60.0F);
double vertical = 0.0D;
if (player.zza > 0.02F) {
vertical = -pitch / 45.0D;
} else if (Math.abs(pitch) > 10.0F) {
vertical = -pitch / 60.0D;
}
return new Vec3(strafe, Mth.clamp(vertical, -1.0D, 1.0D), forward);
}
@Override
protected float getRiddenSpeed(Player player) {
float speed = (this.isInWater() || this.isInLava())
? WATER_RIDE_SPEED * (1.0F + Math.min(this.swimForwardTicks, 60) * 0.025F)
? WATER_RIDE_SPEED * (1.0F + Math.min(this.swimForwardTicks, 60) * 0.03F)
: LAND_BEACH_SPEED;
return (float) this.getAttributeValue(Attributes.MOVEMENT_SPEED) * speed;
}
@@ -270,6 +277,7 @@ public class DorrieEntity extends Animal implements GeoEntity {
public void tick() {
super.tick();
this.updateNavigationMode();
this.setNoGravity(this.isInWater() || this.isInLava());
if (this.getControllingPassenger() instanceof Player player) {
this.setYRot(player.getYRot());
@@ -315,13 +323,6 @@ public class DorrieEntity extends Animal implements GeoEntity {
this.tickQuirkAnimation();
}
if (this.isInWater() && !isLeaping) {
Vec3 mov = this.getDeltaMovement();
double push = this.isUnderWater() ? 0.07D : 0.015D;
double cappedY = this.isUnderWater() ? 0.3D : 0.05D;
this.setDeltaMovement(mov.x, Math.min(mov.y + push, cappedY), mov.z);
}
if (landAnimTicks > 0) {
landAnimTicks--;
}
@@ -342,17 +343,57 @@ public class DorrieEntity extends Animal implements GeoEntity {
}
}
@Override
public void baseTick() {
super.baseTick();
if (this.isInWater() || this.isInLava()) {
this.setAirSupply(this.getMaxAirSupply());
}
}
@Override
public void travel(Vec3 travelVector) {
if (this.getControllingPassenger() instanceof Player rider && this.isVehicle()) {
this.setYRot(rider.getYRot());
this.yRotO = this.getYRot();
this.yBodyRot = this.getYRot();
this.yHeadRot = this.getYRot();
Vec3 riddenInput = this.getRiddenInput(rider, travelVector);
this.setSpeed(this.getRiddenSpeed(rider));
if (this.isInWater() || this.isInLava()) {
this.moveRelative(this.getSpeed(), new Vec3(riddenInput.x, 0.0D, riddenInput.z));
Vec3 motion = this.getDeltaMovement();
double vertical = this.isLeaping ? 0.0D : riddenInput.y * 0.09D;
this.setDeltaMovement(
motion.x * 0.92D,
motion.y * 0.90D + vertical,
motion.z * 0.92D
);
this.move(net.minecraft.world.entity.MoverType.SELF, this.getDeltaMovement());
return;
}
this.moveRelative(this.getSpeed(), new Vec3(riddenInput.x, 0.0D, riddenInput.z));
this.move(net.minecraft.world.entity.MoverType.SELF, this.getDeltaMovement());
return;
}
super.travel(travelVector);
}
public void releaseJump() {
if (!this.level().isClientSide && isCharging) {
float power = chargeTicks / (float) MAX_CHARGE_TICKS;
if (power > 0.05F) {
Vec3 look = this.getLookAngle();
Vec3 current = this.getDeltaMovement();
double forwardAdd = power * 0.5D;
double vertical = 0.55D + power * 0.9D;
double forwardAdd = 0.18D + power * 0.42D;
double vertical = 0.28D + power * 0.62D;
this.setDeltaMovement(
current.x + look.x * forwardAdd,
vertical,
current.y + vertical,
current.z + look.z * forwardAdd
);
this.hasImpulse = true;
@@ -517,6 +558,7 @@ public class DorrieEntity extends Animal implements GeoEntity {
private boolean canUseQuirkAnimation() {
return this.isInWater()
&& !this.isUnderWater()
&& this.getControllingPassenger() == null
&& !this.isLeaping
&& this.landAnimTicks <= 0
@@ -524,6 +566,72 @@ public class DorrieEntity extends Animal implements GeoEntity {
&& !this.isMovingForAnimation(null);
}
@Nullable
private Vec3 findWaterSurfaceTarget(double radius, int minYDelta, int maxYDelta) {
int baseY = Mth.floor(this.getY());
for (int attempt = 0; attempt < 12; attempt++) {
double offsetX = (this.random.nextDouble() - 0.5D) * radius * 2.0D;
double offsetZ = (this.random.nextDouble() - 0.5D) * radius * 2.0D;
int x = Mth.floor(this.getX() + offsetX);
int z = Mth.floor(this.getZ() + offsetZ);
Vec3 target = this.findWaterSurfaceTarget(x, z, baseY + minYDelta, baseY + maxYDelta);
if (target != null) {
return target;
}
}
return this.findWaterSurfaceTarget(Mth.floor(this.getX()), Mth.floor(this.getZ()),
baseY + minYDelta, baseY + maxYDelta);
}
@Nullable
private Vec3 findWaterSurfaceTarget(int x, int z, int minY, int maxY) {
int top = Math.min(maxY, this.level().getMaxBuildHeight() - 2);
int bottom = Math.max(minY, this.level().getMinBuildHeight() + 1);
for (int y = top; y >= bottom; y--) {
BlockPos pos = BlockPos.containing(x, y, z);
if (!this.isWaterAt(pos) || this.isWaterAt(pos.above())) {
continue;
}
return new Vec3(x + 0.5D, y + 0.85D, z + 0.5D);
}
return null;
}
@Nullable
private Vec3 findSubmergedTarget(double radius, int minYDelta, int maxYDelta) {
int baseY = Mth.floor(this.getY());
for (int attempt = 0; attempt < 16; attempt++) {
double offsetX = (this.random.nextDouble() - 0.5D) * radius * 2.0D;
double offsetZ = (this.random.nextDouble() - 0.5D) * radius * 2.0D;
int x = Mth.floor(this.getX() + offsetX);
int z = Mth.floor(this.getZ() + offsetZ);
Vec3 target = this.findSubmergedTarget(x, z, baseY + minYDelta, baseY + maxYDelta);
if (target != null) {
return target;
}
}
return this.findSubmergedTarget(Mth.floor(this.getX()), Mth.floor(this.getZ()),
baseY + minYDelta, baseY + maxYDelta);
}
@Nullable
private Vec3 findSubmergedTarget(int x, int z, int minY, int maxY) {
int top = Math.min(maxY, this.level().getMaxBuildHeight() - 2);
int bottom = Math.max(minY, this.level().getMinBuildHeight() + 1);
for (int y = top; y >= bottom; y--) {
BlockPos pos = BlockPos.containing(x, y, z);
if (!this.isWaterAt(pos) || !this.isWaterAt(pos.below())) {
continue;
}
return new Vec3(x + 0.5D, y + 0.3D, z + 0.5D);
}
return null;
}
private boolean isWaterAt(BlockPos pos) {
return this.level().getFluidState(pos).is(FluidTags.WATER);
}
private boolean isMovingForAnimation(@Nullable AnimationState<DorrieEntity> state) {
if (state != null && state.isMoving()) {
return true;
@@ -535,6 +643,147 @@ public class DorrieEntity extends Animal implements GeoEntity {
|| this.getNavigation().isInProgress();
}
private class DolphinSwimGoal extends Goal {
private static final int COOLDOWN_MIN_TICKS = 120;
private static final int COOLDOWN_MAX_TICKS = 240;
private static final int DIVE_MIN_TICKS = 50;
private static final int DIVE_MAX_TICKS = 110;
private static final int ROAM_MIN_TICKS = 70;
private static final int ROAM_MAX_TICKS = 140;
private static final int TARGET_REFRESH_TICKS = 20;
private static final double AQUATIC_SPEED = 1.15D;
private static final double SEARCH_RADIUS = 12.0D;
@Nullable
private Vec3 targetPos;
private int phaseTicks;
private int targetRefreshTicks;
private int cooldownTicks;
private Phase phase = Phase.ROAM;
DolphinSwimGoal() {
this.setFlags(EnumSet.of(Flag.MOVE, Flag.LOOK));
}
@Override
public boolean canUse() {
if (DorrieEntity.this.level().isClientSide
|| DorrieEntity.this.isVehicle()
|| DorrieEntity.this.getControllingPassenger() != null
|| DorrieEntity.this.isCharging
|| DorrieEntity.this.isLeaping
|| DorrieEntity.this.landAnimTicks > 0
|| DorrieEntity.this.getTarget() != null) {
return false;
}
if (!DorrieEntity.this.isInWater()) {
return false;
}
if (this.cooldownTicks > 0) {
this.cooldownTicks--;
return false;
}
if (DorrieEntity.this.random.nextInt(6) != 0) {
return false;
}
this.targetPos = DorrieEntity.this.isUnderWater()
? DorrieEntity.this.findSubmergedTarget(SEARCH_RADIUS, -5, 2)
: DorrieEntity.this.findSubmergedTarget(SEARCH_RADIUS, -5, 1);
return this.targetPos != null;
}
@Override
public boolean canContinueToUse() {
return this.targetPos != null
&& !DorrieEntity.this.isVehicle()
&& DorrieEntity.this.getControllingPassenger() == null
&& DorrieEntity.this.isAlive()
&& DorrieEntity.this.isInWater();
}
@Override
public void start() {
this.phase = DorrieEntity.this.isUnderWater() ? Phase.ROAM : Phase.DIVE;
this.phaseTicks = this.phase == Phase.DIVE
? DorrieEntity.this.random.nextInt(DIVE_MAX_TICKS - DIVE_MIN_TICKS + 1) + DIVE_MIN_TICKS
: DorrieEntity.this.random.nextInt(ROAM_MAX_TICKS - ROAM_MIN_TICKS + 1) + ROAM_MIN_TICKS;
this.targetRefreshTicks = 0;
if (this.targetPos == null) {
this.targetPos = this.pickTargetForPhase();
}
if (this.targetPos == null) {
this.stop();
}
}
@Override
public void stop() {
this.targetPos = null;
this.phaseTicks = 0;
this.targetRefreshTicks = 0;
DorrieEntity.this.getNavigation().stop();
this.cooldownTicks = DorrieEntity.this.random.nextInt(COOLDOWN_MAX_TICKS - COOLDOWN_MIN_TICKS + 1) + COOLDOWN_MIN_TICKS;
}
@Override
public void tick() {
if (this.targetPos == null) {
this.stop();
return;
}
if (this.phaseTicks > 0) {
this.phaseTicks--;
}
if (this.targetRefreshTicks > 0) {
this.targetRefreshTicks--;
}
if (this.phase == Phase.DIVE && this.phaseTicks <= 0) {
this.phase = Phase.ROAM;
this.phaseTicks = DorrieEntity.this.random.nextInt(ROAM_MAX_TICKS - ROAM_MIN_TICKS + 1) + ROAM_MIN_TICKS;
this.targetPos = this.pickTargetForPhase();
} else if (this.phase == Phase.ROAM && this.phaseTicks <= 0) {
this.phase = Phase.SURFACE;
this.phaseTicks = DorrieEntity.this.random.nextInt(DIVE_MAX_TICKS - DIVE_MIN_TICKS + 1) + DIVE_MIN_TICKS;
this.targetPos = DorrieEntity.this.findWaterSurfaceTarget(SEARCH_RADIUS, -1, 4);
} else if (this.phase == Phase.SURFACE && this.phaseTicks <= 0 && this.isCloseToTarget()) {
this.stop();
return;
}
if (this.targetPos == null || this.targetRefreshTicks <= 0 || this.isCloseToTarget()) {
this.targetPos = this.pickTargetForPhase();
this.targetRefreshTicks = TARGET_REFRESH_TICKS;
}
if (this.targetPos == null) {
this.stop();
return;
}
DorrieEntity.this.getLookControl().setLookAt(this.targetPos.x, this.targetPos.y, this.targetPos.z, 25.0F, 20.0F);
DorrieEntity.this.getNavigation().moveTo(this.targetPos.x, this.targetPos.y, this.targetPos.z, AQUATIC_SPEED);
}
@Nullable
private Vec3 pickTargetForPhase() {
return this.phase == Phase.SURFACE
? DorrieEntity.this.findWaterSurfaceTarget(SEARCH_RADIUS, -1, 4)
: DorrieEntity.this.findSubmergedTarget(SEARCH_RADIUS, -5, 2);
}
private boolean isCloseToTarget() {
return this.targetPos != null && DorrieEntity.this.distanceToSqr(this.targetPos) < 4.0D;
}
private enum Phase {
DIVE,
ROAM,
SURFACE
}
}
private static class HuntCheepGoal extends Goal {
private static final int COOLDOWN_TICKS = 600;
private static final double HUNT_RANGE = 16.0D;
@@ -108,6 +108,15 @@ public class EmperorScorpionEntity extends Monster implements GeoEntity {
.add(Attributes.FOLLOW_RANGE, AntarchySettings.emperorScorpionFollowRange());
}
@Override
public boolean fireImmune() {
return true;
}
@Override
public void lavaHurt() {
}
public static boolean canSpawn(EntityType<EmperorScorpionEntity> entityType, ServerLevelAccessor level,
MobSpawnType spawnReason, BlockPos pos, RandomSource random) {
if (spawnReason == MobSpawnType.SPAWN_EGG
@@ -11,6 +11,7 @@ import net.minecraft.sounds.SoundEvents;
import net.minecraft.sounds.SoundSource;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.Nullable;
@@ -34,10 +35,19 @@ public final class PermanentPortalManager {
return;
}
shape.fill(serverLevel);
Vec3 center = shape.center();
serverLevel.playSound(null, center.x, center.y, center.z, SoundEvents.END_PORTAL_SPAWN, SoundSource.BLOCKS, 0.9F, 0.9F);
spawnActivationParticles(serverLevel, type, center);
activatePortal(serverLevel, type, shape, SoundEvents.END_PORTAL_SPAWN, 0.9F, 0.9F);
}
public static boolean tryIgnitePortal(Level level, BlockPos triggerPos, PermanentPortalType type) {
PermanentPortalShape shape = PermanentPortalShape.findInactiveNear(level, triggerPos, type);
if (shape == null) {
return false;
}
if (level instanceof ServerLevel serverLevel) {
activatePortal(serverLevel, type, shape, SoundEvents.FLINTANDSTEEL_USE, 0.9F, 1.0F);
}
return true;
}
public static void handleEntityInsidePortal(Entity entity, PermanentPortalType type) {
@@ -67,7 +77,7 @@ public final class PermanentPortalManager {
WARMUPS.remove(entity.getUUID());
PermanentPortalTeleporter.teleport(entity, type);
if (entity.level() instanceof ServerLevel currentLevel) {
currentLevel.playSound(null, entity.getX(), entity.getY(), entity.getZ(), SoundEvents.ENDERMAN_TELEPORT, SoundSource.PLAYERS, 0.8F, 1.0F);
currentLevel.playSound(null, entity.getX(), entity.getY(), entity.getZ(), SoundEvents.PORTAL_TRAVEL, SoundSource.PLAYERS, 0.8F, 1.0F);
}
}
@@ -121,6 +131,13 @@ public final class PermanentPortalManager {
}
}
private static void activatePortal(ServerLevel level, PermanentPortalType type, PermanentPortalShape shape, net.minecraft.sounds.SoundEvent sound, float volume, float pitch) {
shape.fill(level);
Vec3 center = shape.center();
level.playSound(null, center.x, center.y, center.z, sound, SoundSource.BLOCKS, volume, pitch);
spawnActivationParticles(level, type, center);
}
private static final class PortalWarmup {
private PermanentPortalType type;
private long lastSeenTick;
@@ -8,6 +8,7 @@ import net.minecraft.server.level.ServerLevel;
import net.minecraft.tags.TagKey;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
import net.minecraft.world.phys.AABB;
@@ -18,6 +19,7 @@ public final class PermanentPortalShape {
public static final int MIN_WIDTH = 2;
public static final int MIN_HEIGHT = 3;
public static final int MAX_SIZE = 21;
public static final int ACTIVE_SEARCH_RADIUS = 8;
private final PermanentPortalType type;
private final Direction.Axis axis;
@@ -38,12 +40,52 @@ public final class PermanentPortalShape {
return find(level, interiorPos, type, axis, state -> state.isAir());
}
@Nullable
public static PermanentPortalShape findInactiveNear(BlockGetter level, BlockPos center, PermanentPortalType type) {
for (int x = center.getX() - 2; x <= center.getX() + 2; x++) {
for (int y = center.getY() - 2; y <= center.getY() + 2; y++) {
for (int z = center.getZ() - 2; z <= center.getZ() + 2; z++) {
BlockPos candidate = new BlockPos(x, y, z);
PermanentPortalShape xShape = findInactive(level, candidate, type, Direction.Axis.X);
if (xShape != null) {
return xShape;
}
PermanentPortalShape zShape = findInactive(level, candidate, type, Direction.Axis.Z);
if (zShape != null) {
return zShape;
}
}
}
}
return null;
}
@Nullable
public static PermanentPortalShape findActive(BlockGetter level, BlockPos interiorPos, PermanentPortalType type, Direction.Axis axis) {
Block portalBlock = type.portalBlock();
return find(level, interiorPos, type, axis, state -> state.is(portalBlock));
}
@Nullable
public static PermanentPortalShape findActiveNear(BlockGetter level, BlockPos center, PermanentPortalType type) {
for (int x = center.getX() - ACTIVE_SEARCH_RADIUS; x <= center.getX() + ACTIVE_SEARCH_RADIUS; x++) {
for (int y = center.getY() - ACTIVE_SEARCH_RADIUS; y <= center.getY() + ACTIVE_SEARCH_RADIUS; y++) {
for (int z = center.getZ() - ACTIVE_SEARCH_RADIUS; z <= center.getZ() + ACTIVE_SEARCH_RADIUS; z++) {
BlockPos candidate = new BlockPos(x, y, z);
PermanentPortalShape xShape = findActive(level, candidate, type, Direction.Axis.X);
if (xShape != null) {
return xShape;
}
PermanentPortalShape zShape = findActive(level, candidate, type, Direction.Axis.Z);
if (zShape != null) {
return zShape;
}
}
}
}
return null;
}
@Nullable
private static PermanentPortalShape find(
BlockGetter level,
@@ -159,6 +201,50 @@ public final class PermanentPortalShape {
this.forEachInterior(pos -> level.setBlock(pos, portalState, Block.UPDATE_ALL));
}
@Nullable
public static PermanentPortalShape create(ServerLevel level, BlockPos bottomLeft, PermanentPortalType type, Direction.Axis axis) {
if (axis == Direction.Axis.Y || !canCreate(level, bottomLeft, type, axis)) {
return null;
}
BlockState frameState = type.platformBlock().defaultBlockState();
BlockState portalState = type.portalBlock().defaultBlockState().setValue(BlockStateProperties.HORIZONTAL_AXIS, axis);
Direction widthDirection = axis == Direction.Axis.X ? Direction.EAST : Direction.SOUTH;
for (int y = -1; y <= 3; y++) {
for (int x = -1; x <= 2; x++) {
BlockPos pos = bottomLeft.relative(widthDirection, x).above(y);
if (x == -1 || x == 2 || y == -1 || y == 3) {
level.setBlock(pos, frameState, Block.UPDATE_ALL);
} else {
level.setBlock(pos, portalState, Block.UPDATE_ALL);
}
}
}
return new PermanentPortalShape(type, axis, bottomLeft.immutable(), 2, 3);
}
private static boolean canCreate(BlockGetter level, BlockPos bottomLeft, PermanentPortalType type, Direction.Axis axis) {
Direction widthDirection = axis == Direction.Axis.X ? Direction.EAST : Direction.SOUTH;
Block frameBlock = type.platformBlock();
for (int y = -1; y <= 3; y++) {
for (int x = -1; x <= 2; x++) {
BlockPos pos = bottomLeft.relative(widthDirection, x).above(y);
BlockState state = level.getBlockState(pos);
boolean frameSpot = x == -1 || x == 2 || y == -1 || y == 3;
if (frameSpot) {
if (!state.isAir() && !state.is(frameBlock) && !state.canBeReplaced()) {
return false;
}
} else if (!state.isAir() && !state.canBeReplaced() && !state.is(Blocks.AIR)) {
return false;
}
}
}
return true;
}
public boolean contains(BlockPos pos) {
if (pos.getY() < this.bottomLeft.getY() || pos.getY() >= this.bottomLeft.getY() + this.height) {
return false;
@@ -1,14 +1,17 @@
package com.craisinlord.antarchy.content.portal;
import com.craisinlord.antarchy.config.AntarchySettings;
import java.util.LinkedHashSet;
import java.util.Set;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.resources.ResourceKey;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.util.Mth;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.vehicle.DismountHelper;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.levelgen.Heightmap;
@@ -28,18 +31,23 @@ public final class PermanentPortalTeleporter {
return;
}
ServerLevel destination = resolveDestination(sourceLevel, type);
if (destination == null) {
return;
}
Vec3 arrival = findArrivalPosition(entity, destination, entity.blockPosition(), type.platformBlock().defaultBlockState());
Entity movedEntity = moveEntity(entity, destination, arrival);
DimensionTransition transition = createTransition(sourceLevel, entity, entity.blockPosition(), type);
Entity movedEntity = moveEntity(entity, transition);
if (movedEntity != null) {
movedEntity.setPortalCooldown();
}
}
public static DimensionTransition createTransition(ServerLevel sourceLevel, Entity entity, BlockPos portalPos, PermanentPortalType type) {
ServerLevel destination = resolveDestination(sourceLevel, type);
if (destination == null) {
return new DimensionTransition(sourceLevel, entity, DimensionTransition.DO_NOTHING);
}
Vec3 arrival = findArrivalPosition(entity, destination, portalPos, type);
return new DimensionTransition(destination, arrival, Vec3.ZERO, entity.getYRot(), entity.getXRot(), DimensionTransition.PLAY_PORTAL_SOUND);
}
@Nullable
private static ServerLevel resolveDestination(ServerLevel sourceLevel, PermanentPortalType type) {
if (sourceLevel.dimension() == type.primaryDimension()) {
@@ -48,13 +56,31 @@ public final class PermanentPortalTeleporter {
return sourceLevel.getServer().getLevel(type.primaryDimension());
}
private static Vec3 findArrivalPosition(Entity entity, ServerLevel destination, BlockPos preferredPos, BlockState platformState) {
private static Vec3 findArrivalPosition(Entity entity, ServerLevel destination, BlockPos preferredPos, PermanentPortalType type) {
PermanentPortalShape active = PermanentPortalShape.findActiveNear(destination, preferredPos, type);
if (active != null) {
return active.center();
}
Vec3 safe = findSafeArrivalPosition(entity, destination, preferredPos);
return safe != null ? safe : createFallbackPlatform(destination, preferredPos, platformState);
if (safe != null) {
PermanentPortalShape created = createReturnPortal(destination, safe, type);
if (created != null) {
return created.center();
}
return safe;
}
return createFallbackPlatform(destination, preferredPos, type.platformBlock().defaultBlockState());
}
@Nullable
private static Vec3 findSafeArrivalPosition(Entity entity, ServerLevel destination, BlockPos preferredPos) {
int[] yRange = getDimensionYRange(destination);
if (yRange != null) {
return findSafeArrivalPositionInYRange(entity, destination, preferredPos, yRange[0], yRange[1]);
}
Set<BlockPos> candidates = new LinkedHashSet<>();
addCandidate(candidates, preferredPos);
@@ -88,6 +114,29 @@ public final class PermanentPortalTeleporter {
return null;
}
@Nullable
private static Vec3 findSafeArrivalPositionInYRange(Entity entity, ServerLevel destination, BlockPos preferredPos, int minY, int maxY) {
for (int radius = 0; radius <= SEARCH_RADIUS; radius++) {
for (int xOff = -radius; xOff <= radius; xOff++) {
for (int zOff = -radius; zOff <= radius; zOff++) {
if (radius > 0 && Math.abs(xOff) != radius && Math.abs(zOff) != radius) {
continue;
}
int x = preferredPos.getX() + xOff;
int z = preferredPos.getZ() + zOff;
for (int y = maxY; y >= minY + 1; y--) {
Vec3 safePos = tryFindSafePosition(entity, destination, new BlockPos(x, y, z));
if (safePos != null && safePos.y >= minY && safePos.y <= maxY) {
return safePos;
}
}
}
}
}
return null;
}
private static void addCandidate(Set<BlockPos> candidates, @Nullable BlockPos pos) {
if (pos == null) {
return;
@@ -103,13 +152,13 @@ public final class PermanentPortalTeleporter {
@Nullable
private static Vec3 tryFindSafePosition(Entity entity, ServerLevel destination, BlockPos candidate) {
Vec3 safe = DismountHelper.findSafeDismountLocation(entity.getType(), destination, candidate, true);
if (safe != null) {
if (safe != null && isValidArrivalPosition(destination, safe)) {
return safe;
}
for (Direction direction : Direction.Plane.HORIZONTAL) {
safe = DismountHelper.findSafeDismountLocation(entity.getType(), destination, candidate.relative(direction), true);
if (safe != null) {
if (safe != null && isValidArrivalPosition(destination, safe)) {
return safe;
}
}
@@ -134,7 +183,60 @@ public final class PermanentPortalTeleporter {
}
@Nullable
private static Entity moveEntity(Entity entity, ServerLevel destination, Vec3 destinationPos) {
private static PermanentPortalShape createReturnPortal(ServerLevel destination, Vec3 safePos, PermanentPortalType type) {
BlockPos preferred = BlockPos.containing(safePos);
int minY = destination.getMinBuildHeight() + 2;
int maxY = destination.getMaxBuildHeight() - 4;
int clampedY = Mth.clamp(preferred.getY(), minY, maxY);
for (int radius = 0; radius <= 4; radius++) {
for (int xOff = -radius; xOff <= radius; xOff++) {
for (int zOff = -radius; zOff <= radius; zOff++) {
if (radius > 0 && Math.abs(xOff) != radius && Math.abs(zOff) != radius) {
continue;
}
BlockPos anchor = new BlockPos(preferred.getX() + xOff, clampedY, preferred.getZ() + zOff);
PermanentPortalShape xShape = PermanentPortalShape.create(destination, anchor, type, Direction.Axis.X);
if (xShape != null) {
return xShape;
}
PermanentPortalShape zShape = PermanentPortalShape.create(destination, anchor, type, Direction.Axis.Z);
if (zShape != null) {
return zShape;
}
}
}
}
return null;
}
@Nullable
private static int[] getDimensionYRange(ServerLevel destination) {
ResourceKey<Level> dim = destination.dimension();
if (dim == AntarchySettings.termiteDestinationDimension()) {
return new int[]{100, 200};
}
if (dim == AntarchySettings.brownAntDestinationDimension()) {
return new int[]{75, 120};
}
return null;
}
private static boolean isValidArrivalPosition(ServerLevel destination, Vec3 safePos) {
BlockPos standPos = BlockPos.containing(safePos);
if (destination.dimension() == AntarchySettings.termiteDestinationDimension()
|| destination.dimension() == AntarchySettings.redAntDestinationDimension()) {
return !destination.getBlockState(standPos.below()).is(net.minecraft.world.level.block.Blocks.BEDROCK);
}
return true;
}
@Nullable
private static Entity moveEntity(Entity entity, DimensionTransition transition) {
ServerLevel destination = transition.newLevel();
Vec3 destinationPos = transition.pos();
if (entity instanceof ServerPlayer player) {
player.teleportTo(destination, destinationPos.x, destinationPos.y, destinationPos.z, player.getYRot(), player.getXRot());
return player;
@@ -145,6 +247,6 @@ public final class PermanentPortalTeleporter {
return entity;
}
return entity.changeDimension(new DimensionTransition(destination, destinationPos, Vec3.ZERO, entity.getYRot(), entity.getXRot(), DimensionTransition.DO_NOTHING));
return entity.changeDimension(transition);
}
}
@@ -0,0 +1,53 @@
package com.craisinlord.antarchy.mixins;
import com.craisinlord.antarchy.config.AntarchySettings;
import com.craisinlord.antarchy.content.portal.PermanentPortalManager;
import com.craisinlord.antarchy.content.portal.PermanentPortalType;
import net.minecraft.core.BlockPos;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.entity.EquipmentSlot;
import net.minecraft.world.item.FlintAndSteelItem;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.context.UseOnContext;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.state.BlockState;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(FlintAndSteelItem.class)
public abstract class FlintAndSteelPortalIgnitionMixin {
@Inject(method = "useOn", at = @At("HEAD"), cancellable = true)
private void antarchy$ignitePermanentPortal(UseOnContext context, CallbackInfoReturnable<InteractionResult> cir) {
if (!AntarchySettings.permanentPortalsEnabled() || !AntarchySettings.permanentPortalsFlintAndSteelEnabled()) {
return;
}
Level level = context.getLevel();
BlockPos clickedPos = context.getClickedPos();
BlockState clickedState = level.getBlockState(clickedPos);
for (PermanentPortalType type : PermanentPortalType.values()) {
if (!type.isEnabled() || !clickedState.is(type.frameTag())) {
continue;
}
if (!PermanentPortalManager.tryIgnitePortal(level, clickedPos, type)) {
continue;
}
if (!level.isClientSide() && context.getPlayer() != null) {
ItemStack stack = context.getItemInHand();
stack.hurtAndBreak(1, context.getPlayer(), handToSlot(context.getHand()));
}
cir.setReturnValue(InteractionResult.sidedSuccess(level.isClientSide()));
return;
}
}
private static EquipmentSlot handToSlot(InteractionHand hand) {
return hand == InteractionHand.MAIN_HAND ? EquipmentSlot.MAINHAND : EquipmentSlot.OFFHAND;
}
}
@@ -13,6 +13,7 @@
"DreamSandLowGravityMixin",
"FireVisualBaseFireBlockMixin",
"FireVisualEntityMixin",
"FlintAndSteelPortalIgnitionMixin",
"GlowVinesUnderLeavesMixin",
"NightmareSwordDamageMixin",
"ServerPlayerGameModeMixin",
@@ -5,7 +5,7 @@
"description": {
"identifier": "geometry.unknown",
"texture_width": 128,
"texture_height": 128,
"texture_height": 256,
"visible_bounds_width": 7,
"visible_bounds_height": 4.5,
"visible_bounds_offset": [0, 1.75, 0]
Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.5 KiB

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

@@ -170,6 +170,7 @@ public final class AntarchyConfigModuleFabric {
return name.equals("disableInfinityBookPortalCreation")
|| name.equals("rainbowAntsLeadToInfinityDimensions")
|| name.equals("permanentPortalsEnabled")
|| name.equals("permanentPortalsFlintAndSteelEnabled")
|| name.equals("elythiaPortalEnabled")
|| name.equals("thoraxisPortalEnabled")
|| name.equals("cavarynPortalEnabled")
@@ -334,6 +334,7 @@ public final class AntarchyConfigModuleNeoforge {
AntarchySettings.setDiamondMinecartMobDamageEnabled(AntarchyMiscConfig.diamondMinecartMobDamageEnabled());
AntarchySettings.setDiamondMinecartMaxMobDamage(AntarchyMiscConfig.diamondMinecartMaxMobDamage());
AntarchySettings.setPermanentPortalsEnabled(AntarchyMiscConfig.permanentPortalsEnabled());
AntarchySettings.setPermanentPortalsFlintAndSteelEnabled(AntarchyMiscConfig.permanentPortalsFlintAndSteelEnabled());
AntarchySettings.setElythiaPortalEnabled(AntarchyMiscConfig.elythiaPortalEnabled());
AntarchySettings.setThoraxisPortalEnabled(AntarchyMiscConfig.thoraxisPortalEnabled());
AntarchySettings.setCavarynPortalEnabled(AntarchyMiscConfig.cavarynPortalEnabled());
@@ -27,6 +27,7 @@ public final class AntarchyMiscConfig {
// Permanent portals
private static final ModConfigSpec.BooleanValue PERMANENT_PORTALS_ENABLED;
private static final ModConfigSpec.BooleanValue PERMANENT_PORTALS_FLINT_AND_STEEL_ENABLED;
private static final ModConfigSpec.BooleanValue ELYTHIA_PORTAL_ENABLED;
private static final ModConfigSpec.BooleanValue THORAXIS_PORTAL_ENABLED;
private static final ModConfigSpec.BooleanValue CAVARYN_PORTAL_ENABLED;
@@ -110,6 +111,7 @@ public final class AntarchyMiscConfig {
b.push("permanentPortals");
PERMANENT_PORTALS_ENABLED = b.comment("Master toggle for sacrifice-activated permanent portals.").define("permanentPortalsEnabled", true);
PERMANENT_PORTALS_FLINT_AND_STEEL_ENABLED = b.comment("Whether flint and steel can ignite permanent portal frames.").define("permanentPortalsFlintAndSteelEnabled", false);
ELYTHIA_PORTAL_ENABLED = b.comment("Whether mossy ouranwood wood portals to Elythia can activate and function.").define("elythiaPortalEnabled", true);
THORAXIS_PORTAL_ENABLED = b.comment("Whether Nyxite portals to Thoraxis can activate and function.").define("thoraxisPortalEnabled", true);
CAVARYN_PORTAL_ENABLED = b.comment("Whether Myrmite portals to Cavaryn can activate and function.").define("cavarynPortalEnabled", true);
@@ -167,6 +169,7 @@ public final class AntarchyMiscConfig {
static boolean disableInfinityBookPortalCreation() { return DISABLE_INFINITY_BOOK_PORTAL_CREATION.get(); }
static boolean rainbowAntsLeadToInfinityDimensions() { return RAINBOW_ANTS_LEAD_TO_INFINITY_DIMENSIONS.get(); }
public static boolean permanentPortalsEnabled() { return PERMANENT_PORTALS_ENABLED.get(); }
public static boolean permanentPortalsFlintAndSteelEnabled() { return PERMANENT_PORTALS_FLINT_AND_STEEL_ENABLED.get(); }
public static boolean elythiaPortalEnabled() { return ELYTHIA_PORTAL_ENABLED.get(); }
public static boolean thoraxisPortalEnabled() { return THORAXIS_PORTAL_ENABLED.get(); }
public static boolean cavarynPortalEnabled() { return CAVARYN_PORTAL_ENABLED.get(); }