diff --git a/common/src/main/java/com/craisinlord/antarchy/config/AntarchySettings.java b/common/src/main/java/com/craisinlord/antarchy/config/AntarchySettings.java index a5ab19e5..db23fffd 100644 --- a/common/src/main/java/com/craisinlord/antarchy/config/AntarchySettings.java +++ b/common/src/main/java/com/craisinlord/antarchy/config/AntarchySettings.java @@ -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; } diff --git a/common/src/main/java/com/craisinlord/antarchy/content/block/AntDimensionPortalBlock.java b/common/src/main/java/com/craisinlord/antarchy/content/block/AntDimensionPortalBlock.java index eca7464d..27eeff06 100644 --- a/common/src/main/java/com/craisinlord/antarchy/content/block/AntDimensionPortalBlock.java +++ b/common/src/main/java/com/craisinlord/antarchy/content/block/AntDimensionPortalBlock.java @@ -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 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); diff --git a/common/src/main/java/com/craisinlord/antarchy/content/entity/DorrieEntity.java b/common/src/main/java/com/craisinlord/antarchy/content/entity/DorrieEntity.java index 613e1fd1..09cc8fed 100644 --- a/common/src/main/java/com/craisinlord/antarchy/content/entity/DorrieEntity.java +++ b/common/src/main/java/com/craisinlord/antarchy/content/entity/DorrieEntity.java @@ -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 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; diff --git a/common/src/main/java/com/craisinlord/antarchy/content/entity/EmperorScorpionEntity.java b/common/src/main/java/com/craisinlord/antarchy/content/entity/EmperorScorpionEntity.java index 7f815a30..421c14de 100644 --- a/common/src/main/java/com/craisinlord/antarchy/content/entity/EmperorScorpionEntity.java +++ b/common/src/main/java/com/craisinlord/antarchy/content/entity/EmperorScorpionEntity.java @@ -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 entityType, ServerLevelAccessor level, MobSpawnType spawnReason, BlockPos pos, RandomSource random) { if (spawnReason == MobSpawnType.SPAWN_EGG diff --git a/common/src/main/java/com/craisinlord/antarchy/content/portal/PermanentPortalManager.java b/common/src/main/java/com/craisinlord/antarchy/content/portal/PermanentPortalManager.java index 04cf7aab..9fc76074 100644 --- a/common/src/main/java/com/craisinlord/antarchy/content/portal/PermanentPortalManager.java +++ b/common/src/main/java/com/craisinlord/antarchy/content/portal/PermanentPortalManager.java @@ -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; diff --git a/common/src/main/java/com/craisinlord/antarchy/content/portal/PermanentPortalShape.java b/common/src/main/java/com/craisinlord/antarchy/content/portal/PermanentPortalShape.java index 879dc473..9d84f0fa 100644 --- a/common/src/main/java/com/craisinlord/antarchy/content/portal/PermanentPortalShape.java +++ b/common/src/main/java/com/craisinlord/antarchy/content/portal/PermanentPortalShape.java @@ -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; diff --git a/common/src/main/java/com/craisinlord/antarchy/content/portal/PermanentPortalTeleporter.java b/common/src/main/java/com/craisinlord/antarchy/content/portal/PermanentPortalTeleporter.java index 0eabe59c..bfd18721 100644 --- a/common/src/main/java/com/craisinlord/antarchy/content/portal/PermanentPortalTeleporter.java +++ b/common/src/main/java/com/craisinlord/antarchy/content/portal/PermanentPortalTeleporter.java @@ -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 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 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 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); } } diff --git a/common/src/main/java/com/craisinlord/antarchy/mixins/FlintAndSteelPortalIgnitionMixin.java b/common/src/main/java/com/craisinlord/antarchy/mixins/FlintAndSteelPortalIgnitionMixin.java new file mode 100644 index 00000000..58bb411b --- /dev/null +++ b/common/src/main/java/com/craisinlord/antarchy/mixins/FlintAndSteelPortalIgnitionMixin.java @@ -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 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; + } +} diff --git a/common/src/main/resources/antarchy-common.mixins.json b/common/src/main/resources/antarchy-common.mixins.json index f9a8a757..2a281ed4 100644 --- a/common/src/main/resources/antarchy-common.mixins.json +++ b/common/src/main/resources/antarchy-common.mixins.json @@ -13,6 +13,7 @@ "DreamSandLowGravityMixin", "FireVisualBaseFireBlockMixin", "FireVisualEntityMixin", + "FlintAndSteelPortalIgnitionMixin", "GlowVinesUnderLeavesMixin", "NightmareSwordDamageMixin", "ServerPlayerGameModeMixin", diff --git a/common/src/main/resources/assets/antarchy/geo/dorrie.geo.json b/common/src/main/resources/assets/antarchy/geo/dorrie.geo.json index 8e16dbbb..074a5581 100644 --- a/common/src/main/resources/assets/antarchy/geo/dorrie.geo.json +++ b/common/src/main/resources/assets/antarchy/geo/dorrie.geo.json @@ -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] diff --git a/common/src/main/resources/assets/antarchy/textures/entity/dorrie.png b/common/src/main/resources/assets/antarchy/textures/entity/dorrie.png index e4750592..98b9b53c 100644 Binary files a/common/src/main/resources/assets/antarchy/textures/entity/dorrie.png and b/common/src/main/resources/assets/antarchy/textures/entity/dorrie.png differ diff --git a/common/src/main/resources/assets/antarchy/textures/entity/dorrie_saddle.png b/common/src/main/resources/assets/antarchy/textures/entity/dorrie_saddle.png index b9f00815..8df880c9 100644 Binary files a/common/src/main/resources/assets/antarchy/textures/entity/dorrie_saddle.png and b/common/src/main/resources/assets/antarchy/textures/entity/dorrie_saddle.png differ diff --git a/fabric/src/main/java/com/craisinlord/antarchy/fabric/AntarchyConfigModuleFabric.java b/fabric/src/main/java/com/craisinlord/antarchy/fabric/AntarchyConfigModuleFabric.java index 07550736..be11f333 100644 --- a/fabric/src/main/java/com/craisinlord/antarchy/fabric/AntarchyConfigModuleFabric.java +++ b/fabric/src/main/java/com/craisinlord/antarchy/fabric/AntarchyConfigModuleFabric.java @@ -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") diff --git a/neoforge/src/main/java/com/craisinlord/antarchy/neoforge/AntarchyConfigModuleNeoforge.java b/neoforge/src/main/java/com/craisinlord/antarchy/neoforge/AntarchyConfigModuleNeoforge.java index f13309c6..c2e356a0 100644 --- a/neoforge/src/main/java/com/craisinlord/antarchy/neoforge/AntarchyConfigModuleNeoforge.java +++ b/neoforge/src/main/java/com/craisinlord/antarchy/neoforge/AntarchyConfigModuleNeoforge.java @@ -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()); diff --git a/neoforge/src/main/java/com/craisinlord/antarchy/neoforge/AntarchyMiscConfig.java b/neoforge/src/main/java/com/craisinlord/antarchy/neoforge/AntarchyMiscConfig.java index ad704183..04369feb 100644 --- a/neoforge/src/main/java/com/craisinlord/antarchy/neoforge/AntarchyMiscConfig.java +++ b/neoforge/src/main/java/com/craisinlord/antarchy/neoforge/AntarchyMiscConfig.java @@ -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(); }