fixed missing tags

added many underside structures
tweaked king and queen AI
fixed undervault and UndertrialSpawnerBlock
This commit is contained in:
craisinlord committed 2026-09-12 16:57:48 -05:00
1 parent 8812938d79
commit 377f3785af
76 files changed
+1503 -1000

No files matched your search

@@ -0,0 +1,186 @@
package com.craisinlord.antarchy.config;
import net.minecraft.resources.ResourceKey;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
public final class AntarchyConfigCatalog {
public enum Section {
MOBS,
TOOLS,
MISC
}
public enum ValueType {
BOOLEAN,
INTEGER,
DOUBLE,
FLOAT,
STRING
}
public record Definition(
String name,
Section section,
ValueType valueType,
Class<?> setterType,
Method getter,
Method setter
) {
}
private static final List<Definition> DEFINITIONS = buildDefinitions();
private AntarchyConfigCatalog() {
}
public static List<Definition> definitions() {
return DEFINITIONS;
}
public static Map<String, Definition> definitionsByName() {
Map<String, Definition> result = new LinkedHashMap<>();
for (Definition definition : DEFINITIONS) {
result.put(definition.name(), definition);
}
return Map.copyOf(result);
}
private static List<Definition> buildDefinitions() {
Map<String, Method> getters = Arrays.stream(AntarchySettings.class.getMethods())
.filter(AntarchyConfigCatalog::isPublicStatic)
.filter(method -> method.getParameterCount() == 0)
.filter(method -> method.getReturnType() != Void.TYPE)
.collect(java.util.stream.Collectors.toMap(
Method::getName,
method -> method,
(first, ignored) -> first,
LinkedHashMap::new));
return Arrays.stream(AntarchySettings.class.getMethods())
.filter(AntarchyConfigCatalog::isPublicStatic)
.filter(method -> method.getName().startsWith("set"))
.filter(method -> method.getParameterCount() == 1)
.sorted(java.util.Comparator.comparing(Method::getName))
.map(setter -> definitionFor(setter, getters))
.filter(java.util.Objects::nonNull)
.toList();
}
private static Definition definitionFor(Method setter, Map<String, Method> getters) {
String name = decapitalize(setter.getName().substring(3));
Method getter = getters.get(name);
if (getter == null) {
return null;
}
Class<?> setterType = setter.getParameterTypes()[0];
ValueType valueType = valueTypeFor(setterType, getter.getReturnType());
if (valueType == null) {
return null;
}
return new Definition(name, sectionFor(name), valueType, setterType, getter, setter);
}
private static ValueType valueTypeFor(Class<?> setterType, Class<?> getterType) {
if (setterType == boolean.class && getterType == boolean.class) {
return ValueType.BOOLEAN;
}
if (setterType == int.class && getterType == int.class) {
return ValueType.INTEGER;
}
if (setterType == double.class && getterType == double.class) {
return ValueType.DOUBLE;
}
if (setterType == float.class && getterType == float.class) {
return ValueType.FLOAT;
}
if (setterType == String.class && ResourceKey.class.isAssignableFrom(getterType)) {
return ValueType.STRING;
}
return null;
}
private static boolean isPublicStatic(Method method) {
int modifiers = method.getModifiers();
return Modifier.isPublic(modifiers) && Modifier.isStatic(modifiers);
}
private static String decapitalize(String value) {
if (value.isEmpty()) {
return value;
}
return Character.toLowerCase(value.charAt(0)) + value.substring(1);
}
private static Section sectionFor(String name) {
if (isMiscSetting(name)) {
return Section.MISC;
}
if (isToolSetting(name)) {
return Section.TOOLS;
}
return Section.MOBS;
}
private static boolean isMiscSetting(String name) {
return name.equals("disableInfinityBookPortalCreation")
|| name.equals("rainbowAntsLeadToInfinityDimensions")
|| name.equals("permanentPortalsEnabled")
|| name.equals("permanentPortalsFlintAndSteelEnabled")
|| name.equals("elythiaPortalEnabled")
|| name.equals("thoraxisPortalEnabled")
|| name.equals("cavarynPortalEnabled")
|| name.equals("hushweedSporeLifetimeSeconds")
|| name.equals("elythiaFireflyParticlesEnabled")
|| name.equals("duplicatorTreeEnabled")
|| name.equals("glowVinesUnderLeaves")
|| name.equals("swingThroughGrassEnabled")
|| name.equals("fabricKeybindingConflictFixEnabled")
|| name.equals("experimentalSettingsPopupDisabled")
|| name.equals("entitySpecificFireOverlayEnabled")
|| name.equals("dreamSandEnabled")
|| name.startsWith("dreamSand")
|| name.equals("ichorWitherEnabled")
|| name.startsWith("diamondMinecart")
|| name.startsWith("hoverboard");
}
private static boolean isToolSetting(String name) {
return name.equals("sizeChangingRaysEnabled")
|| name.startsWith("sizeRay")
|| name.equals("shrinkingPotionDelta")
|| name.equals("growthPotionDelta")
|| name.startsWith("ultimate")
|| name.startsWith("battleAxe")
|| name.startsWith("bigBertha")
|| name.startsWith("attitudeAdjuster")
|| name.startsWith("scorpionWhip")
|| name.startsWith("bloodCrystal")
|| name.startsWith("nightmareHelmet")
|| name.startsWith("nightmareChestplate")
|| name.startsWith("nightmareLeggings")
|| name.startsWith("nightmareBoots")
|| name.startsWith("nightmareArmorDreadAura")
|| name.startsWith("primordialArmor")
|| name.startsWith("nightmareSword")
|| name.startsWith("basiliskDagger")
|| name.startsWith("squidzooka")
|| name.equals("invertProjectilesFromInvertedPlayers")
|| name.startsWith("gravityGun")
|| name.startsWith("critterCage")
|| name.startsWith("minersDream")
|| name.equals("potentNyxiteInvertedDurationSeconds")
|| name.equals("corneaEarNightVisionSeconds")
|| name.startsWith("american")
|| name.startsWith("mogglesVision")
|| name.equals("ductTapeRepairPercentPerUse")
|| name.startsWith("fallenKingCrown");
}
}
@@ -12,25 +12,26 @@ public final class ConfigResetGuard {
private ConfigResetGuard() {
}
public static void wipeIfNeeded(Path configDir, Path... configFiles) {
public static void migrateIfNeeded(Path configDir, Path... configFiles) {
Path markerPath = configDir.resolve(MARKER_FILE_NAME);
if (readGeneration(markerPath) >= RESET_GENERATION) {
return;
}
for (Path configFile : configFiles) {
try {
if (Files.deleteIfExists(configFile)) {
Antarchy.LOGGER.info("Reset config file {} to defaults (config generation {})", configFile, RESET_GENERATION);
}
} catch (IOException e) {
Antarchy.LOGGER.warn("Failed to delete config file {} during config reset", configFile, e);
if (Files.exists(configFile)) {
Antarchy.LOGGER.info("Preserving config file {} during config migration (config generation {})", configFile, RESET_GENERATION);
}
}
writeGeneration(markerPath);
}
@Deprecated
public static void wipeIfNeeded(Path configDir, Path... configFiles) {
migrateIfNeeded(configDir, configFiles);
}
private static int readGeneration(Path markerPath) {
if (!Files.exists(markerPath)) {
return 0;
@@ -64,13 +64,10 @@ public final class UndertrialSpawnerBlock extends BaseEntityBlock {
@Override
public <T extends BlockEntity> BlockEntityTicker<T> getTicker(Level level, BlockState state, BlockEntityType<T> blockEntityType) {
if (this.blockEntityType == null || blockEntityType != this.blockEntityType.get()) {
return null;
}
if (level.isClientSide) {
return (BlockEntityTicker<T>) (BlockEntityTicker<UndertrialSpawnerBlockEntity>) UndertrialSpawnerBlockEntity::clientTick;
return createTickerHelper(blockEntityType, this.blockEntityType.get(), UndertrialSpawnerBlockEntity::clientTick);
}
return (BlockEntityTicker<T>) (BlockEntityTicker<UndertrialSpawnerBlockEntity>) ((tickLevel, tickPos, tickState, blockEntity) -> {
return createTickerHelper(blockEntityType, this.blockEntityType.get(), (tickLevel, tickPos, tickState, blockEntity) -> {
if (tickLevel instanceof net.minecraft.server.level.ServerLevel serverLevel) {
UndertrialSpawnerBlockEntity.serverTick(serverLevel, tickPos, tickState, blockEntity);
}
@@ -58,9 +58,16 @@ public final class QueenTrailSpawnMarkerBlockEntity extends BlockEntity {
}
boolean nearbyPlayer = level.players().stream().anyMatch(player -> !player.isSpectator()
&& player.distanceToSqr(marker.spawnPos.getX() + 0.5D, marker.spawnPos.getY() + 0.5D, marker.spawnPos.getZ() + 0.5D) <= ACTIVATION_DISTANCE_SQUARED);
if (!nearbyPlayer || !level.hasChunkAt(marker.spawnPos)) {
if (!nearbyPlayer) {
return;
}
// The Queen is large enough to span several chunks. Ensure the area around
// the arrival point is available before checking its collision box.
for (int chunkX = (marker.spawnPos.getX() - 16) >> 4; chunkX <= (marker.spawnPos.getX() + 16) >> 4; chunkX++) {
for (int chunkZ = (marker.spawnPos.getZ() - 16) >> 4; chunkZ <= (marker.spawnPos.getZ() + 16) >> 4; chunkZ++) {
level.getChunk(chunkX, chunkZ);
}
}
QueenEntity queen = QueenEntity.spawnFromUndersideTrail(level, marker.spawnPos, marker.homePos, marker.siteId, marker.yaw);
if (queen != null) {
marker.consumed = true;
@@ -37,7 +37,7 @@ public final class UnderVaultBlockEntity extends BlockEntity {
private static VaultConfig createConfig() {
return new VaultConfig(ResourceKey.create(net.minecraft.core.registries.Registries.LOOT_TABLE,
ResourceLocation.withDefaultNamespace("chests/trial_chambers/reward")), 4.0D, 4.0D,
ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "chests/undertrial_vault")), 4.0D, 4.0D,
new ItemStack(BuiltInRegistries.ITEM.get(ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "undertrial_key"))),
Optional.empty());
}
@@ -5,23 +5,33 @@ import com.craisinlord.antarchy.content.block.UndertrialSpawnerBlock;
import com.craisinlord.antarchy.Antarchy;
import net.minecraft.core.BlockPos;
import net.minecraft.core.particles.SimpleParticleType;
import net.minecraft.core.Direction;
import net.minecraft.core.registries.Registries;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtOps;
import net.minecraft.nbt.Tag;
import net.minecraft.resources.RegistryOps;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.core.registries.Registries;
import net.minecraft.world.effect.MobEffect;
import net.minecraft.world.effect.MobEffectInstance;
import net.minecraft.world.entity.Mob;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.trialspawner.TrialSpawner;
import net.minecraft.world.level.block.entity.trialspawner.TrialSpawnerConfig;
import net.minecraft.world.level.block.entity.trialspawner.TrialSpawnerState;
import net.minecraft.world.level.storage.loot.LootTable;
import net.minecraft.world.level.storage.loot.LootParams;
import net.minecraft.world.level.storage.loot.parameters.LootContextParamSets;
import net.minecraft.core.dispenser.DefaultDispenseItemBehavior;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
import net.minecraft.util.random.SimpleWeightedRandomList;
import java.util.HashSet;
import java.util.Set;
@@ -35,11 +45,16 @@ public final class UndertrialSpawnerBlockEntity extends BlockEntity implements T
private TrialSpawnerState previousClientState;
private double clientSpin;
private double clientOldSpin;
private SimpleWeightedRandomList<ResourceKey<LootTable>> normalLootTables = SimpleWeightedRandomList.empty();
private SimpleWeightedRandomList<ResourceKey<LootTable>> ominousLootTables = SimpleWeightedRandomList.empty();
private Tag normalLootTablesTag;
private Tag ominousLootTablesTag;
public UndertrialSpawnerBlockEntity(BlockPos pos, BlockState state, Supplier<? extends net.minecraft.world.level.block.entity.BlockEntityType<UndertrialSpawnerBlockEntity>> type) {
super(type.get(), pos, state);
this.trialSpawner = new TrialSpawner(this, net.minecraft.world.level.block.entity.trialspawner.PlayerDetector.INCLUDING_CREATIVE_PLAYERS,
net.minecraft.world.level.block.entity.trialspawner.PlayerDetector.EntitySelector.SELECT_FROM_LEVEL);
this.trialSpawner.overridePeacefulAndMobSpawnRule();
this.previousClientState = state.getValue(UndertrialSpawnerBlock.STATE);
}
@@ -48,7 +63,14 @@ public final class UndertrialSpawnerBlockEntity extends BlockEntity implements T
for (Mob mob : level.getEntitiesOfClass(Mob.class, new AABB(pos).inflate(16.0D))) {
before.add(mob.getUUID());
}
blockEntity.trialSpawner.tickServer(level, pos, state.getValue(UndertrialSpawnerBlock.OMINOUS));
TrialSpawnerState previousState = blockEntity.getState();
boolean ominous = state.getValue(UndertrialSpawnerBlock.OMINOUS);
boolean rewardReady = previousState == TrialSpawnerState.EJECTING_REWARD
&& blockEntity.trialSpawner.getData().isReadyToEjectItems(level, 30.0F, blockEntity.trialSpawner.getTargetCooldownLength());
blockEntity.trialSpawner.tickServer(level, pos, ominous);
if (rewardReady && blockEntity.getState() == TrialSpawnerState.EJECTING_REWARD) {
blockEntity.ejectReward(level, pos, ominous);
}
for (Mob mob : level.getEntitiesOfClass(Mob.class, new AABB(pos).inflate(16.0D))) {
if (!before.contains(mob.getUUID())) {
blockEntity.applyUndertrialEffects(mob);
@@ -102,18 +124,35 @@ public final class UndertrialSpawnerBlockEntity extends BlockEntity implements T
@Override
protected void loadAdditional(CompoundTag tag, net.minecraft.core.HolderLookup.Provider registries) {
super.loadAdditional(tag, registries);
if (tag.contains(SPAWNER_TAG)) {
trialSpawner.codec().parse(RegistryOps.create(NbtOps.INSTANCE, registries), tag.get(SPAWNER_TAG)).result().ifPresent(decoded -> {
trialSpawner = decoded;
});
}
net.minecraft.nbt.Tag spawnerTag = tag.contains(SPAWNER_TAG) ? tag.get(SPAWNER_TAG) : tag;
trialSpawner.codec().parse(RegistryOps.create(NbtOps.INSTANCE, registries), spawnerTag).result().ifPresent(decoded -> {
captureLootTables(decoded, spawnerTag);
trialSpawner = createRewardlessSpawner(decoded);
});
}
@Override
public CompoundTag getUpdateTag(net.minecraft.core.HolderLookup.Provider registries) {
CompoundTag tag = new CompoundTag();
saveAdditional(tag, registries);
return tag;
}
@Override
protected void saveAdditional(CompoundTag tag, net.minecraft.core.HolderLookup.Provider registries) {
super.saveAdditional(tag, registries);
trialSpawner.codec().encodeStart(RegistryOps.create(NbtOps.INSTANCE, registries), trialSpawner).result()
.ifPresent(encoded -> tag.put(SPAWNER_TAG, encoded));
.ifPresent(encoded -> {
if (encoded instanceof CompoundTag spawnerTag) {
if (normalLootTablesTag != null) {
spawnerTag.getCompound("normal_config").put("loot_tables_to_eject", normalLootTablesTag.copy());
}
if (ominousLootTablesTag != null) {
spawnerTag.getCompound("ominous_config").put("loot_tables_to_eject", ominousLootTablesTag.copy());
}
tag.put(SPAWNER_TAG, spawnerTag);
}
});
}
@Override
@@ -141,4 +180,42 @@ public final class UndertrialSpawnerBlockEntity extends BlockEntity implements T
public double getClientSpin(float partialTick) {
return this.clientOldSpin + (this.clientSpin - this.clientOldSpin) * partialTick;
}
private void captureLootTables(TrialSpawner source, Tag spawnerTag) {
normalLootTables = source.getNormalConfig().lootTablesToEject();
ominousLootTables = source.getOminousConfig().lootTablesToEject();
if (spawnerTag instanceof CompoundTag compound) {
CompoundTag normalConfig = compound.getCompound("normal_config");
CompoundTag ominousConfig = compound.getCompound("ominous_config");
normalLootTablesTag = normalConfig.get("loot_tables_to_eject");
ominousLootTablesTag = ominousConfig.get("loot_tables_to_eject");
}
}
private TrialSpawner createRewardlessSpawner(TrialSpawner source) {
TrialSpawner rewardless = new TrialSpawner(withoutLootTables(source.getNormalConfig()), withoutLootTables(source.getOminousConfig()),
source.getData(), source.getRequiredPlayerRange(), source.getTargetCooldownLength(), this,
source.getPlayerDetector(), source.getEntitySelector());
rewardless.overridePeacefulAndMobSpawnRule();
return rewardless;
}
private static TrialSpawnerConfig withoutLootTables(TrialSpawnerConfig config) {
return new TrialSpawnerConfig(config.spawnRange(), config.totalMobs(), config.simultaneousMobs(),
config.totalMobsAddedPerPlayer(), config.simultaneousMobsAddedPerPlayer(), config.ticksBetweenSpawn(),
config.spawnPotentialsDefinition(), SimpleWeightedRandomList.empty(), config.itemsToDropWhenOminous());
}
private void ejectReward(ServerLevel level, BlockPos pos, boolean ominous) {
SimpleWeightedRandomList<ResourceKey<LootTable>> lootTables = ominous ? ominousLootTables : normalLootTables;
lootTables.getRandomValue(level.getRandom()).ifPresent(key -> {
LootTable lootTable = level.getServer().reloadableRegistries().getLootTable(key);
LootParams params = new LootParams.Builder(level).create(LootContextParamSets.EMPTY);
for (ItemStack stack : lootTable.getRandomItems(params)) {
DefaultDispenseItemBehavior.spawnItem(level, stack, 2, Direction.DOWN,
Vec3.atBottomCenterOf(pos).relative(Direction.DOWN, 1.2D));
}
level.levelEvent(3014, pos, 0);
});
}
}
@@ -1,46 +0,0 @@
package com.craisinlord.antarchy.content.client;
import com.craisinlord.antarchy.content.weather.ThoraxisWeatherKind;
import java.util.HashMap;
import java.util.Map;
import net.minecraft.core.BlockPos;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.Level;
public final class ThoraxisWeatherClientState {
private static final Map<ResourceLocation, ThoraxisWeatherState> STATES = new HashMap<>();
private ThoraxisWeatherClientState() {
}
public static void apply(ResourceLocation dimension, ThoraxisWeatherKind kind, long expiresAt, int anchorX, int anchorY, int anchorZ) {
if (kind == ThoraxisWeatherKind.NONE) {
STATES.remove(dimension);
return;
}
STATES.put(dimension, new ThoraxisWeatherState(kind, expiresAt, new BlockPos(anchorX, anchorY, anchorZ)));
}
public static ThoraxisWeatherState current(Level level) {
if (level == null) {
return null;
}
ThoraxisWeatherState state = STATES.get(level.dimension().location());
if (state == null) {
return null;
}
long now = level.getGameTime();
if (state.expiresAt > 0L && now > state.expiresAt) {
STATES.remove(level.dimension().location());
return null;
}
return state;
}
public record ThoraxisWeatherState(ThoraxisWeatherKind kind, long expiresAt, BlockPos anchor) {
}
}
@@ -50,7 +50,9 @@ public class RoyalBossModel extends GeoModel<RoyalBossEntity> {
Vec3 beamEnd = animatable.getRoyalBeamEndPosition(slot);
if (!animatable.isFiringRoyalBeam(slot) || beamEnd == null) continue;
Vec3 origin = animatable.getRoyalBeamShootFrom(slot, partialTick);
Vec3 direction = beamEnd.subtract(origin);
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;
@@ -65,8 +67,8 @@ public class RoyalBossModel extends GeoModel<RoyalBossEntity> {
float pitchPerBone = pitch / chain.length;
for (String boneName : chain) {
this.getBone(boneName).ifPresent(bone -> {
bone.setRotY(bone.getRotY() + yawPerBone);
bone.setRotX(bone.getRotX() + pitchPerBone);
bone.setRotY(yawPerBone);
bone.setRotX(pitchPerBone);
});
}
}
@@ -13,6 +13,8 @@ import net.minecraft.commands.Commands;
import net.minecraft.core.BlockPos;
import net.minecraft.core.registries.Registries;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.ClickEvent;
import net.minecraft.network.chat.Style;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerLevel;
@@ -67,11 +69,16 @@ public final class QueenLocateCommand {
}
BlockPos spawn = marker.getSpawnPos();
int distance = Mth.floor(Math.sqrt(distanceSquared(originX, originZ, site)));
source.sendSuccess(() -> Component.literal(
"Nearest Queen spawn in antarchy:thoraxis: ["
+ spawn.getX() + ", " + spawn.getY() + ", " + spawn.getZ()
+ "] (" + distance + " blocks away)"
), false);
String coordinates = "[" + spawn.getX() + ", " + spawn.getY() + ", " + spawn.getZ() + "]";
String teleportCommand = "/execute in " + THORAXIS.location() + " run tp @s "
+ spawn.getX() + " " + spawn.getY() + " " + spawn.getZ();
source.sendSuccess(() -> Component.literal("Nearest Queen spawn in " + THORAXIS.location() + ": ")
.append(Component.literal(coordinates)
.withStyle(Style.EMPTY
.withColor(net.minecraft.ChatFormatting.AQUA)
.withUnderlined(true)
.withClickEvent(new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, teleportCommand))))
.append(Component.literal(" (" + distance + " blocks away)")), false);
return distance;
}
@@ -117,7 +117,8 @@ public class BedBugEntity extends Animal implements GeoEntity {
}
public static boolean canSpawn(EntityType<BedBugEntity> entityType, ServerLevelAccessor level, MobSpawnType spawnReason, BlockPos pos, RandomSource random) {
if (spawnReason == MobSpawnType.SPAWN_EGG || spawnReason == MobSpawnType.SPAWNER || spawnReason == MobSpawnType.COMMAND) {
if (spawnReason == MobSpawnType.SPAWN_EGG || spawnReason == MobSpawnType.SPAWNER
|| spawnReason == MobSpawnType.TRIAL_SPAWNER || spawnReason == MobSpawnType.COMMAND) {
return true;
}
@@ -100,8 +100,8 @@ public class ManticoreEntity extends Monster implements GeoEntity {
public static AttributeSupplier.Builder createAttributes() {
return Mob.createMobAttributes()
.add(Attributes.MAX_HEALTH, AntarchySettings.manticoreHealth())
.add(Attributes.MOVEMENT_SPEED, 0.3D)
.add(Attributes.FLYING_SPEED, 1.2D)
.add(Attributes.MOVEMENT_SPEED, 0.45D)
.add(Attributes.FLYING_SPEED, 2.0D)
.add(Attributes.ATTACK_DAMAGE, AntarchySettings.manticoreAttackDamage())
.add(Attributes.FOLLOW_RANGE, 28.0D)
.add(Attributes.KNOCKBACK_RESISTANCE, 0.2D);
@@ -109,7 +109,8 @@ public class ManticoreEntity extends Monster implements GeoEntity {
public static boolean canSpawn(EntityType<ManticoreEntity> type, ServerLevelAccessor level, MobSpawnType spawnReason, BlockPos pos, RandomSource random) {
if (spawnReason == MobSpawnType.SPAWN_EGG || spawnReason == MobSpawnType.SPAWNER
|| spawnReason == MobSpawnType.COMMAND || spawnReason == MobSpawnType.MOB_SUMMONED) {
|| spawnReason == MobSpawnType.TRIAL_SPAWNER || spawnReason == MobSpawnType.COMMAND
|| spawnReason == MobSpawnType.MOB_SUMMONED) {
return true;
}
if (pos.getY() >= 0) {
@@ -486,7 +487,7 @@ public class ManticoreEntity extends Monster implements GeoEntity {
private final ManticoreEntity manticore;
ManticoreAttackGoal(ManticoreEntity manticore) {
super(manticore, 1.1D, true);
super(manticore, 1.6D, true);
this.manticore = manticore;
}
@@ -107,6 +107,7 @@ public class ScorpionEntity extends Monster implements GeoEntity {
MobSpawnType spawnReason, BlockPos pos, RandomSource random) {
if (spawnReason == MobSpawnType.SPAWN_EGG
|| spawnReason == MobSpawnType.SPAWNER
|| spawnReason == MobSpawnType.TRIAL_SPAWNER
|| spawnReason == MobSpawnType.COMMAND) {
return true;
}
@@ -114,6 +114,9 @@ public class LucidEntity extends Monster implements GeoEntity {
public static boolean canSpawn(EntityType<LucidEntity> entityType, ServerLevelAccessor level,
MobSpawnType spawnReason, BlockPos pos, RandomSource random) {
if (spawnReason == MobSpawnType.TRIAL_SPAWNER) {
return level.getDifficulty() != Difficulty.PEACEFUL;
}
return level.getDifficulty() != Difficulty.PEACEFUL
&& pos.getY() <= 250
&& level.isEmptyBlock(pos)
@@ -571,4 +574,3 @@ public class LucidEntity extends Monster implements GeoEntity {
@@ -35,9 +35,7 @@ public class LucidEyeProjectileEntity extends ThrowableItemProjectile {
@Override
protected double getDefaultGravity() {
// Gravity is expressed in the projectile's local frame, so the generic
// Underside hook mirrors this pearl's special rising arc below Y=0.
return -0.03D;
return 0.03D;
}
@Override
@@ -260,6 +260,9 @@ public class NightmareEntity extends Monster implements GeoEntity {
}
public static boolean canSpawn(EntityType<NightmareEntity> entityType, ServerLevelAccessor level, MobSpawnType spawnReason, BlockPos pos, RandomSource random) {
if (spawnReason == MobSpawnType.TRIAL_SPAWNER) {
return level.getDifficulty() != Difficulty.PEACEFUL;
}
boolean posEmpty = level.isEmptyBlock(pos);
boolean floorSpawn = level.getBlockState(pos.below()).isFaceSturdy(level, pos.below(), Direction.UP) && level.isEmptyBlock(pos.above());
boolean ceilingSpawn = level.getBlockState(pos.above()).isFaceSturdy(level, pos.above(), Direction.DOWN) && level.isEmptyBlock(pos.below());
@@ -275,7 +275,7 @@ public class DimensionalTearEntity extends Entity implements GeoEntity {
if (target != null && target.isAlive()) {
manticore.setTarget(target);
}
manticore.setDeltaMovement(this.getViewVector(1.0F).scale(0.2D).add(0.0D, 0.08D, 0.0D));
manticore.setDeltaMovement(this.getViewVector(1.0F).scale(0.7D).add(0.0D, 0.18D, 0.0D));
manticore.setNoGravity(false);
level.addFreshEntity(manticore);
}
@@ -868,8 +868,6 @@ public class KingEntity extends RoyalBossEntity {
new com.craisinlord.antarchy.content.entity.royal.attack.RoyalAttackScheduler.Action() {
@Override public void onStart() {
KingEntity.this.triggerAnim(RoyalHead.Slot.LEFT.controllerName(), "shoot");
KingEntity.this.playRoyalSound(SoundEvents.FIRECHARGE_USE, 0.8F);
KingEntity.this.playRoyalSound(SoundEvents.GHAST_WARN, 1.2F);
}
@Override public void onActive(int elapsedTicks) {
Vec3 origin = KingEntity.this.headAnchor(KingEntity.this.royalHead(RoyalHead.Slot.LEFT));
@@ -59,6 +59,7 @@ public class QueenEntity extends RoyalBossEntity {
private static final int MOMENTUM_LOCK_COOLDOWN = 420;
private static final int MOMENTUM_LOCK_DURATION = 90;
private static final double MOMENTUM_LOCK_RADIUS = 26.0D;
private static final float MOMENTUM_LOCK_EARLY_HEALTH_THRESHOLD = 0.80F;
private static final int CRUSHING_GRAVITY_COOLDOWN = 360;
private static final int CRUSHING_GRAVITY_DURATION = 120;
private static final double CRUSHING_GRAVITY_RADIUS = 18.0D;
@@ -252,8 +253,6 @@ public class QueenEntity extends RoyalBossEntity {
@Override public void onStart() {
head.startShoot();
QueenEntity.this.triggerAnim(slot.controllerName(), "shoot");
QueenEntity.this.playRoyalSound(net.minecraft.sounds.SoundEvents.FIRECHARGE_USE, 0.75F);
QueenEntity.this.playRoyalSound(net.minecraft.sounds.SoundEvents.GHAST_WARN, 1.35F);
}
@Override public void onActive(int elapsedTicks) {
Vec3 origin = QueenEntity.this.headAnchor(head);
@@ -381,11 +380,21 @@ public class QueenEntity extends RoyalBossEntity {
private int spawnManticoreTears(ServerLevel level, LivingEntity target, int count) {
int pairs = Math.max(1, Math.min(3, (count + 1) / 2));
int created = 0;
Vec3 towardTarget = target.position().subtract(this.position()).multiply(1.0D, 0.0D, 1.0D);
if (towardTarget.lengthSqr() < 1.0E-4D) {
towardTarget = this.getViewVector(1.0F).multiply(1.0D, 0.0D, 1.0D);
}
towardTarget = towardTarget.normalize();
Vec3 behindTarget = target.position().add(towardTarget.scale(8.0D));
Vec3 lateral = new Vec3(-towardTarget.z, 0.0D, towardTarget.x);
double vertical = AntarchyGravityApi.isGravityInverted(this) ? -2.0D : 2.0D;
for (int i = 0; i < pairs; i++) {
double angle = this.random.nextDouble() * Mth.TWO_PI;
double distance = Math.max(8.0D, AntarchySettings.queenManticoreSummonRange() * 0.65D);
Vec3 firstPos = this.position().add(Math.cos(angle) * distance, 1.0D, Math.sin(angle) * distance);
Vec3 secondPos = this.position().add(Math.cos(angle + Math.PI) * distance, 1.0D, Math.sin(angle + Math.PI) * distance);
double spread = (i - (pairs - 1) * 0.5D) * 5.0D;
Vec3 pairCenter = behindTarget.add(lateral.scale(spread)).add(0.0D, vertical, 0.0D);
Vec3 firstPos = pairCenter.add(lateral.scale(3.0D));
Vec3 secondPos = pairCenter.add(lateral.scale(-3.0D));
DimensionalTearEntity first = DimensionalTearEntity.createQueenManticoreTear(level, firstPos,
(float) Math.toDegrees(angle), 240, this.getUUID(), Math.min(3, count));
DimensionalTearEntity second = DimensionalTearEntity.createQueenManticoreTear(level, secondPos,
@@ -417,7 +426,7 @@ public class QueenEntity extends RoyalBossEntity {
Phase phase = this.phase();
boolean majorBusy = this.royalEffects.active("momentum_lock") || this.royalEffects.active("crushing_gravity");
boolean allowOverlap = phase != Phase.ONE;
String[] candidates = phase == Phase.ONE
String[] candidates = phase == Phase.ONE && !this.isMomentumLockAvailableEarly()
? new String[] {"gravity_stomp", "black_hole"}
: new String[] {"gravity_stomp", "black_hole", "momentum_lock", "crushing_gravity", "acceleration"};
String selected = this.attackScheduler.chooseWeighted(candidates, id -> {
@@ -490,6 +499,11 @@ public class QueenEntity extends RoyalBossEntity {
};
}
private boolean isMomentumLockAvailableEarly() {
return this.getMaxHealth() > 0.0F
&& this.getHealth() / this.getMaxHealth() <= MOMENTUM_LOCK_EARLY_HEALTH_THRESHOLD;
}
private void tickFinalAcceleration() {
if (this.phase() != Phase.THREE) {
return;
@@ -517,7 +531,7 @@ public class QueenEntity extends RoyalBossEntity {
private void castBlackHole(ServerLevel level, LivingEntity target) {
double vertical = 2.0D + this.random.nextDouble() * 2.0D;
if (AntarchyGravityApi.isGravityInverted(target)) {
if (AntarchyGravityApi.isGravityInverted(this)) {
vertical = -vertical;
}
Vec3 anchor = target.position().add(
@@ -301,7 +301,7 @@ public class RoyalBlackHoleEntity extends Entity implements GeoEntity {
}
Vec3 center = this.effectCenter();
Vec3 terrainOrigin = this.position();
Vec3 terrainOrigin = center;
double radius = this.effectRadius();
int pulledThisTick = 0;
for (int attempt = 0; attempt < BLOCK_SUCTION_ATTEMPTS && pulledThisTick < BLOCKS_PER_SUCTION
@@ -11,6 +11,7 @@ import com.craisinlord.antarchy.content.entity.royal.beam.RoyalBeamElement;
import com.craisinlord.antarchy.content.entity.royal.attack.RoyalAttackLane;
import com.craisinlord.antarchy.content.entity.royal.attack.RoyalAttackScheduler;
import com.craisinlord.antarchy.content.gravity.AntarchyGravityApi;
import com.craisinlord.antarchy.content.gravity.AntarchyGravityRotationUtil;
import com.craisinlord.antarchy.content.worldgen.thoraxis.ThoraxisUndersideManager;
import java.util.Arrays;
import java.util.List;
@@ -26,6 +27,7 @@ import net.minecraft.sounds.SoundEvent;
import net.minecraft.util.Mth;
import net.minecraft.world.BossEvent;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.damagesource.DamageTypes;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.LivingEntity;
@@ -586,6 +588,7 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
int index = head.slot().ordinal();
head.setBeamActive(false);
head.stopShoot();
this.stopTriggeredAnim(head.slot().controllerName(), null);
this.beamControllers[index].stop();
this.entityData.set(BEAM_ACTIVE[index], false);
this.entityData.set(BEAM_ELEMENT[index], RoyalBeamElement.GENERIC.ordinal());
@@ -874,10 +877,12 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
double yaw = this.yBodyRot * Mth.DEG_TO_RAD;
Vec3 forward = new Vec3(-Math.sin(yaw), 0.0D, Math.cos(yaw));
Vec3 right = new Vec3(forward.z, 0.0D, -forward.x);
return new Vec3(
this.getX() + forward.x * spec.forwardOffset() + right.x * spec.lateralOffset(),
this.getY() + spec.yOffset(),
this.getZ() + forward.z * spec.forwardOffset() + right.z * spec.lateralOffset());
Vec3 localOffset = new Vec3(
forward.x * spec.forwardOffset() + right.x * spec.lateralOffset(),
spec.yOffset(),
forward.z * spec.forwardOffset() + right.z * spec.lateralOffset());
return this.position().add(AntarchyGravityRotationUtil.vecPlayerToWorld(localOffset,
AntarchyGravityApi.getGravityDirection(this)));
}
protected Vec3 beamAnchor(RoyalHead head) {
@@ -894,10 +899,12 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
case RIGHT -> BEAM_MUZZLE_LATERAL;
};
double vertical = slot == RoyalHead.Slot.CENTER ? BEAM_MUZZLE_CENTER_Y : BEAM_MUZZLE_SIDE_Y;
return new Vec3(
x + forward.x * BEAM_MUZZLE_FORWARD + right.x * lateral,
y + vertical,
z + forward.z * BEAM_MUZZLE_FORWARD + right.z * lateral);
Vec3 localOffset = new Vec3(
forward.x * BEAM_MUZZLE_FORWARD + right.x * lateral,
vertical,
forward.z * BEAM_MUZZLE_FORWARD + right.z * lateral);
return new Vec3(x, y, z).add(AntarchyGravityRotationUtil.vecPlayerToWorld(localOffset,
AntarchyGravityApi.getGravityDirection(this)));
}
protected double royalBeamMinimumRange() {
@@ -989,6 +996,9 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
@Override
public boolean hurt(DamageSource source, float amount) {
if (source.is(DamageTypes.GENERIC_KILL)) {
return super.hurt(source, amount);
}
float cappedAmount = (float) Math.min(amount, AntarchySettings.royalBossMaxSingleHitDamage());
return super.hurt(source, cappedAmount);
}
@@ -1,46 +0,0 @@
package com.craisinlord.antarchy.content.network;
import com.craisinlord.antarchy.Antarchy;
import com.craisinlord.antarchy.content.weather.ThoraxisWeatherKind;
import io.netty.buffer.ByteBuf;
import net.minecraft.network.codec.ByteBufCodecs;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
import net.minecraft.resources.ResourceLocation;
public record ThoraxisWeatherPayload(
String dimensionId,
String weatherId,
long expiresAt,
int anchorX,
int anchorY,
int anchorZ
) implements CustomPacketPayload {
public static final Type<ThoraxisWeatherPayload> TYPE =
new Type<>(ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "thoraxis_weather"));
public static final StreamCodec<ByteBuf, ThoraxisWeatherPayload> STREAM_CODEC = StreamCodec.composite(
ByteBufCodecs.STRING_UTF8,
ThoraxisWeatherPayload::dimensionId,
ByteBufCodecs.STRING_UTF8,
ThoraxisWeatherPayload::weatherId,
ByteBufCodecs.VAR_LONG,
ThoraxisWeatherPayload::expiresAt,
ByteBufCodecs.VAR_INT,
ThoraxisWeatherPayload::anchorX,
ByteBufCodecs.VAR_INT,
ThoraxisWeatherPayload::anchorY,
ByteBufCodecs.VAR_INT,
ThoraxisWeatherPayload::anchorZ,
ThoraxisWeatherPayload::new
);
public ThoraxisWeatherPayload(ResourceLocation dimensionId, ThoraxisWeatherKind kind, long expiresAt, int anchorX, int anchorY, int anchorZ) {
this(dimensionId.toString(), kind.id(), expiresAt, anchorX, anchorY, anchorZ);
}
@Override
public Type<ThoraxisWeatherPayload> type() {
return TYPE;
}
}
@@ -153,6 +153,10 @@ public class TimeDilationFieldEntity extends Entity {
return this.entityData.get(RADIUS);
}
public double influenceRadius() {
return this.chronosphere ? this.fieldRadius() * 2.0D : this.fieldRadius();
}
public double fieldRadiusSqr() {
double radius = this.fieldRadius();
return radius * radius;
@@ -40,8 +40,23 @@ public final class TimeDilationFieldSampler {
double dy = center.y - entity.getY();
double dz = center.z - entity.getZ();
double distanceSqr = dx * dx + dy * dy + dz * dz;
if (distanceSqr < field.fieldRadiusSqr()) {
double falloff = TimeDilationMath.gaussianFalloff(Math.sqrt(distanceSqr), field.fieldRadius());
double distance = Math.sqrt(distanceSqr);
double radius = field.fieldRadius();
double falloff;
if (field.isChronosphere()) {
if (distance >= radius * 2.0D) {
continue;
}
falloff = distance <= radius
? 1.0D
: TimeDilationMath.gaussianFalloff(distance - radius, radius);
} else {
if (distanceSqr >= field.fieldRadiusSqr()) {
continue;
}
falloff = TimeDilationMath.gaussianFalloff(distance, radius);
}
if (falloff > 0.0D) {
combinedRate *= TimeDilationMath.localFieldRate(field.effectiveFieldRate(), falloff);
}
}
@@ -83,7 +83,7 @@ public final class TimeDilationManager {
private static void discoverFieldEntities(ServerLevel level, List<TimeDilationFieldEntity> fields, TrackingState tracking) {
for (TimeDilationFieldEntity field : fields) {
double radius = field.fieldRadius();
double radius = field.influenceRadius();
var center = field.position();
var area = new net.minecraft.world.phys.AABB(
center.x - radius, center.y - radius, center.z - radius,
@@ -1,43 +0,0 @@
package com.craisinlord.antarchy.content.weather;
import java.util.Optional;
public enum ThoraxisWeatherKind {
NONE("none"),
INVERSION_STORM("inversion_storm"),
BLOOD_RAIN("blood_rain"),
SANDSTORM("sandstorm");
private final String id;
ThoraxisWeatherKind(String id) {
this.id = id;
}
public String id() {
return this.id;
}
public boolean isActive() {
return this != NONE;
}
public static ThoraxisWeatherKind byId(String value) {
if (value == null) {
return NONE;
}
for (ThoraxisWeatherKind kind : values()) {
if (kind.id.equalsIgnoreCase(value)) {
return kind;
}
}
return NONE;
}
public static Optional<ThoraxisWeatherKind> parse(String value) {
ThoraxisWeatherKind kind = byId(value);
return kind == NONE ? Optional.empty() : Optional.of(kind);
}
}
@@ -1,304 +0,0 @@
package com.craisinlord.antarchy.content.weather;
import com.craisinlord.antarchy.Antarchy;
import com.craisinlord.antarchy.content.AntarchyObjects;
import com.craisinlord.antarchy.content.gravity.AntarchyGravityApi;
import com.craisinlord.antarchy.content.gravity.AntarchyGravityDirection;
import com.craisinlord.antarchy.content.gravity.AntarchyGravityTransition;
import net.minecraft.core.BlockPos;
import net.minecraft.core.particles.BlockParticleOption;
import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.effect.MobEffectInstance;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.util.RandomSource;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.LightningBolt;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.block.Blocks;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.UUID;
public final class ThoraxisWeatherManager {
private static final ResourceLocation THORAXIS_DIMENSION = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "thoraxis");
private static final ResourceKey<Biome> THORAXIS_WASTES = ResourceKey.create(
net.minecraft.core.registries.Registries.BIOME,
ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "nightmare_wastes")
);
private static final ResourceKey<Biome> DREAM_DUNES = ResourceKey.create(
net.minecraft.core.registries.Registries.BIOME,
ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "dream_dunes")
);
private static final ResourceKey<Biome> LUCID_POOLS = ResourceKey.create(
net.minecraft.core.registries.Registries.BIOME,
ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "lucid_pools")
);
private static final int WEATHER_RECHECK_MIN_TICKS = 400;
private static final int WEATHER_RECHECK_MAX_TICKS = 900;
private static final int STORM_MIN_TICKS = 600;
private static final int STORM_MAX_TICKS = 1800;
private static final int INVERSION_STRIKE_INTERVAL = 600;
private static final int INVERSION_GRAVITY_TICKS = 220;
private static final int INVERSION_EFFECT_TICKS = 200;
private static final int WEATHER_PARTICLE_INTERVAL = 4;
private static final double WEATHER_RADIUS = 48.0D;
private static final WeakHashMap<ServerLevel, WeatherState> STATES = new WeakHashMap<>();
private ThoraxisWeatherManager() {
}
public static ThoraxisWeatherSnapshot tick(ServerLevel level) {
if (!isThoraxis(level)) {
return null;
}
WeatherState state = STATES.computeIfAbsent(level, ignored -> new WeatherState());
long now = level.getGameTime();
RandomSource random = level.getRandom();
if (state.kind.isActive()) {
runActiveWeather(level, state, now);
if (now >= state.expiresAt) {
state.clear(now + randomCooldown(random));
return snapshot(level, state);
}
return null;
}
if (now < state.nextDecisionAt) {
return null;
}
WeatherKindSelection selection = chooseWeather(level, random);
if (selection.kind == ThoraxisWeatherKind.NONE) {
state.nextDecisionAt = now + randomCooldown(random);
return null;
}
state.start(selection.kind, selection.anchor, now, now + randomDuration(random));
runActiveWeather(level, state, now);
return snapshot(level, state);
}
public static ThoraxisWeatherSnapshot snapshot(ServerLevel level) {
WeatherState state = STATES.get(level);
if (state == null) {
return new ThoraxisWeatherSnapshot(level.dimension().location(), ThoraxisWeatherKind.NONE, 0L, BlockPos.ZERO);
}
return snapshot(level, state);
}
public static ThoraxisWeatherSnapshot force(ServerLevel level, ThoraxisWeatherKind kind, BlockPos anchor, long durationTicks) {
if (!isThoraxis(level)) {
return new ThoraxisWeatherSnapshot(level.dimension().location(), ThoraxisWeatherKind.NONE, 0L, BlockPos.ZERO);
}
WeatherState state = STATES.computeIfAbsent(level, ignored -> new WeatherState());
long now = level.getGameTime();
if (kind == null || !kind.isActive()) {
state.clear(now + randomCooldown(level.getRandom()));
return snapshot(level, state);
}
long expiresAt = durationTicks <= 0L ? now + randomDuration(level.getRandom()) : now + durationTicks;
state.start(kind, anchor == null ? BlockPos.ZERO : anchor, now, expiresAt);
runActiveWeather(level, state, now);
return snapshot(level, state);
}
public static ThoraxisWeatherSnapshot clear(ServerLevel level) {
if (!isThoraxis(level)) {
return new ThoraxisWeatherSnapshot(level.dimension().location(), ThoraxisWeatherKind.NONE, 0L, BlockPos.ZERO);
}
WeatherState state = STATES.computeIfAbsent(level, ignored -> new WeatherState());
state.clear(level.getGameTime() + randomCooldown(level.getRandom()));
return snapshot(level, state);
}
private static ThoraxisWeatherSnapshot snapshot(ServerLevel level, WeatherState state) {
return new ThoraxisWeatherSnapshot(level.dimension().location(), state.kind, state.expiresAt, state.anchor);
}
private static void runActiveWeather(ServerLevel level, WeatherState state, long now) {
switch (state.kind) {
case INVERSION_STORM -> tickInversionStorm(level, state, now);
case BLOOD_RAIN -> tickBloodRain(level, state, now);
case SANDSTORM -> tickSandstorm(level, state, now);
default -> {
}
}
}
private static void tickInversionStorm(ServerLevel level, WeatherState state, long now) {
cleanupExpiredForcedInversions(level, state, now);
if (now % INVERSION_STRIKE_INTERVAL != 0L) {
return;
}
List<LivingEntity> targets = level.getEntitiesOfClass(
LivingEntity.class,
new net.minecraft.world.phys.AABB(state.anchor).inflate(WEATHER_RADIUS),
entity -> entity.isAlive() && !entity.isSpectator()
);
if (targets.isEmpty()) {
return;
}
LivingEntity target = targets.get(level.getRandom().nextInt(targets.size()));
LightningBolt bolt = EntityType.LIGHTNING_BOLT.create(level);
if (bolt != null) {
bolt.moveTo(target.getX(), target.getY() + target.getBbHeight() * 0.5D, target.getZ());
bolt.setVisualOnly(true);
level.addFreshEntity(bolt);
}
if (!AntarchyGravityApi.isGravityForced(target)) {
AntarchyGravityApi.setForcedGravityDirection(target, AntarchyGravityDirection.UP, new AntarchyGravityTransition(12));
state.forcedInversionExpiry.put(target.getUUID(), now + INVERSION_GRAVITY_TICKS);
}
target.addEffect(new MobEffectInstance(AntarchyObjects.INVERTED_EFFECT.get(), INVERSION_EFFECT_TICKS, 0, false, true, true));
}
private static void cleanupExpiredForcedInversions(ServerLevel level, WeatherState state, long now) {
Iterator<Map.Entry<UUID, Long>> iterator = state.forcedInversionExpiry.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<UUID, Long> entry = iterator.next();
if (entry.getValue() > now) {
continue;
}
LivingEntity entity = level.getEntity(entry.getKey()) instanceof LivingEntity living ? living : null;
if (entity != null && entity.isAlive() && AntarchyGravityApi.isGravityForced(entity)) {
AntarchyGravityApi.clearForcedGravity(entity);
}
iterator.remove();
}
}
private static void tickBloodRain(ServerLevel level, WeatherState state, long now) {
if (now % WEATHER_PARTICLE_INTERVAL != 0L) {
return;
}
level.sendParticles(
new BlockParticleOption(ParticleTypes.BLOCK, Blocks.RED_SAND.defaultBlockState()),
state.anchor.getX() + 0.5D,
state.anchor.getY() + 28.0D,
state.anchor.getZ() + 0.5D,
26,
18.0D,
12.0D,
18.0D,
0.12D
);
level.playSound(null, state.anchor, SoundEvents.LIGHTNING_BOLT_THUNDER, net.minecraft.sounds.SoundSource.WEATHER, 0.2F, 0.8F);
}
private static void tickSandstorm(ServerLevel level, WeatherState state, long now) {
if (now % WEATHER_PARTICLE_INTERVAL != 0L) {
return;
}
level.sendParticles(
new BlockParticleOption(ParticleTypes.BLOCK, Blocks.SAND.defaultBlockState()),
state.anchor.getX() + 0.5D,
state.anchor.getY() + 12.0D,
state.anchor.getZ() + 0.5D,
40,
14.0D,
6.0D,
14.0D,
0.10D
);
}
private static WeatherKindSelection chooseWeather(ServerLevel level, RandomSource random) {
var players = level.players();
if (players.isEmpty()) {
return WeatherKindSelection.none();
}
LivingEntity anchorEntity = players.get(random.nextInt(players.size()));
BlockPos anchor = anchorEntity.blockPosition();
var biome = level.getBiome(anchor);
if (biome.is(DREAM_DUNES)) {
return new WeatherKindSelection(ThoraxisWeatherKind.SANDSTORM, anchor);
}
if (biome.is(LUCID_POOLS)) {
return random.nextFloat() < 0.35F
? new WeatherKindSelection(ThoraxisWeatherKind.BLOOD_RAIN, anchor)
: WeatherKindSelection.none();
}
if (biome.is(THORAXIS_WASTES)) {
float roll = random.nextFloat();
if (roll < 0.58F) {
return new WeatherKindSelection(ThoraxisWeatherKind.INVERSION_STORM, anchor);
}
if (roll < 0.88F) {
return new WeatherKindSelection(ThoraxisWeatherKind.BLOOD_RAIN, anchor);
}
}
return WeatherKindSelection.none();
}
private static int randomDuration(RandomSource random) {
return STORM_MIN_TICKS + random.nextInt(STORM_MAX_TICKS - STORM_MIN_TICKS + 1);
}
private static int randomCooldown(RandomSource random) {
return WEATHER_RECHECK_MIN_TICKS + random.nextInt(WEATHER_RECHECK_MAX_TICKS - WEATHER_RECHECK_MIN_TICKS + 1);
}
private static boolean isThoraxis(ServerLevel level) {
return level.dimension().location().equals(THORAXIS_DIMENSION);
}
private record WeatherKindSelection(ThoraxisWeatherKind kind, BlockPos anchor) {
private static WeatherKindSelection none() {
return new WeatherKindSelection(ThoraxisWeatherKind.NONE, BlockPos.ZERO);
}
}
private static final class WeatherState {
private ThoraxisWeatherKind kind = ThoraxisWeatherKind.NONE;
private long expiresAt;
private long nextDecisionAt;
private long startedAt;
private BlockPos anchor = BlockPos.ZERO;
private final Map<UUID, Long> forcedInversionExpiry = new java.util.HashMap<>();
private void start(ThoraxisWeatherKind kind, BlockPos anchor, long startedAt, long expiresAt) {
this.kind = kind;
this.anchor = anchor;
this.startedAt = startedAt;
this.expiresAt = expiresAt;
this.nextDecisionAt = expiresAt;
}
private void clear(long nextDecisionAt) {
this.kind = ThoraxisWeatherKind.NONE;
this.anchor = BlockPos.ZERO;
this.expiresAt = 0L;
this.startedAt = 0L;
this.nextDecisionAt = nextDecisionAt;
this.forcedInversionExpiry.clear();
}
}
}
@@ -1,15 +0,0 @@
package com.craisinlord.antarchy.content.weather;
import net.minecraft.core.BlockPos;
import net.minecraft.resources.ResourceLocation;
public record ThoraxisWeatherSnapshot(
ResourceLocation dimension,
ThoraxisWeatherKind kind,
long expiresAt,
BlockPos anchor
) {
public boolean isActive() {
return this.kind != ThoraxisWeatherKind.NONE;
}
}
@@ -6,6 +6,7 @@ import com.craisinlord.antarchy.content.entity.royal.KingEntity;
import com.mojang.serialization.Codec;
import net.minecraft.core.BlockPos;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.util.RandomSource;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.WorldGenLevel;
@@ -23,10 +24,14 @@ import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.WeakHashMap;
/** Places the authored King's Tree as independently generated chunk slices on a fixed world grid. */
public final class KingsTreeGridFeature extends Feature<NoneFeatureConfiguration> {
private static final Map<ServerLevel, Set<Long>> CLAIMED_TREE_KINGS = new WeakHashMap<>();
private static final int TREE_MIN_X = -191;
private static final int TREE_MAX_X = 192;
private static final int TREE_MIN_Z = -227;
@@ -110,31 +115,42 @@ public final class KingsTreeGridFeature extends Feature<NoneFeatureConfiguration
private static void spawnTreeKing(WorldGenLevel level, int centerX, int centerZ, int baseY) {
var serverLevel = level.getLevel();
Vec3 treeCenter = new Vec3(centerX + 0.5D, 0.0D, centerZ + 0.5D);
AABB searchBox = new AABB(
centerX - 512.0D, level.getMinBuildHeight(), centerZ - 512.0D,
centerX + 512.0D, level.getMaxBuildHeight(), centerZ + 512.0D);
List<KingEntity> treeKings = serverLevel.getEntitiesOfClass(KingEntity.class, searchBox,
king -> king.isAlive() && king.isTreePatrolFor(treeCenter));
if (!treeKings.isEmpty()) {
// Repair older worlds that already contain duplicate tree-bound Kings.
for (int index = 1; index < treeKings.size(); index++) {
treeKings.get(index).discard();
long treeKey = ((long) centerX << 32) ^ (centerZ & 0xffffffffL);
synchronized (serverLevel) {
synchronized (CLAIMED_TREE_KINGS) {
if (!CLAIMED_TREE_KINGS.computeIfAbsent(serverLevel, ignored -> new java.util.HashSet<>()).add(treeKey)) {
return;
}
}
return;
Vec3 treeCenter = new Vec3(centerX + 0.5D, 0.0D, centerZ + 0.5D);
AABB searchBox = new AABB(
centerX - 512.0D, level.getMinBuildHeight(), centerZ - 512.0D,
centerX + 512.0D, level.getMaxBuildHeight(), centerZ + 512.0D);
List<KingEntity> treeKings = serverLevel.getEntitiesOfClass(KingEntity.class, searchBox,
king -> king.isAlive() && king.isTreePatrolFor(treeCenter));
if (!treeKings.isEmpty()) {
// Repair older worlds that already contain duplicate tree-bound Kings.
for (int index = 1; index < treeKings.size(); index++) {
treeKings.get(index).discard();
}
return;
}
KingEntity king = AntarchyObjects.KING.get().create(serverLevel);
if (king == null) {
synchronized (CLAIMED_TREE_KINGS) {
CLAIMED_TREE_KINGS.getOrDefault(serverLevel, Set.of()).remove(treeKey);
}
return;
}
double minimumY = baseY + 24.0D;
double maximumY = baseY + 304.0D;
double spawnY = (minimumY + maximumY) * 0.5D;
// Start just outside the central trunk, inside the tree's authored footprint.
king.moveTo(centerX + 48.5D, spawnY, centerZ + 0.5D, 90.0F, 0.0F);
king.setTreePatrolHome(treeCenter, minimumY, maximumY, 0.0D);
king.setPersistenceRequired();
serverLevel.addFreshEntity(king);
}
KingEntity king = AntarchyObjects.KING.get().create(serverLevel);
if (king == null) {
return;
}
double minimumY = baseY + 24.0D;
double maximumY = baseY + 304.0D;
double spawnY = (minimumY + maximumY) * 0.5D;
// Start just outside the central trunk, inside the tree's authored footprint.
king.moveTo(centerX + 48.5D, spawnY, centerZ + 0.5D, 90.0F, 0.0F);
king.setTreePatrolHome(treeCenter, minimumY, maximumY, 0.0D);
king.setPersistenceRequired();
serverLevel.addFreshEntity(king);
}
private static ResourceLocation tileLocation(int tileX, int tileZ) {
@@ -341,21 +341,18 @@ public final class ThoraxisUndersideManager {
}
/**
* Keeps projectile gravity positional: UP below Y=0 in Thoraxis and DOWN
* everywhere else. World velocity is preserved when the gravity frame changes.
* Projectiles perform their own movement in world coordinates, so they must
* not retain an inverted entity-local movement frame. Underside gravity is
* reversed separately when gravitational acceleration is applied.
*/
public static void updateProjectileGravity(Projectile projectile) {
boolean inUnderside = isInUnderside(projectile);
AntarchyGravityDirection desiredDirection = inUnderside
? AntarchyGravityDirection.UP
: AntarchyGravityDirection.DOWN;
boolean stateMismatch = AntarchyGravityApi.getGravityDirection(projectile) != desiredDirection
|| AntarchyGravityApi.isGravityForced(projectile) != inUnderside;
public static void normalizeProjectileGravityFrame(Projectile projectile) {
boolean stateMismatch = AntarchyGravityApi.getGravityDirection(projectile) != AntarchyGravityDirection.DOWN
|| AntarchyGravityApi.isGravityForced(projectile);
if (stateMismatch) {
AntarchyGravityApi.setAirborneGravityDirection(
projectile,
desiredDirection,
inUnderside,
AntarchyGravityDirection.DOWN,
false,
AntarchyGravityTransition.INSTANT
);
}
@@ -0,0 +1,40 @@
package com.craisinlord.antarchy.mixins;
import com.craisinlord.antarchy.content.enchantment.AntarchyEnchantments;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Stream;
import net.minecraft.core.Holder;
import net.minecraft.core.HolderLookup;
import net.minecraft.world.item.enchantment.Enchantment;
import net.minecraft.world.item.CreativeModeTabs;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
@Mixin(CreativeModeTabs.class)
public abstract class CreativeModeTabsEnchantmentOrderMixin {
@Redirect(
method = {"generateEnchantmentBookTypesOnlyMaxLevel", "generateEnchantmentBookTypesAllLevels"},
at = @At(value = "INVOKE", target = "Lnet/minecraft/core/HolderLookup;listElements()Ljava/util/stream/Stream;")
)
private static Stream<Holder.Reference<Enchantment>> antarchy$orderEnchantments(HolderLookup<Enchantment> lookup) {
List<Holder.Reference<Enchantment>> enchantments = lookup.listElements().toList();
List<Holder.Reference<Enchantment>> antarchyEnchantments = enchantments.stream()
.filter(CreativeModeTabsEnchantmentOrderMixin::isAntarchyEnchantment)
.sorted(Comparator.comparingInt(CreativeModeTabsEnchantmentOrderMixin::priority))
.toList();
return Stream.concat(
antarchyEnchantments.stream(),
enchantments.stream().filter(holder -> !isAntarchyEnchantment(holder))
);
}
private static boolean isAntarchyEnchantment(Holder.Reference<Enchantment> holder) {
return holder.is(AntarchyEnchantments.FEATHER_RISING) || holder.is(AntarchyEnchantments.CHRONOSPHERE);
}
private static int priority(Holder.Reference<Enchantment> holder) {
return holder.is(AntarchyEnchantments.FEATHER_RISING) ? 0 : 1;
}
}
@@ -1,47 +0,0 @@
package com.craisinlord.antarchy.mixins.client;
import com.craisinlord.antarchy.content.client.ThoraxisWeatherClientState;
import com.craisinlord.antarchy.content.weather.ThoraxisWeatherKind;
import net.minecraft.client.renderer.entity.LightningBoltRenderer;
import net.minecraft.world.entity.LightningBolt;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.ModifyArgs;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.invoke.arg.Args;
@Mixin(LightningBoltRenderer.class)
public abstract class LightningBoltRendererMixin {
@Unique
private static final ThreadLocal<Boolean> antarchy$redLightning = ThreadLocal.withInitial(() -> false);
@Inject(method = "render", at = @At("HEAD"))
private void antarchy$markRedLightning(LightningBolt bolt, float entityYaw, float partialTick, com.mojang.blaze3d.vertex.PoseStack poseStack, net.minecraft.client.renderer.MultiBufferSource buffer, int packedLight, CallbackInfo ci) {
ThoraxisWeatherClientState.ThoraxisWeatherState weather = ThoraxisWeatherClientState.current(bolt.level());
antarchy$redLightning.set(weather != null
&& weather.kind() == ThoraxisWeatherKind.INVERSION_STORM
&& weather.anchor().closerToCenterThan(bolt.position(), 64.0D));
}
@Inject(method = "render", at = @At("RETURN"))
private void antarchy$clearRedLightning(LightningBolt bolt, float entityYaw, float partialTick, com.mojang.blaze3d.vertex.PoseStack poseStack, net.minecraft.client.renderer.MultiBufferSource buffer, int packedLight, CallbackInfo ci) {
antarchy$redLightning.remove();
}
@ModifyArgs(
method = "quad",
at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/vertex/VertexConsumer;setColor(FFFF)Lcom/mojang/blaze3d/vertex/VertexConsumer;")
)
private static void antarchy$tintLightning(Args args) {
if (!Boolean.TRUE.equals(antarchy$redLightning.get())) {
return;
}
args.set(0, 0.95F);
args.set(1, 0.12F);
args.set(2, 0.12F);
args.set(3, 0.35F);
}
}
@@ -0,0 +1,23 @@
package com.craisinlord.antarchy.mixins.client;
import com.craisinlord.antarchy.content.client.renderer.TemporalContractionAfterimageLayer;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.model.EntityModel;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.entity.EntityRendererProvider;
import net.minecraft.client.renderer.entity.LivingEntityRenderer;
import net.minecraft.world.entity.LivingEntity;
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.CallbackInfo;
@Mixin(LivingEntityRenderer.class)
public abstract class TemporalContractionLivingRendererMixin<T extends LivingEntity, M extends EntityModel<T>> {
@Inject(method = "<init>", at = @At("RETURN"))
private void antarchy$addTemporalContractionLayer(EntityRendererProvider.Context context, M model,
float shadowRadius, CallbackInfo callbackInfo) {
LivingEntityRenderer<T, M> renderer = (LivingEntityRenderer<T, M>) (Object) this;
renderer.addLayer(new TemporalContractionAfterimageLayer<>(renderer));
}
}
@@ -1,19 +1,15 @@
package com.craisinlord.antarchy.mixins.gravity;
import com.craisinlord.antarchy.content.gravity.AntarchyGravityApi;
import com.craisinlord.antarchy.config.AntarchySettings;
import com.craisinlord.antarchy.content.worldgen.thoraxis.ThoraxisUndersideManager;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.projectile.AbstractArrow;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
@Mixin(AbstractArrow.class)
/*
* Flips arrow gravity for inverted shooters.
* Flips arrow gravity from the arrow's current position.
*/
public abstract class AbstractArrowGravityMixin {
@@ -21,22 +17,12 @@ public abstract class AbstractArrowGravityMixin {
value = "INVOKE",
target = "Lnet/minecraft/world/entity/projectile/AbstractArrow;applyGravity()V"))
private void antarchy$applyArrowGravityUpward(AbstractArrow self, Operation<Void> original) {
Entity owner = self.getOwner();
if (!(owner instanceof LivingEntity living)) {
if (!ThoraxisUndersideManager.isInUnderside(self)) {
original.call(self);
return;
}
if (owner instanceof net.minecraft.world.entity.player.Player && !AntarchySettings.invertProjectilesFromInvertedPlayers()) {
original.call(self);
return;
}
if (!ThoraxisUndersideManager.shouldInvertInUnderside(living)) {
original.call(self);
return;
}
// Vanilla applies gravity as -getGravity(). Negate that acceleration to
// mirror the projectile's arc for the inverted Underside gravity.
// Vanilla subtracts gravity from world Y; the Underside adds it instead.
double gravity = self.getGravity();
self.setDeltaMovement(self.getDeltaMovement().add(0.0D, -gravity, 0.0D));
self.setDeltaMovement(self.getDeltaMovement().add(0.0D, gravity, 0.0D));
}
}
@@ -5,14 +5,16 @@ import com.craisinlord.antarchy.content.gravity.AntarchyGravityApi;
import com.craisinlord.antarchy.content.gravity.AntarchyGravityDirection;
import com.craisinlord.antarchy.content.gravity.AntarchyGravityRotationUtil;
import com.craisinlord.antarchy.content.portalgun.PortalGunCollisionHelper;
import java.util.ArrayList;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import java.util.List;
import java.util.TreeSet;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.world.level.material.FluidState;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.MoverType;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.material.FluidState;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
import net.minecraft.world.phys.shapes.VoxelShape;
@@ -21,7 +23,10 @@ import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.ModifyArg;
import org.spongepowered.asm.mixin.injection.ModifyArgs;
import org.spongepowered.asm.mixin.injection.ModifyVariable;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.invoke.arg.Args;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@@ -36,6 +41,8 @@ public abstract class EntityMoveMixin {
private double antarchy$lastPlayerMoveY = 0.0;
@Unique
private AABB antarchy$moveStartBox;
@Unique
private Vec3 antarchy$collideWorldMovement = Vec3.ZERO;
@Inject(method = "move", at = @At("HEAD"))
private void antarchy$captureMoveStart(MoverType type, Vec3 movement, CallbackInfo ci) {
@@ -139,105 +146,119 @@ public abstract class EntityMoveMixin {
}
}
@Inject(method = "collide", at = @At("HEAD"), cancellable = true)
private void antarchy$invertedStepUp(Vec3 movement, CallbackInfoReturnable<Vec3> cir) {
Entity entity = (Entity) (Object) this;
if (!AntarchyGravityApi.isGravityInverted(entity)) {
return;
}
AABB aabb = entity.getBoundingBox();
List<VoxelShape> entityShapes = entity.level().getEntityCollisions(entity, aabb.expandTowards(movement));
Vec3 collided = movement.lengthSqr() == 0.0 ? movement : Entity.collideBoundingBox(entity, movement, aabb, entity.level(), entityShapes);
boolean movedX = movement.x != collided.x;
boolean movedY = movement.y != collided.y;
boolean movedZ = movement.z != collided.z;
boolean pressingIntoCeiling = movedY && movement.y > 0.0D;
boolean supportedByCeiling = entity.onGround()
|| !entity.level().noCollision(entity, aabb.move(0.0D, 1.0E-5D, 0.0D));
float maxUpStep = entity.maxUpStep();
if (!(maxUpStep > 0.0F) || !(pressingIntoCeiling || supportedByCeiling) || !(movedX || movedZ)) {
cir.setReturnValue(PortalGunCollisionHelper.resolveCollision(entity, aabb, movement, collided));
return;
}
AABB steppedBox = pressingIntoCeiling ? aabb.move(0.0, collided.y, 0.0) : aabb;
AABB searchBox = steppedBox.expandTowards(movement.x, -(double) maxUpStep, movement.z);
if (!pressingIntoCeiling) {
searchBox = searchBox.expandTowards(0.0, 1.0E-5D, 0.0);
}
List<VoxelShape> stepShapes = new ArrayList<>(entityShapes);
entity.level().getBlockCollisions(entity, searchBox).forEach(stepShapes::add);
float currentStepY = (float) collided.y;
float[] candidateHeights = antarchy$collectCandidateStepDownHeights(steppedBox, stepShapes, maxUpStep, currentStepY);
for (float candidate : candidateHeights) {
Vec3 stepped = Entity.collideBoundingBox(entity, new Vec3(movement.x, -(double) candidate, movement.z), steppedBox, entity.level(), stepShapes);
if (stepped.horizontalDistanceSqr() > collided.horizontalDistanceSqr()) {
double offset = steppedBox.maxY - aabb.maxY;
cir.setReturnValue(PortalGunCollisionHelper.resolveCollision(entity, aabb, movement, stepped.add(0.0, offset, 0.0)));
return;
}
}
// Exact candidate heights can still fail for composite ceiling shapes
// (notably corner stairs). Mirror the older three-phase step routine:
// move away from the ceiling, cross the obstruction, then settle back
// toward the new ceiling surface.
Vec3 awayFromCeiling = Entity.collideBoundingBox(
entity,
new Vec3(0.0D, -(double) maxUpStep, 0.0D),
steppedBox,
entity.level(),
stepShapes
);
if (awayFromCeiling.y < -1.0E-7D) {
AABB awayBox = steppedBox.move(awayFromCeiling);
Vec3 acrossStep = Entity.collideBoundingBox(
entity,
new Vec3(movement.x, 0.0D, movement.z),
awayBox,
entity.level(),
stepShapes
);
if (acrossStep.horizontalDistanceSqr() > collided.horizontalDistanceSqr()) {
AABB acrossBox = awayBox.move(acrossStep);
Vec3 settleToCeiling = Entity.collideBoundingBox(
entity,
new Vec3(0.0D, -awayFromCeiling.y, 0.0D),
acrossBox,
entity.level(),
stepShapes
);
double initialVerticalOffset = steppedBox.maxY - aabb.maxY;
Vec3 stepped = acrossStep.add(
0.0D,
initialVerticalOffset + awayFromCeiling.y + settleToCeiling.y,
0.0D
);
cir.setReturnValue(PortalGunCollisionHelper.resolveCollision(entity, aabb, movement, stepped));
return;
}
}
cir.setReturnValue(PortalGunCollisionHelper.resolveCollision(entity, aabb, movement, collided));
@Inject(method = "collide", at = @At("HEAD"))
private void antarchy$captureCollideWorldMovement(Vec3 movement, CallbackInfoReturnable<Vec3> cir) {
this.antarchy$collideWorldMovement = movement;
}
@Unique
private static float[] antarchy$collectCandidateStepDownHeights(AABB box, List<VoxelShape> shapes, float maxStep, float currentY) {
/*
* Keep vanilla's step algorithm in player-local coordinates. Its Y tests,
* candidate heights and maxUpStep are all defined in that frame. Only the
* physical AABB operations and low-level shape collision belong in world
* coordinates.
*/
@ModifyVariable(
method = "collide",
at = @At(
value = "INVOKE_ASSIGN",
target = "Lnet/minecraft/world/level/Level;getEntityCollisions(Lnet/minecraft/world/entity/Entity;Lnet/minecraft/world/phys/AABB;)Ljava/util/List;",
ordinal = 0
),
ordinal = 0
)
private Vec3 antarchy$collideWorldToPlayer(Vec3 movement) {
Entity entity = (Entity) (Object) this;
AntarchyGravityDirection direction = AntarchyGravityApi.getGravityDirection(entity);
if (!direction.isInverted()) {
return movement;
}
return AntarchyGravityRotationUtil.vecWorldToPlayer(movement, direction);
}
@ModifyArgs(
method = "collide",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/world/phys/AABB;expandTowards(DDD)Lnet/minecraft/world/phys/AABB;"
)
)
private void antarchy$expandStepSearchInWorldFrame(Args args) {
Entity entity = (Entity) (Object) this;
AntarchyGravityDirection direction = AntarchyGravityApi.getGravityDirection(entity);
if (!direction.isInverted()) {
return;
}
Vec3 world = AntarchyGravityRotationUtil.vecPlayerToWorld(
new Vec3(args.get(0), args.get(1), args.get(2)), direction);
args.set(0, world.x);
args.set(1, world.y);
args.set(2, world.z);
}
@ModifyArgs(
method = "collide",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/world/phys/AABB;move(DDD)Lnet/minecraft/world/phys/AABB;"
)
)
private void antarchy$moveStepBoxInWorldFrame(Args args) {
Entity entity = (Entity) (Object) this;
AntarchyGravityDirection direction = AntarchyGravityApi.getGravityDirection(entity);
if (!direction.isInverted()) {
return;
}
Vec3 world = AntarchyGravityRotationUtil.vecPlayerToWorld(
new Vec3(args.get(0), args.get(1), args.get(2)), direction);
args.set(0, world.x);
args.set(1, world.y);
args.set(2, world.z);
}
@WrapOperation(
method = "collide",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/world/entity/Entity;collideBoundingBox(Lnet/minecraft/world/entity/Entity;Lnet/minecraft/world/phys/Vec3;Lnet/minecraft/world/phys/AABB;Lnet/minecraft/world/level/Level;Ljava/util/List;)Lnet/minecraft/world/phys/Vec3;"
)
)
private Vec3 antarchy$collideBoundingBoxInLocalFrame(
Entity collisionEntity,
Vec3 localMovement,
AABB box,
Level level,
List<VoxelShape> collisions,
Operation<Vec3> original
) {
AntarchyGravityDirection direction = AntarchyGravityApi.getGravityDirection(collisionEntity);
if (!direction.isInverted()) {
return original.call(collisionEntity, localMovement, box, level, collisions);
}
Vec3 worldMovement = AntarchyGravityRotationUtil.vecPlayerToWorld(localMovement, direction);
Vec3 worldResult = original.call(collisionEntity, worldMovement, box, level, collisions);
return AntarchyGravityRotationUtil.vecWorldToPlayer(worldResult, direction);
}
@Redirect(
method = "collide",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/world/entity/Entity;collectCandidateStepUpHeights(Lnet/minecraft/world/phys/AABB;Ljava/util/List;FF)[F"
)
)
private float[] antarchy$collectGravityAwareStepHeights(
AABB box, List<VoxelShape> shapes, float maxStep, float currentY) {
Entity entity = (Entity) (Object) this;
boolean inverted = AntarchyGravityApi.isGravityInverted(entity);
TreeSet<Float> heights = new TreeSet<>();
for (VoxelShape shape : shapes) {
for (double coord : shape.getCoords(Direction.Axis.Y)) {
float height = (float) (box.maxY - coord);
float height = (float) (inverted ? box.maxY - coord : coord - box.minY);
if (height >= 0.0F && height != currentY && height <= maxStep) {
heights.add(height);
}
}
}
float[] result = new float[heights.size()];
int i = 0;
for (float height : heights) {
@@ -246,6 +267,34 @@ public abstract class EntityMoveMixin {
return result;
}
@Redirect(
method = "collide",
at = @At(value = "FIELD", target = "Lnet/minecraft/world/phys/AABB;minY:D")
)
private double antarchy$readLocalStepBase(AABB box) {
Entity entity = (Entity) (Object) this;
if (!AntarchyGravityApi.isGravityInverted(entity)) {
return box.minY;
}
return -box.maxY;
}
@Inject(method = "collide", at = @At("RETURN"), cancellable = true)
private void antarchy$collidePlayerToWorld(Vec3 movement, CallbackInfoReturnable<Vec3> cir) {
Entity entity = (Entity) (Object) this;
AntarchyGravityDirection direction = AntarchyGravityApi.getGravityDirection(entity);
if (!direction.isInverted()) {
return;
}
Vec3 worldResult = AntarchyGravityRotationUtil.vecPlayerToWorld(cir.getReturnValue(), direction);
cir.setReturnValue(PortalGunCollisionHelper.resolveCollision(
entity,
entity.getBoundingBox(),
this.antarchy$collideWorldMovement,
worldResult
));
}
@Unique
private boolean antarchy$intersectsAntiwater(Entity entity, AABB box) {
BlockPos.MutableBlockPos cursor = new BlockPos.MutableBlockPos();
@@ -0,0 +1,31 @@
package com.craisinlord.antarchy.mixins.gravity;
import com.craisinlord.antarchy.content.worldgen.thoraxis.ThoraxisUndersideManager;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.projectile.Projectile;
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.CallbackInfo;
@Mixin(Entity.class)
/*
* Entity declares applyGravity in 1.21.1. Restrict the reversed acceleration to
* projectiles that are physically inside the Thoraxis Underside.
*/
public abstract class EntityProjectileGravityMixin {
@Inject(method = "applyGravity", at = @At("HEAD"), cancellable = true)
private void antarchy$applyProjectileGravityUpward(CallbackInfo ci) {
Entity self = (Entity) (Object) this;
if (!(self instanceof Projectile projectile)
|| !ThoraxisUndersideManager.isInUnderside(projectile)) {
return;
}
projectile.setDeltaMovement(
projectile.getDeltaMovement().add(0.0D, projectile.getGravity(), 0.0D)
);
ci.cancel();
}
}
@@ -1,8 +1,14 @@
package com.craisinlord.antarchy.mixins.gravity;
import com.craisinlord.antarchy.content.gravity.AntarchyGravityApi;
import com.craisinlord.antarchy.content.gravity.AntarchyGravityRotationUtil;
import com.craisinlord.antarchy.content.worldgen.thoraxis.ThoraxisUndersideManager;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import com.llamalad7.mixinextras.sugar.Local;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.projectile.Projectile;
import net.minecraft.world.phys.Vec3;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
@@ -10,9 +16,8 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(Projectile.class)
/*
* Gives every projectile a gravity frame based on its own position in Thoraxis.
* A shooter can influence the initial aim, but cannot leave a projectile inverted
* after it exits the Underside.
* Keeps projectile movement in world space while reversing gravity and launch
* direction only inside the Thoraxis Underside.
*/
public abstract class ProjectileGravityMixin {
@@ -26,11 +31,39 @@ public abstract class ProjectileGravityMixin {
float inaccuracy,
CallbackInfo ci
) {
ThoraxisUndersideManager.updateProjectileGravity((Projectile) (Object) this);
ThoraxisUndersideManager.normalizeProjectileGravityFrame((Projectile) (Object) this);
}
@Inject(method = "tick", at = @At("HEAD"))
private void antarchy$updateUndersideGravity(CallbackInfo ci) {
ThoraxisUndersideManager.updateProjectileGravity((Projectile) (Object) this);
private void antarchy$keepWorldMovementFrame(CallbackInfo ci) {
ThoraxisUndersideManager.normalizeProjectileGravityFrame((Projectile) (Object) this);
}
@WrapOperation(
method = "shootFromRotation",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/world/entity/projectile/Projectile;shoot(DDDFF)V"
)
)
private void antarchy$rotateUndersideLaunchDirection(
Projectile projectile,
double x,
double y,
double z,
float velocity,
float inaccuracy,
Operation<Void> original,
@Local(argsOnly = true) Entity shooter
) {
Vec3 direction = new Vec3(x, y, z);
if (ThoraxisUndersideManager.shouldInvertInUnderside(shooter)) {
direction = AntarchyGravityRotationUtil.vecPlayerToWorld(
direction,
AntarchyGravityApi.getGravityDirection(shooter)
);
}
original.call(projectile, direction.x, direction.y, direction.z, velocity, inaccuracy);
}
}
@@ -1,20 +1,15 @@
package com.craisinlord.antarchy.mixins.gravity;
import com.craisinlord.antarchy.content.gravity.AntarchyGravityApi;
import com.craisinlord.antarchy.config.AntarchySettings;
import com.craisinlord.antarchy.content.worldgen.thoraxis.ThoraxisUndersideManager;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.entity.projectile.ThrowableProjectile;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
@Mixin(ThrowableProjectile.class)
/*
* Flips thrown projectile gravity for inverted shooters.
* Flips thrown projectile gravity from the projectile's current position.
*/
public abstract class ThrowableProjectileGravityMixin {
@@ -22,22 +17,12 @@ public abstract class ThrowableProjectileGravityMixin {
value = "INVOKE",
target = "Lnet/minecraft/world/entity/projectile/ThrowableProjectile;applyGravity()V"))
private void antarchy$applyThrownProjectileGravityUpward(ThrowableProjectile self, Operation<Void> original) {
Entity owner = self.getOwner();
if (!(owner instanceof LivingEntity living)) {
if (!ThoraxisUndersideManager.isInUnderside(self)) {
original.call(self);
return;
}
if (owner instanceof Player && !AntarchySettings.invertProjectilesFromInvertedPlayers()) {
original.call(self);
return;
}
if (!ThoraxisUndersideManager.shouldInvertInUnderside(living)) {
original.call(self);
return;
}
// Vanilla applies gravity as -getGravity(). Negate that acceleration to
// mirror the projectile's arc for the inverted Underside gravity.
// Vanilla subtracts gravity from world Y; the Underside adds it instead.
double gravity = self.getGravity();
self.setDeltaMovement(self.getDeltaMovement().add(0.0D, -gravity, 0.0D));
self.setDeltaMovement(self.getDeltaMovement().add(0.0D, gravity, 0.0D));
}
}
@@ -38,6 +38,7 @@
"MinecartItemMixin",
"GoopedStepSoundMixin",
"ExperimentalSettingsLevelSummaryMixin",
"CreativeModeTabsEnchantmentOrderMixin",
"NightmareSwordDamageMixin",
"NightmareArmorEffectImmunityMixin",
"PotionBrewingMixin",
@@ -63,6 +64,7 @@
"gravity.EntityGravityFrameMixin",
"gravity.EntityGravityMixin",
"gravity.EntityMoveMixin",
"gravity.EntityProjectileGravityMixin",
"gravity.ExplosionGravityMixin",
"gravity.FallingBlockEntityBlockDataMixin",
"gravity.FireworkRocketGravityMixin",
@@ -128,7 +130,6 @@
"client.InventoryScreenRotationBypassMixin",
"client.ExperimentalSettingsPopupMixin",
"client.KrakensGraspChargePoseMixin",
"client.LightningBoltRendererMixin",
"client.MountedVehicleCameraMixin",
"client.ParalyzedMouseTurnMixin",
"client.LocalPlayerPortalMixin",
@@ -140,6 +141,7 @@
"client.ThoraxisFogMixin",
"client.TigerEyeArmorHideMixin",
"client.TigerEyeNameTagHideMixin",
"client.TemporalContractionLivingRendererMixin",
"client.WolfRendererGrimMixin",
"gravity.client.CameraGravityMixin",
"gravity.client.ClientPacketListenerGravityMixin",
@@ -1,10 +1,10 @@
{
"variants": {
"trial_spawner_state=inactive": {"model": "antarchy:block/undertrial_spawner_inactive", "x": 90},
"trial_spawner_state=waiting_for_players": {"model": "antarchy:block/undertrial_spawner_inactive", "x": 90},
"trial_spawner_state=active": {"model": "antarchy:block/undertrial_spawner_active", "x": 90},
"trial_spawner_state=waiting_for_reward_ejection": {"model": "antarchy:block/undertrial_spawner_active", "x": 90},
"trial_spawner_state=ejecting_reward": {"model": "antarchy:block/undertrial_spawner_ejecting_reward", "x": 90},
"trial_spawner_state=cooldown": {"model": "antarchy:block/undertrial_spawner_inactive", "x": 90}
"trial_spawner_state=inactive": {"model": "antarchy:block/undertrial_spawner_inactive", "x": 180},
"trial_spawner_state=waiting_for_players": {"model": "antarchy:block/undertrial_spawner_inactive", "x": 180},
"trial_spawner_state=active": {"model": "antarchy:block/undertrial_spawner_active", "x": 180},
"trial_spawner_state=waiting_for_reward_ejection": {"model": "antarchy:block/undertrial_spawner_active", "x": 180},
"trial_spawner_state=ejecting_reward": {"model": "antarchy:block/undertrial_spawner_ejecting_reward", "x": 180},
"trial_spawner_state=cooldown": {"model": "antarchy:block/undertrial_spawner_inactive", "x": 180}
}
}
@@ -5,24 +5,24 @@
"vault_state=inactive,under_facing=east": {"model": "antarchy:block/undervault_off", "y": 90},
"vault_state=inactive,under_facing=west": {"model": "antarchy:block/undervault_off", "y": 270},
"vault_state=inactive,under_facing=up": {"model": "antarchy:block/undervault_off", "x": 270},
"vault_state=inactive,under_facing=down": {"model": "antarchy:block/undervault_off", "x": 90},
"vault_state=inactive,under_facing=down": {"model": "antarchy:block/undervault_off", "x": 180},
"vault_state=active,under_facing=north": {"model": "antarchy:block/undervault_on"},
"vault_state=active,under_facing=south": {"model": "antarchy:block/undervault_on", "y": 180},
"vault_state=active,under_facing=east": {"model": "antarchy:block/undervault_on", "y": 90},
"vault_state=active,under_facing=west": {"model": "antarchy:block/undervault_on", "y": 270},
"vault_state=active,under_facing=up": {"model": "antarchy:block/undervault_on", "x": 270},
"vault_state=active,under_facing=down": {"model": "antarchy:block/undervault_on", "x": 90},
"vault_state=active,under_facing=down": {"model": "antarchy:block/undervault_on", "x": 180},
"vault_state=unlocking,under_facing=north": {"model": "antarchy:block/undervault_on"},
"vault_state=unlocking,under_facing=south": {"model": "antarchy:block/undervault_on", "y": 180},
"vault_state=unlocking,under_facing=east": {"model": "antarchy:block/undervault_on", "y": 90},
"vault_state=unlocking,under_facing=west": {"model": "antarchy:block/undervault_on", "y": 270},
"vault_state=unlocking,under_facing=up": {"model": "antarchy:block/undervault_on", "x": 270},
"vault_state=unlocking,under_facing=down": {"model": "antarchy:block/undervault_on", "x": 90},
"vault_state=unlocking,under_facing=down": {"model": "antarchy:block/undervault_on", "x": 180},
"vault_state=ejecting,under_facing=north": {"model": "antarchy:block/undervault_ejecting"},
"vault_state=ejecting,under_facing=south": {"model": "antarchy:block/undervault_ejecting", "y": 180},
"vault_state=ejecting,under_facing=east": {"model": "antarchy:block/undervault_ejecting", "y": 90},
"vault_state=ejecting,under_facing=west": {"model": "antarchy:block/undervault_ejecting", "y": 270},
"vault_state=ejecting,under_facing=up": {"model": "antarchy:block/undervault_ejecting", "x": 270},
"vault_state=ejecting,under_facing=down": {"model": "antarchy:block/undervault_ejecting", "x": 90}
"vault_state=ejecting,under_facing=down": {"model": "antarchy:block/undervault_ejecting", "x": 180}
}
}
@@ -8,5 +8,7 @@
{"type":"minecraft:item","name":"antarchy:blood_crystal_shard","weight":9,"functions":[{"function":"minecraft:set_count","count":{"type":"minecraft:uniform","min":1,"max":3}}]},
{"type":"minecraft:item","name":"antarchy:blood_crystal_apple","weight":15,"functions":[{"function":"minecraft:set_count","count":{"type":"minecraft:uniform","min":1,"max":2}}]},
{"type":"minecraft:item","name":"minecraft:potion","weight":8,"functions":[{"function":"minecraft:set_potion","id":"antarchy:inversion"}]},
{"type":"minecraft:item","name":"minecraft:potion","weight":6,"functions":[{"function":"minecraft:set_potion","id":"antarchy:time_contraction"}]},
{"type":"minecraft:item","name":"minecraft:potion","weight":6,"functions":[{"function":"minecraft:set_potion","id":"antarchy:time_dilation"}]},
{"type":"minecraft:item","name":"minecraft:book","weight":6,"functions":[{"function":"minecraft:enchant_randomly","options":["minecraft:mending","minecraft:unbreaking","minecraft:efficiency"]}]}
]}],"random_sequence":"antarchy:chests/underside_ruins/generic"}
@@ -8,6 +8,7 @@
{"type":"minecraft:item","name":"minecraft:diamond_pickaxe","weight":8,"functions":[{"function":"minecraft:enchant_with_levels","levels":{"type":"minecraft:uniform","min":18,"max":30},"options":"#minecraft:on_random_loot"}]},
{"type":"minecraft:item","name":"minecraft:book","weight":10,"functions":[{"function":"minecraft:enchant_randomly","options":["minecraft:mending","minecraft:unbreaking","minecraft:efficiency","minecraft:protection","antarchy:feather_rising","antarchy:chronosphere"]}]},
{"type":"minecraft:item","name":"minecraft:tipped_arrow","weight":7,"functions":[{"function":"minecraft:set_count","count":{"type":"minecraft:uniform","min":4,"max":10}},{"function":"minecraft:set_potion","id":"antarchy:inversion"}]},
{"type":"minecraft:item","name":"minecraft:potion","weight":7,"functions":[{"function":"minecraft:set_potion","id":"antarchy:inversion"}]},
{"type":"minecraft:item","name":"minecraft:potion","weight":6,"functions":[{"function":"minecraft:set_potion","id":"antarchy:time_contraction"}]},
{"type":"minecraft:item","name":"minecraft:potion","weight":6,"functions":[{"function":"minecraft:set_potion","id":"antarchy:time_dilation"}]},
{"type":"minecraft:item","name":"antarchy:titanium_ingot","weight":1},{"type":"minecraft:item","name":"antarchy:uranium_ingot","weight":1}
]}],"random_sequence":"antarchy:chests/underside_ruins/treasure"}
@@ -0,0 +1,135 @@
{
"type": "minecraft:chest",
"pools": [
{
"rolls": {
"type": "minecraft:uniform",
"min": 4,
"max": 6
},
"entries": [
{
"type": "minecraft:item",
"name": "antarchy:ultimate_helmet",
"weight": 2
},
{
"type": "minecraft:item",
"name": "antarchy:ultimate_chestplate",
"weight": 2
},
{
"type": "minecraft:item",
"name": "antarchy:ultimate_leggings",
"weight": 2
},
{
"type": "minecraft:item",
"name": "antarchy:ultimate_boots",
"weight": 2
},
{
"type": "minecraft:item",
"name": "antarchy:ultimate_sword",
"weight": 2
},
{
"type": "minecraft:item",
"name": "antarchy:ultimate_pickaxe",
"weight": 2
},
{
"type": "minecraft:item",
"name": "antarchy:ultimate_axe",
"weight": 2
},
{
"type": "minecraft:item",
"name": "antarchy:ultimate_shovel",
"weight": 2
},
{
"type": "minecraft:item",
"name": "antarchy:ultimate_hoe",
"weight": 2
},
{
"type": "minecraft:item",
"name": "antarchy:ultimate_bow",
"weight": 2
},
{
"type": "minecraft:item",
"name": "antarchy:ultimate_crossbow",
"weight": 2
},
{
"type": "minecraft:item",
"name": "minecraft:potion",
"weight": 8,
"functions": [
{
"function": "minecraft:set_potion",
"id": "antarchy:time_contraction"
}
]
},
{
"type": "minecraft:item",
"name": "minecraft:potion",
"weight": 8,
"functions": [
{
"function": "minecraft:set_potion",
"id": "antarchy:time_dilation"
}
]
},
{
"type": "minecraft:item",
"name": "minecraft:book",
"weight": 4,
"functions": [
{
"function": "minecraft:enchant_randomly",
"options": [
"antarchy:chronosphere"
]
}
]
},
{
"type": "minecraft:item",
"name": "antarchy:queen_scale",
"weight": 1
},
{
"type": "minecraft:item",
"name": "minecraft:netherite_scrap",
"weight": 4,
"functions": [
{
"function": "minecraft:set_count",
"count": {
"type": "minecraft:uniform",
"min": 1,
"max": 3
}
}
]
},
{
"type": "minecraft:item",
"name": "antarchy:titanium_block",
"weight": 3
},
{
"type": "minecraft:item",
"name": "antarchy:uranium_block",
"weight": 3
}
]
}
],
"random_sequence": "antarchy:chests/undertrial_vault"
}
@@ -3,5 +3,5 @@
{"type":"minecraft:item","name":"antarchy:blood_crystal_katana","weight":4},{"type":"minecraft:item","name":"antarchy:blood_crystal_chestplate","weight":2},{"type":"minecraft:item","name":"antarchy:blood_crystal_leggings","weight":2},{"type":"minecraft:item","name":"antarchy:lucid_pearl","weight":4},
{"type":"minecraft:item","name":"antarchy:bluestone","weight":14,"functions":[{"function":"minecraft:set_count","count":{"type":"minecraft:uniform","min":8,"max":20}}]},{"type":"minecraft:item","name":"antarchy:nyxite","weight":10,"functions":[{"function":"minecraft:set_count","count":{"type":"minecraft:uniform","min":3,"max":8}}]},
{"type":"minecraft:item","name":"antarchy:blood_crystal_apple","weight":20,"functions":[{"function":"minecraft:set_count","count":{"type":"minecraft:uniform","min":1,"max":3}}]},
{"type":"minecraft:item","name":"minecraft:tipped_arrow","weight":8,"functions":[{"function":"minecraft:set_count","count":{"type":"minecraft:uniform","min":4,"max":10}},{"function":"minecraft:set_potion","id":"antarchy:inversion"}]},{"type":"minecraft:item","name":"minecraft:potion","weight":8,"functions":[{"function":"minecraft:set_potion","id":"antarchy:inversion"}]},{"type":"minecraft:item","name":"minecraft:book","weight":10,"functions":[{"function":"minecraft:enchant_randomly","options":["minecraft:mending","minecraft:unbreaking","minecraft:efficiency","minecraft:fortune","antarchy:feather_rising","antarchy:chronosphere"]}]},{"type":"minecraft:item","name":"antarchy:titanium_ingot","weight":1},{"type":"minecraft:item","name":"antarchy:uranium_ingot","weight":1}
{"type":"minecraft:item","name":"minecraft:tipped_arrow","weight":8,"functions":[{"function":"minecraft:set_count","count":{"type":"minecraft:uniform","min":4,"max":10}},{"function":"minecraft:set_potion","id":"antarchy:inversion"}]},{"type":"minecraft:item","name":"minecraft:potion","weight":8,"functions":[{"function":"minecraft:set_potion","id":"antarchy:inversion"}]},{"type":"minecraft:item","name":"minecraft:potion","weight":6,"functions":[{"function":"minecraft:set_potion","id":"antarchy:time_contraction"}]},{"type":"minecraft:item","name":"minecraft:potion","weight":6,"functions":[{"function":"minecraft:set_potion","id":"antarchy:time_dilation"}]},{"type":"minecraft:item","name":"minecraft:book","weight":10,"functions":[{"function":"minecraft:enchant_randomly","options":["minecraft:mending","minecraft:unbreaking","minecraft:efficiency","minecraft:fortune","antarchy:feather_rising","antarchy:chronosphere"]}]},{"type":"minecraft:item","name":"antarchy:titanium_ingot","weight":1},{"type":"minecraft:item","name":"antarchy:uranium_ingot","weight":1}
]}],"random_sequence":"antarchy:spawners/underside_ruins/hard"}
@@ -5,5 +5,5 @@
{"type":"minecraft:item","name":"antarchy:bluestone","weight":15,"functions":[{"function":"minecraft:set_count","count":{"type":"minecraft:uniform","min":5,"max":14}}]},
{"type":"minecraft:item","name":"antarchy:nyxite","weight":10,"functions":[{"function":"minecraft:set_count","count":{"type":"minecraft:uniform","min":1,"max":4}}]},
{"type":"minecraft:item","name":"antarchy:blood_crystal_apple","weight":18,"functions":[{"function":"minecraft:set_count","count":{"type":"minecraft:uniform","min":1,"max":2}}]},
{"type":"minecraft:item","name":"minecraft:potion","weight":9,"functions":[{"function":"minecraft:set_potion","id":"antarchy:inversion"}]},{"type":"minecraft:item","name":"minecraft:book","weight":7,"functions":[{"function":"minecraft:enchant_randomly","options":["minecraft:mending","minecraft:unbreaking","minecraft:efficiency"]}]}
{"type":"minecraft:item","name":"minecraft:potion","weight":9,"functions":[{"function":"minecraft:set_potion","id":"antarchy:inversion"}]},{"type":"minecraft:item","name":"minecraft:potion","weight":6,"functions":[{"function":"minecraft:set_potion","id":"antarchy:time_contraction"}]},{"type":"minecraft:item","name":"minecraft:potion","weight":6,"functions":[{"function":"minecraft:set_potion","id":"antarchy:time_dilation"}]},{"type":"minecraft:item","name":"minecraft:book","weight":7,"functions":[{"function":"minecraft:enchant_randomly","options":["minecraft:mending","minecraft:unbreaking","minecraft:efficiency"]}]}
]}],"random_sequence":"antarchy:spawners/underside_ruins/medium"}
@@ -0,0 +1,76 @@
{
"type": "minecraft:chest",
"pools": [
{
"rolls": {
"type": "minecraft:uniform",
"min": 2,
"max": 4
},
"entries": [
{
"type": "minecraft:item",
"name": "antarchy:undertrial_key",
"weight": 2
},
{
"type": "minecraft:item",
"name": "minecraft:potion",
"weight": 8,
"functions": [
{
"function": "minecraft:set_potion",
"id": "antarchy:time_dilation"
}
]
},
{
"type": "minecraft:item",
"name": "antarchy:cornea_stalk",
"weight": 8,
"functions": [
{
"function": "minecraft:set_count",
"count": {
"type": "minecraft:uniform",
"min": 1,
"max": 3
}
}
]
},
{
"type": "minecraft:item",
"name": "antarchy:bluestone",
"weight": 10,
"functions": [
{
"function": "minecraft:set_count",
"count": {
"type": "minecraft:uniform",
"min": 2,
"max": 8
}
}
]
},
{
"type": "minecraft:item",
"name": "minecraft:diamond",
"weight": 4,
"functions": [
{
"function": "minecraft:set_count",
"count": {
"type": "minecraft:uniform",
"min": 1,
"max": 2
}
}
]
}
]
}
],
"random_sequence": "antarchy:spawners/undertrial"
}
@@ -2,6 +2,11 @@
"replace": false,
"values": [
"antarchy:lucid",
"antarchy:reverie"
"antarchy:reverie",
"antarchy:scorpion",
"antarchy:bed_bug",
"antarchy:queen",
"antarchy:princess",
"antarchy:manticore"
]
}
@@ -6,8 +6,7 @@
"antarchy:lucid",
"antarchy:reverie",
"antarchy:bed_bug",
"antarchy:crawling_blight",
"antarchy:skulking_fright",
"antarchy:springbug"
"antarchy:scorpion",
"antarchy:emperor_scorpion"
]
}
@@ -0,0 +1,6 @@
{
"replace": false,
"values": [
"antarchy:dream_dunes"
]
}
@@ -0,0 +1,27 @@
{
"type": "integrated_api:ceiling_structure",
"start_pool": "antarchy:underside_castle/underside_castle",
"size": 1,
"y_allowance": {
"min_y_allowed": -256,
"max_y_allowed": 24
},
"valid_biome_radius_check": 1,
"biomes": "#antarchy:has_structure/underside_castle_biomes",
"step": "underground_structures",
"enhanced_terrain_adaptation": {
"type": "integrated_api:none"
},
"start_height": {
"type": "minecraft:constant",
"value": {
"absolute": -128
}
},
"max_roof_search_distance": 256,
"min_roof_thickness": 3,
"min_open_space_below": 16,
"ceiling_offset": 0,
"spawn_overrides": {},
"liquid_settings": "ignore_waterlogging"
}
@@ -2,6 +2,10 @@
"structures": [
{
"structure": "antarchy:underside_ruins",
"weight": 2
},
{
"structure": "antarchy:underside_castle",
"weight": 1
}
],
@@ -0,0 +1,42 @@
{
"name": "antarchy:underside_castle/underside_castle",
"fallback": "minecraft:empty",
"elements": [
{
"weight": 1,
"element": {
"location": "antarchy:underside_castle/underside_castle1",
"processors": "antarchy:generic_processor",
"projection": "rigid",
"element_type": "integrated_api:integrated_api_single_pool_element"
}
},
{
"weight": 1,
"element": {
"location": "antarchy:underside_castle/underside_castle2",
"processors": "antarchy:generic_processor",
"projection": "rigid",
"element_type": "integrated_api:integrated_api_single_pool_element"
}
},
{
"weight": 1,
"element": {
"location": "antarchy:underside_castle/underside_castle3",
"processors": "antarchy:generic_processor",
"projection": "rigid",
"element_type": "integrated_api:integrated_api_single_pool_element"
}
},
{
"weight": 1,
"element": {
"location": "antarchy:underside_castle/underside_castle4",
"processors": "antarchy:generic_processor",
"projection": "rigid",
"element_type": "integrated_api:integrated_api_single_pool_element"
}
}
]
}
@@ -73,6 +73,15 @@
"projection": "rigid",
"element_type": "integrated_api:integrated_api_single_pool_element"
}
},
{
"weight": 1,
"element": {
"location": "antarchy:underside_ruins/underside_ruins9",
"processors": "antarchy:generic_processor",
"projection": "rigid",
"element_type": "integrated_api:integrated_api_single_pool_element"
}
}
]
}
@@ -6,6 +6,7 @@
"antarchy:blood_crystal_chestplate",
"antarchy:primordial_chestplate",
"antarchy:royal_guardian_chestplate",
"antarchy:royal_assailant_chestplate"
"antarchy:royal_assailant_chestplate",
"antarchy:tigers_eye_chestplate"
]
}
@@ -41,6 +41,10 @@
"antarchy:royal_assailant_chestplate",
"antarchy:royal_assailant_leggings",
"antarchy:royal_assailant_boots",
"antarchy:tigers_eye_helmet",
"antarchy:tigers_eye_chestplate",
"antarchy:tigers_eye_leggings",
"antarchy:tigers_eye_boots",
"antarchy:royal_guardian_sword",
"antarchy:royal_assailant_battleaxe",
"antarchy:royal_guardian_shield",
@@ -7,6 +7,10 @@
"antarchy:brutalfly_elytra",
"antarchy:springy_boots",
"antarchy:manticore_wings",
"antarchy:tigers_eye_helmet",
"antarchy:tigers_eye_chestplate",
"antarchy:tigers_eye_leggings",
"antarchy:tigers_eye_boots",
"antarchy:royal_assailant_battleaxe"
]
}
@@ -7,6 +7,7 @@
"antarchy:primordial_boots",
"antarchy:springy_boots",
"antarchy:royal_guardian_boots",
"antarchy:royal_assailant_boots"
"antarchy:royal_assailant_boots",
"antarchy:tigers_eye_boots"
]
}
@@ -8,6 +8,7 @@
"antarchy:fallen_king_crown",
"antarchy:primordial_helmet",
"antarchy:royal_guardian_helmet",
"antarchy:royal_assailant_helmet"
"antarchy:royal_assailant_helmet",
"antarchy:tigers_eye_helmet"
]
}
@@ -6,6 +6,7 @@
"antarchy:blood_crystal_leggings",
"antarchy:primordial_leggings",
"antarchy:royal_guardian_leggings",
"antarchy:royal_assailant_leggings"
"antarchy:royal_assailant_leggings",
"antarchy:tigers_eye_leggings"
]
}