Compare commits

..
Author SHA1 Message Date
craisinlord e505367aed Merge pull request #81 from craisinlord/anthology-split
2.2.6
2026-10-20 10:24:44 -05:00
craisinlord c00d57e4e0 2.2.6 2026-10-20 10:23:25 -05:00
craisinlord ffadad3caf Merge pull request #79 from craisinlord/anthology-split
2.2.5
2026-10-17 14:46:58 -05:00
craisinlord 758fa58b1b 2.2.5
add missing local jars to git
2026-10-17 14:41:03 -05:00
craisinlord 770b5a607e fix apotheosis 1.20 crash 2026-10-16 15:34:52 -05:00
craisinlord 333269c3da perf improvements with portal guns
fix infinity crash
2026-10-16 14:55:59 -05:00
craisinlord 2c63c7bb0b Merge pull request #75 from craisinlord/anthology-split
1.20 backport
2026-10-14 17:43:07 -05:00
craisinlord d54264c6c8 1.20 backport 2026-10-14 17:39:56 -05:00
craisinlord adb7372f3f Merge pull request #74 from craisinlord/anthology-split
Anthology split
2026-10-13 08:37:52 -05:00
craisinlord f7e7bebac3 anthology split 2026-10-12 19:55:08 -05:00
craisinlord bba2a56671 Move gravity direction system to Anthology
Gravity API, mixins, projectile compat, inverted effect, gravity sync and
/gravity now live in the Anthology library. Antarchy registers the Underside
region, antiwater, stale-inversion cleanup and portal gun camera roll through
GravityHooks, and depends on the local Anthology jar in anthology_jars/.
2026-10-12 15:37:13 -05:00
craisinlord 0cad456efd improve portal gun perf 2026-10-12 15:00:42 -05:00
5075 changed files with 70237 additions and 97306 deletions

No files matched your search

Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,6 +1,9 @@
package com.craisinlord.antarchy;
import com.craisinlord.antarchy.content.computer.AntarchyArchiveLocators;
import com.craisinlord.antarchy.content.AntarchyAnthologyHooks;
import com.craisinlord.antarchy.content.util.AttributeRangeInit;
import com.craisinlord.antarchy.content.worldgen.VillagePoolAdditions;
import com.craisinlord.integrated_api.events.lifecycle.ServerGoingToStartEvent;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@@ -8,12 +11,17 @@ import org.apache.logging.log4j.Logger;
public class Antarchy {
public static final String MODID = "antarchy";
public static final String MOD_VERSION = "2.0.0";
public static final Logger LOGGER = LogManager.getLogger();
public static volatile boolean physicalClient;
public static void init() {
AttributeRangeInit.apply();
AntarchyArchiveLocators.register();
AntarchyAnthologyHooks.register();
ServerGoingToStartEvent.EVENT.addListener(Antarchy::serverAboutToStart);
}
private static void serverAboutToStart(final ServerGoingToStartEvent event) {
VillagePoolAdditions.apply(event.getServer());
}
}
@@ -0,0 +1,77 @@
package com.craisinlord.antarchy.compat.entity;
import com.craisinlord.antarchy.content.AntarchyObjects;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.Multimap;
import java.nio.charset.StandardCharsets;
import java.util.UUID;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.EquipmentSlot;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.ai.attributes.Attribute;
import net.minecraft.world.entity.ai.attributes.AttributeInstance;
import net.minecraft.world.entity.ai.attributes.AttributeModifier;
import net.minecraft.world.entity.player.Player;
import org.jetbrains.annotations.Nullable;
public final class EntityReach {
public static final double DEFAULT_ENTITY_INTERACTION_RANGE = 3.0D;
public static final double DEFAULT_BLOCK_INTERACTION_RANGE = 4.5D;
private static final double CREATIVE_ENTITY_BONUS = 2.0D;
private static final double CREATIVE_BLOCK_BONUS = 0.5D;
private EntityReach() {
}
@Nullable
public static Attribute attribute() {
java.util.function.Supplier<Attribute> supplier = AntarchyObjects.ENTITY_REACH;
return supplier == null ? null : supplier.get();
}
@Nullable
public static AttributeInstance instance(LivingEntity entity) {
Attribute attribute = attribute();
return attribute == null ? null : entity.getAttribute(attribute);
}
public static double entityInteractionRange(LivingEntity entity) {
AttributeInstance instance = instance(entity);
double range = instance != null ? instance.getValue() : DEFAULT_ENTITY_INTERACTION_RANGE;
if (entity instanceof Player player && player.isCreative()) {
range += CREATIVE_ENTITY_BONUS;
}
return range;
}
public static double blockInteractionRange(Player player) {
return DEFAULT_BLOCK_INTERACTION_RANGE + (player.isCreative() ? CREATIVE_BLOCK_BONUS : 0.0D);
}
public static UUID modifierId(ResourceLocation id) {
return UUID.nameUUIDFromBytes(id.toString().getBytes(StandardCharsets.UTF_8));
}
public static AttributeModifier modifier(ResourceLocation id, double amount, AttributeModifier.Operation operation) {
return new AttributeModifier(modifierId(id), id.toString(), amount, operation);
}
public static void addModifier(ImmutableMultimap.Builder<Attribute, AttributeModifier> builder, ResourceLocation id, double amount) {
Attribute attribute = attribute();
if (attribute != null) {
builder.put(attribute, modifier(id, amount, AttributeModifier.Operation.ADDITION));
}
}
public static Multimap<Attribute, AttributeModifier> withMainhandModifier(Multimap<Attribute, AttributeModifier> base, EquipmentSlot slot,
ResourceLocation id, double amount) {
Attribute attribute = attribute();
if (slot != EquipmentSlot.MAINHAND || attribute == null) {
return base;
}
ImmutableMultimap.Builder<Attribute, AttributeModifier> builder = ImmutableMultimap.builder();
builder.putAll(base);
builder.put(attribute, modifier(id, amount, AttributeModifier.Operation.ADDITION));
return builder.build();
}
}
@@ -4,6 +4,7 @@ import net.minecraft.core.BlockPos;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.MinecraftServer;
import net.minecraft.util.RandomSource;
public interface InfinityCompatBridge {
@@ -22,15 +23,14 @@ public interface InfinityCompatBridge {
return getRandomDimensionId(random);
}
default boolean requestWarp(ServerPlayer player, ResourceLocation dimensionId) {
return false;
default InfinityWarpResult requestWarp(ServerPlayer player, ResourceLocation dimensionId) {
return InfinityWarpResult.REJECTED;
}
default InfinityWarpResult requestDimensionCreation(MinecraftServer server, ResourceLocation dimensionId) {
return InfinityWarpResult.REJECTED;
}
/**
* Converts an already-ignited vanilla Nether portal frame at {@code anyPortalBlockPos} into a real, permanent
* Infinite Dimensions portal to {@code dimensionId}, generating the dimension first if it doesn't exist yet.
* Mirrors what Infinity itself does when a written book is thrown into a lit portal.
*/
default boolean createPhysicalPortal(ServerLevel level, BlockPos anyPortalBlockPos, ResourceLocation dimensionId) {
return false;
}
@@ -0,0 +1,86 @@
package com.craisinlord.antarchy.compat.infinity;
import com.craisinlord.antarchy.Antarchy;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import net.minecraft.core.Registry;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.resources.ResourceLocation;
import java.io.Reader;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
public final class InfinityConfigRefresh {
private static final Map<String, Registry<?>> CHECKED_FILES = Map.of(
"mobs.json", BuiltInRegistries.ENTITY_TYPE,
"items.json", BuiltInRegistries.ITEM,
"effects.json", BuiltInRegistries.MOB_EFFECT
);
private static final Set<String> DISABLED_IDS = Set.of("antarchy:paralyzed");
private InfinityConfigRefresh() {
}
public static void refreshIfStale(Path configDir) {
Path modularDir = configDir.resolve("infinity").resolve("modular");
Path invocationLock = modularDir.resolve("invocation.lock");
if (!Files.exists(invocationLock)) {
return;
}
Path antarchyDir = modularDir.resolve(Antarchy.MODID);
List<String> staleIds = new ArrayList<>();
CHECKED_FILES.forEach((fileName, registry) -> collectStaleIds(antarchyDir.resolve(fileName), registry, staleIds));
if (staleIds.isEmpty()) {
return;
}
try {
Files.delete(invocationLock);
Antarchy.LOGGER.info(
"[Antarchy] Infinite Dimensions config lists unregistered or disabled Antarchy content {}; its config will regenerate on next world join",
staleIds
);
} catch (Exception exception) {
Antarchy.LOGGER.warn("[Antarchy] Failed to reset stale Infinite Dimensions config at {}", invocationLock, exception);
}
}
private static void collectStaleIds(Path file, Registry<?> registry, List<String> staleIds) {
if (!Files.exists(file)) {
return;
}
try (Reader reader = Files.newBufferedReader(file, StandardCharsets.UTF_8)) {
JsonElement root = JsonParser.parseReader(reader);
if (!root.isJsonObject() || !root.getAsJsonObject().has("elements")) {
return;
}
for (JsonElement element : root.getAsJsonObject().getAsJsonArray("elements")) {
if (!element.isJsonObject()) {
continue;
}
JsonObject entry = element.getAsJsonObject();
if (!entry.has("key")) {
continue;
}
String key = entry.get("key").getAsString();
ResourceLocation id = ResourceLocation.tryParse(key);
if (id != null && Antarchy.MODID.equals(id.getNamespace()) && !registry.containsKey(id)) {
staleIds.add(key);
} else if (DISABLED_IDS.contains(key) && (!entry.has("weight") || entry.get("weight").getAsDouble() > 0.0D)) {
staleIds.add(key);
}
}
} catch (Exception exception) {
Antarchy.LOGGER.warn("[Antarchy] Failed to read Infinite Dimensions config {}", file, exception);
}
}
}
@@ -1,6 +1,5 @@
package com.craisinlord.antarchy.compat.infinity;
/** Shared result vocabulary for future asynchronous Infinity dimension integrations. */
public enum InfinityWarpResult {
REJECTED,
PENDING,
@@ -2,10 +2,8 @@ package com.craisinlord.antarchy.compat.item;
import java.util.function.Function;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.ItemStack;
/** NBT-backed stand-in for the 1.20.1 port of Minecraft's 1.20.5+ item components. */
public final class AntarchyItemComponent<T> {
private final String key;
private final Function<CompoundTag, T> reader;
@@ -22,14 +20,22 @@ public final class AntarchyItemComponent<T> {
}
public T get(ItemStack stack) {
if (!this.has(stack)) {
return null;
}
return this.reader.apply(stack.getTag());
}
public T getOrDefault(ItemStack stack, T fallback) {
return this.has(stack) ? this.get(stack) : fallback;
T value = this.get(stack);
return value != null ? value : fallback;
}
public void set(ItemStack stack, T value) {
if (value == null) {
this.remove(stack);
return;
}
Object encoded = this.writer.apply(value);
CompoundTag tag = stack.getOrCreateTag();
if (encoded instanceof Integer integer) tag.putInt(this.key, integer);
@@ -49,7 +55,7 @@ public final class AntarchyItemComponent<T> {
return new AntarchyItemComponent<>(key, tag -> tag.getInt(key), value -> value);
}
public static <T> AntarchyItemComponent<T> marker(String key) {
return new AntarchyItemComponent<>(key, tag -> null, value -> true);
public static AntarchyItemComponent<net.minecraft.util.Unit> marker(String key) {
return new AntarchyItemComponent<>(key, tag -> net.minecraft.util.Unit.INSTANCE, value -> true);
}
}
@@ -0,0 +1,17 @@
package com.craisinlord.antarchy.compat.item;
import net.minecraft.world.item.ArmorItem;
public final class ArmorDurability {
private ArmorDurability() {
}
public static int forType(ArmorItem.Type type, int multiplier) {
return switch (type) {
case HELMET -> 11 * multiplier;
case CHESTPLATE -> 16 * multiplier;
case LEGGINGS -> 15 * multiplier;
case BOOTS -> 13 * multiplier;
};
}
}
@@ -0,0 +1,46 @@
package com.craisinlord.antarchy.compat.item;
import com.craisinlord.antarchy.compat.entity.EntityReach;
import com.google.common.collect.ImmutableMultimap;
import java.util.UUID;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.ai.attributes.Attribute;
import net.minecraft.world.entity.ai.attributes.AttributeModifier;
import net.minecraft.world.entity.ai.attributes.Attributes;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.Tier;
public final class WeaponAttributes {
private WeaponAttributes() {
}
public static ImmutableMultimap.Builder<Attribute, AttributeModifier> base(Tier tier, float attackDamage, float attackSpeed) {
ImmutableMultimap.Builder<Attribute, AttributeModifier> builder = ImmutableMultimap.builder();
builder.put(Attributes.ATTACK_DAMAGE, new AttributeModifier(Ids.ATTACK_DAMAGE, "Weapon modifier",
attackDamage + tier.getAttackDamageBonus(), AttributeModifier.Operation.ADDITION));
builder.put(Attributes.ATTACK_SPEED, new AttributeModifier(Ids.ATTACK_SPEED, "Weapon modifier",
attackSpeed, AttributeModifier.Operation.ADDITION));
return builder;
}
public static void add(ImmutableMultimap.Builder<Attribute, AttributeModifier> builder, Attribute attribute, ResourceLocation id, double amount) {
builder.put(attribute, EntityReach.modifier(id, amount, AttributeModifier.Operation.ADDITION));
}
public static UUID baseAttackDamageId() {
return Ids.ATTACK_DAMAGE;
}
public static UUID baseAttackSpeedId() {
return Ids.ATTACK_SPEED;
}
private static final class Ids extends Item {
static final UUID ATTACK_DAMAGE = BASE_ATTACK_DAMAGE_UUID;
static final UUID ATTACK_SPEED = BASE_ATTACK_SPEED_UUID;
private Ids() {
super(new Item.Properties());
}
}
}
@@ -1,93 +0,0 @@
package com.craisinlord.antarchy.compat.network;
import io.netty.buffer.ByteBuf;
import net.minecraft.network.FriendlyByteBuf;
public final class ByteBufCodecs {
private ByteBufCodecs() {
}
public static final StreamCodec<ByteBuf, Integer> VAR_INT = new StreamCodec<>() {
@Override
public Integer decode(ByteBuf buf) {
return ((FriendlyByteBuf) buf).readVarInt();
}
@Override
public void encode(ByteBuf buf, Integer value) {
((FriendlyByteBuf) buf).writeVarInt(value);
}
};
public static final StreamCodec<ByteBuf, Long> VAR_LONG = new StreamCodec<>() {
@Override
public Long decode(ByteBuf buf) {
return ((FriendlyByteBuf) buf).readVarLong();
}
@Override
public void encode(ByteBuf buf, Long value) {
((FriendlyByteBuf) buf).writeVarLong(value);
}
};
public static final StreamCodec<ByteBuf, Boolean> BOOL = new StreamCodec<>() {
@Override
public Boolean decode(ByteBuf buf) {
return ((FriendlyByteBuf) buf).readBoolean();
}
@Override
public void encode(ByteBuf buf, Boolean value) {
((FriendlyByteBuf) buf).writeBoolean(value);
}
};
public static final StreamCodec<ByteBuf, Byte> BYTE = new StreamCodec<>() {
@Override
public Byte decode(ByteBuf buf) {
return ((FriendlyByteBuf) buf).readByte();
}
@Override
public void encode(ByteBuf buf, Byte value) {
((FriendlyByteBuf) buf).writeByte(value);
}
};
public static final StreamCodec<ByteBuf, Float> FLOAT = new StreamCodec<>() {
@Override
public Float decode(ByteBuf buf) {
return ((FriendlyByteBuf) buf).readFloat();
}
@Override
public void encode(ByteBuf buf, Float value) {
((FriendlyByteBuf) buf).writeFloat(value);
}
};
public static final StreamCodec<ByteBuf, Double> DOUBLE = new StreamCodec<>() {
@Override
public Double decode(ByteBuf buf) {
return ((FriendlyByteBuf) buf).readDouble();
}
@Override
public void encode(ByteBuf buf, Double value) {
((FriendlyByteBuf) buf).writeDouble(value);
}
};
public static final StreamCodec<ByteBuf, String> STRING_UTF8 = new StreamCodec<>() {
@Override
public String decode(ByteBuf buf) {
return ((FriendlyByteBuf) buf).readUtf();
}
@Override
public void encode(ByteBuf buf, String value) {
((FriendlyByteBuf) buf).writeUtf(value);
}
};
}
@@ -1,10 +0,0 @@
package com.craisinlord.antarchy.compat.network;
import net.minecraft.resources.ResourceLocation;
public interface CustomPacketPayload {
Type<? extends CustomPacketPayload> type();
record Type<T extends CustomPacketPayload>(ResourceLocation id) {
}
}
@@ -1,204 +0,0 @@
package com.craisinlord.antarchy.compat.network;
import java.util.function.Function;
public interface StreamCodec<B, V> {
V decode(B buf);
void encode(B buf, V value);
default <O> StreamCodec<B, O> map(Function<V, O> to, Function<O, V> from) {
StreamCodec<B, V> self = this;
return new StreamCodec<>() {
@Override
public O decode(B buf) {
return to.apply(self.decode(buf));
}
@Override
public void encode(B buf, O value) {
self.encode(buf, from.apply(value));
}
};
}
static <B, V> StreamCodec<B, V> unit(V value) {
return new StreamCodec<>() {
@Override
public V decode(B buf) {
return value;
}
@Override
public void encode(B buf, V value2) {
}
};
}
static <B, C1, V> StreamCodec<B, V> composite(
StreamCodec<B, C1> codec1, Function<V, C1> getter1,
Function<C1, V> factory) {
return new StreamCodec<>() {
@Override
public V decode(B buf) {
C1 v1 = codec1.decode(buf);
return factory.apply(v1);
}
@Override
public void encode(B buf, V value) {
codec1.encode(buf, getter1.apply(value));
}
};
}
interface Factory2<C1, C2, V> {
V create(C1 c1, C2 c2);
}
static <B, C1, C2, V> StreamCodec<B, V> composite(
StreamCodec<B, C1> codec1, Function<V, C1> getter1,
StreamCodec<B, C2> codec2, Function<V, C2> getter2,
Factory2<C1, C2, V> factory) {
return new StreamCodec<>() {
@Override
public V decode(B buf) {
C1 v1 = codec1.decode(buf);
C2 v2 = codec2.decode(buf);
return factory.create(v1, v2);
}
@Override
public void encode(B buf, V value) {
codec1.encode(buf, getter1.apply(value));
codec2.encode(buf, getter2.apply(value));
}
};
}
interface Factory3<C1, C2, C3, V> {
V create(C1 c1, C2 c2, C3 c3);
}
static <B, C1, C2, C3, V> StreamCodec<B, V> composite(
StreamCodec<B, C1> codec1, Function<V, C1> getter1,
StreamCodec<B, C2> codec2, Function<V, C2> getter2,
StreamCodec<B, C3> codec3, Function<V, C3> getter3,
Factory3<C1, C2, C3, V> factory) {
return new StreamCodec<>() {
@Override
public V decode(B buf) {
C1 v1 = codec1.decode(buf);
C2 v2 = codec2.decode(buf);
C3 v3 = codec3.decode(buf);
return factory.create(v1, v2, v3);
}
@Override
public void encode(B buf, V value) {
codec1.encode(buf, getter1.apply(value));
codec2.encode(buf, getter2.apply(value));
codec3.encode(buf, getter3.apply(value));
}
};
}
interface Factory4<C1, C2, C3, C4, V> {
V create(C1 c1, C2 c2, C3 c3, C4 c4);
}
static <B, C1, C2, C3, C4, V> StreamCodec<B, V> composite(
StreamCodec<B, C1> codec1, Function<V, C1> getter1,
StreamCodec<B, C2> codec2, Function<V, C2> getter2,
StreamCodec<B, C3> codec3, Function<V, C3> getter3,
StreamCodec<B, C4> codec4, Function<V, C4> getter4,
Factory4<C1, C2, C3, C4, V> factory) {
return new StreamCodec<>() {
@Override
public V decode(B buf) {
C1 v1 = codec1.decode(buf);
C2 v2 = codec2.decode(buf);
C3 v3 = codec3.decode(buf);
C4 v4 = codec4.decode(buf);
return factory.create(v1, v2, v3, v4);
}
@Override
public void encode(B buf, V value) {
codec1.encode(buf, getter1.apply(value));
codec2.encode(buf, getter2.apply(value));
codec3.encode(buf, getter3.apply(value));
codec4.encode(buf, getter4.apply(value));
}
};
}
interface Factory5<C1, C2, C3, C4, C5, V> {
V create(C1 c1, C2 c2, C3 c3, C4 c4, C5 c5);
}
static <B, C1, C2, C3, C4, C5, V> StreamCodec<B, V> composite(
StreamCodec<B, C1> codec1, Function<V, C1> getter1,
StreamCodec<B, C2> codec2, Function<V, C2> getter2,
StreamCodec<B, C3> codec3, Function<V, C3> getter3,
StreamCodec<B, C4> codec4, Function<V, C4> getter4,
StreamCodec<B, C5> codec5, Function<V, C5> getter5,
Factory5<C1, C2, C3, C4, C5, V> factory) {
return new StreamCodec<>() {
@Override
public V decode(B buf) {
C1 v1 = codec1.decode(buf);
C2 v2 = codec2.decode(buf);
C3 v3 = codec3.decode(buf);
C4 v4 = codec4.decode(buf);
C5 v5 = codec5.decode(buf);
return factory.create(v1, v2, v3, v4, v5);
}
@Override
public void encode(B buf, V value) {
codec1.encode(buf, getter1.apply(value));
codec2.encode(buf, getter2.apply(value));
codec3.encode(buf, getter3.apply(value));
codec4.encode(buf, getter4.apply(value));
codec5.encode(buf, getter5.apply(value));
}
};
}
interface Factory6<C1, C2, C3, C4, C5, C6, V> {
V create(C1 c1, C2 c2, C3 c3, C4 c4, C5 c5, C6 c6);
}
static <B, C1, C2, C3, C4, C5, C6, V> StreamCodec<B, V> composite(
StreamCodec<B, C1> codec1, Function<V, C1> getter1,
StreamCodec<B, C2> codec2, Function<V, C2> getter2,
StreamCodec<B, C3> codec3, Function<V, C3> getter3,
StreamCodec<B, C4> codec4, Function<V, C4> getter4,
StreamCodec<B, C5> codec5, Function<V, C5> getter5,
StreamCodec<B, C6> codec6, Function<V, C6> getter6,
Factory6<C1, C2, C3, C4, C5, C6, V> factory) {
return new StreamCodec<>() {
@Override
public V decode(B buf) {
C1 v1 = codec1.decode(buf);
C2 v2 = codec2.decode(buf);
C3 v3 = codec3.decode(buf);
C4 v4 = codec4.decode(buf);
C5 v5 = codec5.decode(buf);
C6 v6 = codec6.decode(buf);
return factory.create(v1, v2, v3, v4, v5, v6);
}
@Override
public void encode(B buf, V value) {
codec1.encode(buf, getter1.apply(value));
codec2.encode(buf, getter2.apply(value));
codec3.encode(buf, getter3.apply(value));
codec4.encode(buf, getter4.apply(value));
codec5.encode(buf, getter5.apply(value));
codec6.encode(buf, getter6.apply(value));
}
};
}
}
@@ -9,17 +9,35 @@ import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/** Shared reflection metadata for presenting the Antarchy settings consistently. */
public final class AntarchyConfigCatalog {
public enum Section { MOBS, TOOLS, MISC }
public enum ValueType { BOOLEAN, INTEGER, DOUBLE, FLOAT, STRING }
public enum Section {
MOBS,
TOOLS,
MISC
}
public record Definition(String name, Section section, ValueType valueType,
Class<?> setterType, Method getter, Method setter) {}
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() {}
private AntarchyConfigCatalog() {
}
public static List<Definition> definitions() {
return DEFINITIONS;
@@ -38,8 +56,11 @@ public final class AntarchyConfigCatalog {
.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));
.collect(java.util.stream.Collectors.toMap(
Method::getName,
method -> method,
(first, ignored) -> first,
LinkedHashMap::new));
return Arrays.stream(AntarchySettings.class.getMethods())
.filter(AntarchyConfigCatalog::isPublicStatic)
@@ -54,18 +75,35 @@ public final class AntarchyConfigCatalog {
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;
if (getter == null) {
return null;
}
Class<?> setterType = setter.getParameterTypes()[0];
ValueType valueType = valueTypeFor(setterType, getter.getReturnType());
return valueType == null ? null : new Definition(name, sectionFor(name), valueType, setterType, getter, setter);
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;
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;
}
@@ -75,11 +113,20 @@ public final class AntarchyConfigCatalog {
}
private static String decapitalize(String value) {
return value.isEmpty() ? value : Character.toLowerCase(value.charAt(0)) + value.substring(1);
if (value.isEmpty()) {
return value;
}
return Character.toLowerCase(value.charAt(0)) + value.substring(1);
}
private static Section sectionFor(String name) {
return isMiscSetting(name) ? Section.MISC : isToolSetting(name) ? Section.TOOLS : Section.MOBS;
if (isMiscSetting(name)) {
return Section.MISC;
}
if (isToolSetting(name)) {
return Section.TOOLS;
}
return Section.MOBS;
}
private static boolean isMiscSetting(String name) {
@@ -87,32 +134,66 @@ public final class AntarchyConfigCatalog {
|| 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("elythiaPortalEnabled")
|| name.equals("thoraxisPortalEnabled")
|| name.equals("cavarynPortalEnabled")
|| name.startsWith("portalGunCanFireThrough")
|| name.startsWith("portalGunSeeThrough")
|| name.startsWith("portalGunFancy")
|| name.equals("portalGunCanPortalsResizeWhenCreated")
|| name.equals("portalGunCanPortalProjectilesChunkload")
|| name.equals("portalGunMaxShootDistance")
|| name.equals("portalGunMinFloorExitSpeed")
|| name.equals("portalGunMaxLiveViews")
|| name.equals("portalGunMaxExitSpeed")
|| name.startsWith("portalGunMoon")
|| name.equals("hushweedSporeLifetimeSeconds")
|| name.equals("elythiaFireflyParticlesEnabled")
|| name.equals("duplicatorTreeEnabled")
|| name.equals("glowVinesUnderLeaves")
|| name.equals("swingThroughGrassEnabled")
|| name.equals("fabricKeybindingConflictFixEnabled")
|| name.equals("experimentalSettingsPopupDisabled") || name.equals("unlockAllArchives")
|| name.equals("entitySpecificFireOverlayEnabled") || name.equals("dreamSandEnabled")
|| name.startsWith("dreamSand") || name.equals("ichorWitherEnabled")
|| name.startsWith("diamondMinecart") || name.startsWith("hoverboard");
|| name.equals("experimentalSettingsPopupDisabled")
|| name.equals("unlockAllArchives")
|| name.equals("entitySpecificFireOverlayEnabled")
|| 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")
return name.equals("sizeChangingRaysEnabled")
|| name.startsWith("sizeRay")
|| name.startsWith("temporalTuner")
|| 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.startsWith("gravityGun")
|| name.startsWith("critterCage")
|| name.equals("potentNyxiteInvertedDurationSeconds")
|| name.equals("corneaEarNightVisionSeconds") || name.startsWith("american")
|| name.startsWith("mogglesVision") || name.equals("ductTapeRepairPercentPerUse")
|| name.startsWith("fallenKingCrown");
|| name.equals("corneaEarNightVisionSeconds")
|| name.startsWith("american")
|| name.startsWith("mogglesVision")
|| name.equals("ductTapeRepairPercentPerUse")
|| name.startsWith("fallenKingCrown")
|| name.startsWith("royalArmor")
|| name.startsWith("royalGuardian")
|| name.startsWith("royalAssailant")
|| name.startsWith("royalWeapon");
}
}
@@ -17,6 +17,7 @@ public final class AntarchySettings {
private static boolean rainbowAntRequiresReagent = true;
private static boolean rainbowAntRightClickDimension = true;
private static boolean termiteRequiresReagent = false;
private static boolean termitesFearBlockLight = true;
private static boolean termiteRightClickDimension = true;
private static boolean antsStealFromChests = true;
private static boolean duplicatorTreeEnabled = true;
@@ -24,10 +25,13 @@ public final class AntarchySettings {
private static boolean swingThroughGrassEnabled = true;
private static boolean fabricKeybindingConflictFixEnabled = true;
private static boolean experimentalSettingsPopupDisabled = true;
private static boolean unlockAllArchives = true;
private static boolean sizeChangingRaysEnabled = true;
private static double sizeRayMinScale = 0.1D;
private static double sizeRayMaxScale = 10.0D;
private static double sizeRayDeltaPerHit = 0.25D;
private static double temporalTunerMinRate = 0.1D;
private static double temporalTunerMaxRate = 3.0D;
private static double shrinkingPotionDelta = 0.3D;
private static double growthPotionDelta = 0.5D;
private static double antDanceRadius = 5.0D;
@@ -152,7 +156,6 @@ public final class AntarchySettings {
private static double rpoLauncherExplosionDamage = 34.0D;
private static double rpoLauncherExplosionRadius = 14.0D;
private static double sizeRayCooldownSeconds = 1.0D;
private static boolean invertProjectilesFromInvertedPlayers = false;
private static boolean gravityGunEnabled = true;
private static boolean gravityGunBlocksEnabled = true;
private static boolean gravityGunEntitiesEnabled = true;
@@ -171,11 +174,7 @@ public final class AntarchySettings {
private static double americanBonusSaturation = 1.0D;
private static int americanRegenerationDurationTicks = 60;
private static int americanRegenerationAmplifier = 0;
private static boolean dreamSandEnabled = true;
private static boolean entitySpecificFireOverlayEnabled = true;
private static double dreamSandJumpVelocityMultiplier = 1.3D;
private static double dreamSandGravityMultiplier = 0.45D;
private static double dreamSandEffectDurationSeconds = 7.0D;
private static double dreamSandFallingBlockGravityMultiplier = 0.4D;
private static boolean ichorWitherEnabled = true;
private static int nightmareHelmetArmorValue = 4;
@@ -206,6 +205,11 @@ public final class AntarchySettings {
private static int royalGuardianBootsArmorValue = 5;
private static double royalGuardianArmorToughness = 4.0D;
private static double royalGuardianArmorKnockbackResistance = 0.15D;
private static double royalGuardianHelmetJudgmentBonus = 0.06D;
private static double royalGuardianChestplateJudgmentBonus = 0.08D;
private static double royalGuardianLeggingsJudgmentBonus = 0.07D;
private static double royalGuardianBootsJudgmentBonus = 0.05D;
private static int royalGuardianJudgmentDurationTicks = 160;
private static int royalAssailantHelmetArmorValue = 5;
private static int royalAssailantChestplateArmorValue = 10;
private static int royalAssailantLeggingsArmorValue = 8;
@@ -217,43 +221,69 @@ public final class AntarchySettings {
private static double royalAssailantLeggingsDoubleDamageChance = 0.07D;
private static double royalAssailantBootsDoubleDamageChance = 0.05D;
private static double royalGuardianSwordAttackDamage = 111.0D;
private static double giantFryingPanAttackDamage = 50.0D;
private static int giantFryingPanCookTimeTicks = 200;
private static double royalGuardianSwordAttackSpeed = -2.5D;
private static int royalGuardianSwordElementalCooldownTicks = 120;
private static double royalGuardianSwordFirePrimaryDamage = 22.0D;
private static double royalGuardianSwordFireSplashDamage = 14.0D;
private static double royalGuardianSwordFrostSpikeDamage = 9.0D;
private static double royalGuardianSwordStormPrimaryDamage = 16.0D;
private static double royalGuardianSwordStormJumpDamage = 14.0D;
private static int royalGuardianSwordStormMaxJumps = 4;
private static double royalGuardianSwordStormJumpRange = 9.0D;
private static double royalAssailantBattleAxeAttackDamage = 151.0D;
private static double royalAssailantBattleAxeAttackSpeed = -2.9D;
private static double royalWeaponAttackReachBonus = 1.0D;
private static double royalWeaponAttackReachBonus = 3.2D;
private static double royalWeaponAttackKnockbackBonus = 1.0D;
private static int royalWeaponDurability = 4608;
private static int royalWeaponEnchantability = 30;
private static int royalGuardianShieldDurability = 1008;
private static double royalGuardianMusterRange = 16.0D;
private static int royalGuardianMusterDurationTicks = 1200;
private static int royalGuardianMusterCooldownTicks = 500;
private static int royalGuardianMusterMobCap = 3;
private static double royalGuardianMusterDamageReduction = 0.40D;
private static double royalGuardianMusterAttackBonus = 0.50D;
private static double royalGuardianMusterSpeedBonus = 0.25D;
private static double royalGuardianMusterPassiveDamageFloor = 3.0D;
private static double royalGuardianBoundaryRadius = 9.0D;
private static int royalGuardianBoundaryDurationTicks = 120;
private static int royalGuardianBoundaryCooldownTicks = 700;
private static int royalGuardianBoundaryStrengthAmplifier = 1;
private static int royalGuardianBoundaryArrestTicks = 20;
private static int royalGuardianBoundaryDropTicks = 8;
private static int royalGuardianBoundaryProjectileCap = 64;
private static double royalBoltDamage = 6.0D;
private static int royalEggHatchChance = 3;
private static double princeHealth = 120.0D;
private static double princeHealth = 360.0D;
private static double princeAttackDamage = 14.0D;
private static double princeMovementSpeed = 0.28D;
private static double princeFlyingSpeed = 0.9D;
private static double princeArmor = 10.0D;
private static double princeKnockbackResistance = 0.6D;
private static double princeFollowRange = 48.0D;
private static double princessHealth = 110.0D;
private static double princessHealth = 330.0D;
private static double princessAttackDamage = 10.0D;
private static double princessMovementSpeed = 0.28D;
private static double princessFlyingSpeed = 0.95D;
private static double princessArmor = 8.0D;
private static double princessKnockbackResistance = 0.5D;
private static double princessFollowRange = 48.0D;
private static double kingHealth = 6000.0D;
private static double queenHealth = 6000.0D;
private static double kingHealth = 3400.0D;
private static double queenHealth = 4300.0D;
private static double kingAttackDamage = 50.0D;
private static double queenAttackDamage = 45.0D;
private static double royalBossSoundVolume = 4.5D;
private static double queenBlackHoleRadius = 14.0D;
private static int queenBlackHoleActiveTicks = 120;
private static double queenBlackHolePullStrength = 0.16D;
private static double queenFinalTimeFieldRadius = 20.0D;
private static double queenFinalTimeFieldRate = 0.05D;
private static int queenFinalTimeFieldDurationTicks = 45;
private static int queenFinalTimeFieldCooldownTicks = 80;
private static double kingFireballDamage = 32.0D;
private static int queenBlackHoleBlockSuctionCap = 160;
private static double axeBlackHoleRadius = 28.0D;
private static int axeBlackHoleActiveTicks = 120;
private static double axeBlackHolePullStrength = 0.16D;
private static int axeBlackHoleBlockSuctionCap = 160;
private static double kingFireballDamage = 48.0D;
private static double kingFireballRadius = 4.5D;
private static int kingFireballCooldownTicks = 150;
private static double kingIceballDamage = 18.0D;
@@ -263,14 +293,22 @@ public final class AntarchySettings {
private static int kingIceSpikeCooldownTicks = 240;
private static int kingElementalTerrainCap = 256;
private static double royalBossArmor = 20.0D;
private static double kingArmor = 30.0D;
private static double royalBossFollowRange = 128.0D;
private static double royalBossMovementSpeed = 0.22D;
private static double royalBossKnockbackResistance = 1.0D;
private static double royalBossStepHeight = 3.0D;
private static double royalBossMaxSingleHitDamage = 250.0D;
private static double royalBossBiteReach = 10.0D;
private static double royalBossBiteReach = 12.0D;
private static double royalBossBiteDamageMultiplier = 1.6D;
private static int royalBossBiteCooldownTicks = 45;
private static boolean royalBossMultiplayerScalingEnabled = true;
private static int royalBossScalingMaxPlayers = 4;
private static double royalBossHealthPerAdditionalPlayer = 0.35D;
private static double royalBossDamagePerAdditionalPlayer = 0.10D;
private static double royalBoundaryRadius = 192.0D;
private static double royalBoundaryWarningRadius = 160.0D;
private static int royalBoundaryGraceTicks = 60;
private static boolean basiliskPetrifyingGazeEnabled = true;
private static int basiliskSpawnMaxLightLevel = 5;
private static double basiliskHealth = 320.0D;
@@ -379,19 +417,19 @@ public final class AntarchySettings {
private static double bedBugAttackDamage = 4.0D;
private static double bedBugMovementSpeed = 0.24D;
private static double bedBugArmor = 2.0D;
private static double jumpyBugHealth = 100.0D;
private static double jumpyBugPounceDamage = 12.0D;
private static double jumpyBugCamouflageAlpha = 0.18D;
private static double springbugHealth = 100.0D;
private static double springbugPounceDamage = 12.0D;
private static double springbugCamouflageAlpha = 0.18D;
private static double brutalflyArmor = 10.0D;
private static double brutalflyArmorToughness = 5.0D;
private static double brutalflyKnockbackResistance = 0.7D;
private static double brutalflyMovementSpeed = 0.34D;
private static double brutalflyFlyingSpeed = 0.55D;
private static double cloudSharkHealth = 40.0D;
private static double cloudSharkAttackDamage = 5.0D;
private static double cloudSharkMovementSpeed = 0.34D;
private static double cloudSharkFlyingSpeed = 0.5D;
private static double cloudSharkKnockbackResistance = 0.2D;
private static double stratosharkHealth = 40.0D;
private static double stratosharkAttackDamage = 5.0D;
private static double stratosharkMovementSpeed = 0.34D;
private static double stratosharkFlyingSpeed = 0.5D;
private static double stratosharkKnockbackResistance = 0.2D;
private static double krakenMovementSpeed = 0.24D;
private static double krakenFlyingSpeed = 0.24D;
private static double krakenKnockbackResistance = 0.8D;
@@ -446,10 +484,10 @@ public final class AntarchySettings {
private static double molewormHealth = 8.0D;
private static double molewormAttackDamage = 1.0D;
private static double molewormMovementSpeed = 0.25D;
private static double triffidHealth = 160.0D;
private static double triffidAttackDamage = 10.0D;
private static double triffidGrabDamage = 15.0D;
private static int triffidSweepHitCooldownTicks = 10;
private static double flytrapHealth = 160.0D;
private static double flytrapAttackDamage = 10.0D;
private static double flytrapGrabDamage = 15.0D;
private static int flytrapSweepHitCooldownTicks = 10;
private static double molevoreHealth = 140.0D;
private static double molevoreAttackDamage = 10.0D;
private static double molevoreMovementSpeed = 0.27D;
@@ -463,9 +501,6 @@ public final class AntarchySettings {
private static double molevoreBreakRange = 1.8D;
private static double molevoreBreakVerticalRange = 2.2D;
private static double molevoreBreakHalfWidth = 1.1D;
private static double jumpyBugLatchDamage = 2.0D;
private static double waterCannonCooldownSeconds = 4.0D;
private static double toreterrorRangedWaterBombChance = 0.35D;
private static double nightmareHealth = 380.0D;
private static double nightmareAttackDamage = 16.0D;
private static double nightmareMovementSpeed = 0.28D;
@@ -509,22 +544,34 @@ public final class AntarchySettings {
private static boolean elythiaPortalEnabled = true;
private static boolean thoraxisPortalEnabled = true;
private static boolean cavarynPortalEnabled = true;
private static boolean portalGunCanFireThroughGlass = false;
private static boolean portalGunCanFireThroughLiquid = false;
private static boolean portalGunCanPortalsResizeWhenCreated = true;
private static boolean portalGunCanPortalProjectilesChunkload = true;
private static boolean portalGunSeeThroughPortals = true;
private static boolean portalGunFancyPortals = true;
private static int portalGunMaxShootDistance = 10000;
private static double portalGunMinFloorExitSpeed = 0.6D;
private static int portalGunMaxLiveViews = 8;
private static double portalGunMaxExitSpeed = 0.0D;
private static boolean portalGunMoonPullsBlocks = true;
private static int portalGunMoonPortalSeconds = 10;
private static int portalGunMoonBlockCap = 500;
private static boolean elythiaFireflyParticlesEnabled = true;
private static int bloodCrystalArmorShieldRechargeTicks = 600;
private static int bloodCrystalAppleShieldCount = 2;
private static int bloodCrystalAppleDurationTicks = 2400;
private static int bloodCrystalAppleShieldRechargeTicks = 600;
private static int bloodCrystalHardMaxShields = 10;
// Armor stats (iron defense, gold durability, 0 toughness by default)
private static int bloodCrystalHelmetDefense = 3;
private static int bloodCrystalChestplateDefense = 7;
private static int bloodCrystalLeggingsDefense = 6;
private static int bloodCrystalBootsDefense = 3;
private static double bloodCrystalArmorToughness = 2.0D;
private static int bloodCrystalHelmetDurability = 77;
private static int bloodCrystalChestplateDurability = 112;
private static int bloodCrystalLeggingsDurability = 105;
private static int bloodCrystalBootsDurability = 91;
private static int bloodCrystalHelmetDurability = 92;
private static int bloodCrystalChestplateDurability = 134;
private static int bloodCrystalLeggingsDurability = 126;
private static int bloodCrystalBootsDurability = 109;
private static double toreterrorHealth = 450.0D;
private static double toreterrorJumpAttackDamage = 14.0D;
private static double toreterrorJumpAttackKnockback = 2.5D;
@@ -535,19 +582,20 @@ public final class AntarchySettings {
private static int waterBombLifetimeTicks = 120;
private static double waterBombGravity = 0.12D;
private static double waterBombKnockback = 1.2D;
private static double creepingHorrorHealth = 15.0D;
private static double creepingHorrorAttackDamage = 6.0D;
private static double lurkingTerrorHealth = 15.0D;
private static double lurkingTerrorAttackDamage = 6.0D;
private static double manticoreHealth = 40.0D;
private static double manticoreAttackDamage = 7.0D;
private static double crawlingBlightHealth = 15.0D;
private static double crawlingBlightAttackDamage = 6.0D;
private static double skulkingFrightHealth = 15.0D;
private static double skulkingFrightAttackDamage = 6.0D;
private static double manticoreHealth = 60.0D;
private static double manticoreAttackDamage = 11.0D;
private static int manticoreStingPoisonTicks = 100;
private static int queenManticoreCap = 15;
private static int queenManticoreSummonCooldownTicks = 600;
private static int queenManticoreSummonCount = 3;
private static int queenManticoreSummonCooldownTicks = 800;
private static int queenManticoreSummonCount = 2;
private static double queenManticoreSummonRange = 24.0D;
private static double queenBeamDamage = 45.0D;
private static double queenBeamRange = 100.0D;
private static double queenBeamMinimumRange = 22.0D;
private static int queenBeamDurationTicks = 30;
private static int queenBeamWindupTicks = 20;
private static int queenBeamTravelTicks = 12;
@@ -555,8 +603,13 @@ public final class AntarchySettings {
private static double queenBeamTracking = 0.1D;
private static double queenBeamTerrainRadius = 5.0D;
private static int queenBeamTerrainCap = 192;
private static int queenBeamPhaseOneCap = 1;
private static int queenBeamPhaseTwoCap = 2;
private static int queenBeamPhaseThreeCap = 3;
private static double queenBeamPhaseThreeMaxVolleyChance = 0.2D;
private static double kingBeamDamage = 50.0D;
private static double kingBeamRange = 100.0D;
private static double kingBeamMinimumRange = 32.0D;
private static int kingBeamDurationTicks = 30;
private static int kingBeamWindupTicks = 20;
private static int kingBeamTravelTicks = 12;
@@ -564,7 +617,7 @@ public final class AntarchySettings {
private static double kingBeamTracking = 0.1D;
private static double kingBeamTerrainRadius = 5.0D;
private static int kingBeamTerrainCap = 192;
private static int royalDecreeCooldownTicks = 1200;
private static int royalDecreeCooldownTicks = 700;
private static int kingRoyalMusterCooldownTicks = 500;
private static int kingRoyalMusterWindupTicks = 30;
private static int kingRoyalMusterDurationTicks = 600;
@@ -588,7 +641,7 @@ public final class AntarchySettings {
private static double glimmerHealth = 20.0D;
private static double lucidArmorToughness = 2.0D;
private static double triffidArmorToughness = 3.0D;
private static double flytrapArmorToughness = 3.0D;
private static double basiliskArmorToughness = 5.0D;
private static double nightmareMobArmorToughness = 5.0D;
private static double alphaMantisArmorToughness = 4.0D;
@@ -663,6 +716,10 @@ public final class AntarchySettings {
return termiteRequiresReagent;
}
public static boolean termitesFearBlockLight() {
return termitesFearBlockLight;
}
public static boolean antsStealFromChests() {
return antsStealFromChests;
}
@@ -687,6 +744,10 @@ public final class AntarchySettings {
return experimentalSettingsPopupDisabled;
}
public static boolean unlockAllArchives() {
return unlockAllArchives;
}
public static boolean sizeChangingRaysEnabled() {
return sizeChangingRaysEnabled;
}
@@ -703,6 +764,14 @@ public final class AntarchySettings {
return sizeRayDeltaPerHit;
}
public static double temporalTunerMinRate() {
return temporalTunerMinRate;
}
public static double temporalTunerMaxRate() {
return temporalTunerMaxRate;
}
public static double shrinkingPotionDelta() {
return shrinkingPotionDelta;
}
@@ -1140,10 +1209,6 @@ public final class AntarchySettings {
return sizeRayCooldownSeconds;
}
public static boolean invertProjectilesFromInvertedPlayers() {
return invertProjectilesFromInvertedPlayers;
}
public static boolean gravityGunEnabled() {
return gravityGunEnabled;
}
@@ -1240,11 +1305,76 @@ public final class AntarchySettings {
cavarynPortalEnabled = value;
}
public static void setPortalGunCanFireThroughGlass(boolean value) {
portalGunCanFireThroughGlass = value;
}
public static void setPortalGunCanFireThroughLiquid(boolean value) {
portalGunCanFireThroughLiquid = value;
}
public static void setPortalGunCanPortalsResizeWhenCreated(boolean value) {
portalGunCanPortalsResizeWhenCreated = value;
}
public static void setPortalGunCanPortalProjectilesChunkload(boolean value) {
portalGunCanPortalProjectilesChunkload = value;
}
public static void setPortalGunSeeThroughPortals(boolean value) {
portalGunSeeThroughPortals = value;
}
public static void setPortalGunFancyPortals(boolean value) {
portalGunFancyPortals = value;
}
public static void setPortalGunMaxShootDistance(int value) {
portalGunMaxShootDistance = Math.max(1, value);
}
public static void setPortalGunMaxLiveViews(int value) {
portalGunMaxLiveViews = Math.max(0, Math.min(32, value));
}
public static void setPortalGunMinFloorExitSpeed(double value) {
portalGunMinFloorExitSpeed = Math.max(0.0D, Math.min(4.0D, value));
}
public static void setPortalGunMoonPullsBlocks(boolean value) {
portalGunMoonPullsBlocks = value;
}
public static void setPortalGunMoonPortalSeconds(int value) {
portalGunMoonPortalSeconds = Math.max(1, Math.min(120, value));
}
public static void setPortalGunMoonBlockCap(int value) {
portalGunMoonBlockCap = Math.max(0, Math.min(2000, value));
}
public static void setPortalGunMaxExitSpeed(double value) {
portalGunMaxExitSpeed = Math.max(0.0D, Math.min(100.0D, value));
}
public static boolean permanentPortalsEnabled() { return permanentPortalsEnabled; }
public static boolean permanentPortalsFlintAndSteelEnabled() { return permanentPortalsFlintAndSteelEnabled; }
public static boolean elythiaPortalEnabled() { return elythiaPortalEnabled; }
public static boolean thoraxisPortalEnabled() { return thoraxisPortalEnabled; }
public static boolean cavarynPortalEnabled() { return cavarynPortalEnabled; }
public static boolean portalGunCanFireThroughGlass() { return portalGunCanFireThroughGlass; }
public static boolean portalGunCanFireThroughLiquid() { return portalGunCanFireThroughLiquid; }
public static boolean portalGunCanPortalsResizeWhenCreated() { return portalGunCanPortalsResizeWhenCreated; }
public static boolean portalGunCanPortalProjectilesChunkload() { return portalGunCanPortalProjectilesChunkload; }
public static boolean portalGunSeeThroughPortals() { return portalGunSeeThroughPortals; }
public static boolean portalGunFancyPortals() { return portalGunFancyPortals; }
public static int portalGunMaxShootDistance() { return portalGunMaxShootDistance; }
public static double portalGunMinFloorExitSpeed() { return portalGunMinFloorExitSpeed; }
public static int portalGunMaxLiveViews() { return portalGunMaxLiveViews; }
public static double portalGunMaxExitSpeed() { return portalGunMaxExitSpeed; }
public static boolean portalGunMoonPullsBlocks() { return portalGunMoonPullsBlocks; }
public static int portalGunMoonPortalSeconds() { return portalGunMoonPortalSeconds; }
public static int portalGunMoonBlockCap() { return portalGunMoonBlockCap; }
public static void setBrownAntRequiresReagent(boolean value) {
brownAntRequiresReagent = value;
@@ -1262,6 +1392,10 @@ public final class AntarchySettings {
termiteRequiresReagent = value;
}
public static void setTermitesFearBlockLight(boolean value) {
termitesFearBlockLight = value;
}
public static void setAntsStealFromChests(boolean value) {
antsStealFromChests = value;
}
@@ -1286,6 +1420,10 @@ public final class AntarchySettings {
experimentalSettingsPopupDisabled = value;
}
public static void setUnlockAllArchives(boolean value) {
unlockAllArchives = value;
}
public static void setSizeChangingRaysEnabled(boolean value) {
sizeChangingRaysEnabled = value;
}
@@ -1302,6 +1440,14 @@ public final class AntarchySettings {
sizeRayDeltaPerHit = value;
}
public static void setTemporalTunerMinRate(double value) {
temporalTunerMinRate = value;
}
public static void setTemporalTunerMaxRate(double value) {
temporalTunerMaxRate = value;
}
public static void setShrinkingPotionDelta(double value) {
shrinkingPotionDelta = value;
}
@@ -1662,10 +1808,6 @@ public final class AntarchySettings {
sizeRayCooldownSeconds = value;
}
public static void setInvertProjectilesFromInvertedPlayers(boolean value) {
invertProjectilesFromInvertedPlayers = value;
}
public static void setGravityGunEnabled(boolean value) {
gravityGunEnabled = value;
}
@@ -1770,46 +1912,14 @@ public final class AntarchySettings {
americanRegenerationAmplifier = value;
}
public static boolean dreamSandEnabled() {
return dreamSandEnabled;
}
public static boolean entitySpecificFireOverlayEnabled() {
return entitySpecificFireOverlayEnabled;
}
public static void setDreamSandEnabled(boolean value) {
dreamSandEnabled = value;
}
public static void setEntitySpecificFireOverlayEnabled(boolean value) {
entitySpecificFireOverlayEnabled = value;
}
public static double dreamSandJumpVelocityMultiplier() {
return dreamSandJumpVelocityMultiplier;
}
public static void setDreamSandJumpVelocityMultiplier(double value) {
dreamSandJumpVelocityMultiplier = value;
}
public static double dreamSandGravityMultiplier() {
return dreamSandGravityMultiplier;
}
public static void setDreamSandGravityMultiplier(double value) {
dreamSandGravityMultiplier = value;
}
public static double dreamSandEffectDurationSeconds() {
return dreamSandEffectDurationSeconds;
}
public static void setDreamSandEffectDurationSeconds(double value) {
dreamSandEffectDurationSeconds = value;
}
public static double dreamSandFallingBlockGravityMultiplier() {
return dreamSandFallingBlockGravityMultiplier;
}
@@ -1895,6 +2005,17 @@ public final class AntarchySettings {
public static double royalGuardianArmorKnockbackResistance() { return royalGuardianArmorKnockbackResistance; }
public static void setRoyalGuardianArmorKnockbackResistance(double v) { royalGuardianArmorKnockbackResistance = v; }
public static double royalGuardianHelmetJudgmentBonus() { return royalGuardianHelmetJudgmentBonus; }
public static void setRoyalGuardianHelmetJudgmentBonus(double v) { royalGuardianHelmetJudgmentBonus = v; }
public static double royalGuardianChestplateJudgmentBonus() { return royalGuardianChestplateJudgmentBonus; }
public static void setRoyalGuardianChestplateJudgmentBonus(double v) { royalGuardianChestplateJudgmentBonus = v; }
public static double royalGuardianLeggingsJudgmentBonus() { return royalGuardianLeggingsJudgmentBonus; }
public static void setRoyalGuardianLeggingsJudgmentBonus(double v) { royalGuardianLeggingsJudgmentBonus = v; }
public static double royalGuardianBootsJudgmentBonus() { return royalGuardianBootsJudgmentBonus; }
public static void setRoyalGuardianBootsJudgmentBonus(double v) { royalGuardianBootsJudgmentBonus = v; }
public static int royalGuardianJudgmentDurationTicks() { return royalGuardianJudgmentDurationTicks; }
public static void setRoyalGuardianJudgmentDurationTicks(int v) { royalGuardianJudgmentDurationTicks = v; }
public static int royalAssailantHelmetArmorValue() { return royalAssailantHelmetArmorValue; }
public static void setRoyalAssailantHelmetArmorValue(int v) { royalAssailantHelmetArmorValue = v; }
@@ -1927,10 +2048,31 @@ public final class AntarchySettings {
public static double royalGuardianSwordAttackDamage() { return royalGuardianSwordAttackDamage; }
public static void setRoyalGuardianSwordAttackDamage(double v) { royalGuardianSwordAttackDamage = v; }
public static double giantFryingPanAttackDamage() { return giantFryingPanAttackDamage; }
public static void setGiantFryingPanAttackDamage(double v) { giantFryingPanAttackDamage = v; }
public static int giantFryingPanCookTimeTicks() { return giantFryingPanCookTimeTicks; }
public static void setGiantFryingPanCookTimeTicks(int v) { giantFryingPanCookTimeTicks = v; }
public static double royalGuardianSwordAttackSpeed() { return royalGuardianSwordAttackSpeed; }
public static void setRoyalGuardianSwordAttackSpeed(double v) { royalGuardianSwordAttackSpeed = v; }
public static int royalGuardianSwordElementalCooldownTicks() { return royalGuardianSwordElementalCooldownTicks; }
public static void setRoyalGuardianSwordElementalCooldownTicks(int v) { royalGuardianSwordElementalCooldownTicks = v; }
public static double royalGuardianSwordFirePrimaryDamage() { return royalGuardianSwordFirePrimaryDamage; }
public static void setRoyalGuardianSwordFirePrimaryDamage(double v) { royalGuardianSwordFirePrimaryDamage = v; }
public static double royalGuardianSwordFireSplashDamage() { return royalGuardianSwordFireSplashDamage; }
public static void setRoyalGuardianSwordFireSplashDamage(double v) { royalGuardianSwordFireSplashDamage = v; }
public static double royalGuardianSwordFrostSpikeDamage() { return royalGuardianSwordFrostSpikeDamage; }
public static void setRoyalGuardianSwordFrostSpikeDamage(double v) { royalGuardianSwordFrostSpikeDamage = v; }
public static double royalGuardianSwordStormPrimaryDamage() { return royalGuardianSwordStormPrimaryDamage; }
public static void setRoyalGuardianSwordStormPrimaryDamage(double v) { royalGuardianSwordStormPrimaryDamage = v; }
public static double royalGuardianSwordStormJumpDamage() { return royalGuardianSwordStormJumpDamage; }
public static void setRoyalGuardianSwordStormJumpDamage(double v) { royalGuardianSwordStormJumpDamage = v; }
public static int royalGuardianSwordStormMaxJumps() { return royalGuardianSwordStormMaxJumps; }
public static void setRoyalGuardianSwordStormMaxJumps(int v) { royalGuardianSwordStormMaxJumps = v; }
public static double royalGuardianSwordStormJumpRange() { return royalGuardianSwordStormJumpRange; }
public static void setRoyalGuardianSwordStormJumpRange(double v) { royalGuardianSwordStormJumpRange = v; }
public static double royalAssailantBattleAxeAttackDamage() { return royalAssailantBattleAxeAttackDamage; }
public static void setRoyalAssailantBattleAxeAttackDamage(double v) { royalAssailantBattleAxeAttackDamage = v; }
@@ -1952,6 +2094,37 @@ public final class AntarchySettings {
public static int royalGuardianShieldDurability() { return royalGuardianShieldDurability; }
public static void setRoyalGuardianShieldDurability(int v) { royalGuardianShieldDurability = v; }
public static double royalGuardianMusterRange() { return royalGuardianMusterRange; }
public static void setRoyalGuardianMusterRange(double v) { royalGuardianMusterRange = v; }
public static int royalGuardianMusterDurationTicks() { return royalGuardianMusterDurationTicks; }
public static void setRoyalGuardianMusterDurationTicks(int v) { royalGuardianMusterDurationTicks = v; }
public static int royalGuardianMusterCooldownTicks() { return royalGuardianMusterCooldownTicks; }
public static void setRoyalGuardianMusterCooldownTicks(int v) { royalGuardianMusterCooldownTicks = v; }
public static int royalGuardianMusterMobCap() { return royalGuardianMusterMobCap; }
public static void setRoyalGuardianMusterMobCap(int v) { royalGuardianMusterMobCap = v; }
public static double royalGuardianMusterDamageReduction() { return royalGuardianMusterDamageReduction; }
public static void setRoyalGuardianMusterDamageReduction(double v) { royalGuardianMusterDamageReduction = v; }
public static double royalGuardianMusterAttackBonus() { return royalGuardianMusterAttackBonus; }
public static void setRoyalGuardianMusterAttackBonus(double v) { royalGuardianMusterAttackBonus = v; }
public static double royalGuardianMusterSpeedBonus() { return royalGuardianMusterSpeedBonus; }
public static void setRoyalGuardianMusterSpeedBonus(double v) { royalGuardianMusterSpeedBonus = v; }
public static double royalGuardianMusterPassiveDamageFloor() { return royalGuardianMusterPassiveDamageFloor; }
public static void setRoyalGuardianMusterPassiveDamageFloor(double v) { royalGuardianMusterPassiveDamageFloor = v; }
public static double royalGuardianBoundaryRadius() { return royalGuardianBoundaryRadius; }
public static void setRoyalGuardianBoundaryRadius(double v) { royalGuardianBoundaryRadius = v; }
public static int royalGuardianBoundaryDurationTicks() { return royalGuardianBoundaryDurationTicks; }
public static void setRoyalGuardianBoundaryDurationTicks(int v) { royalGuardianBoundaryDurationTicks = v; }
public static int royalGuardianBoundaryCooldownTicks() { return royalGuardianBoundaryCooldownTicks; }
public static void setRoyalGuardianBoundaryCooldownTicks(int v) { royalGuardianBoundaryCooldownTicks = v; }
public static int royalGuardianBoundaryStrengthAmplifier() { return royalGuardianBoundaryStrengthAmplifier; }
public static void setRoyalGuardianBoundaryStrengthAmplifier(int v) { royalGuardianBoundaryStrengthAmplifier = v; }
public static int royalGuardianBoundaryArrestTicks() { return royalGuardianBoundaryArrestTicks; }
public static void setRoyalGuardianBoundaryArrestTicks(int v) { royalGuardianBoundaryArrestTicks = v; }
public static int royalGuardianBoundaryDropTicks() { return royalGuardianBoundaryDropTicks; }
public static void setRoyalGuardianBoundaryDropTicks(int v) { royalGuardianBoundaryDropTicks = v; }
public static int royalGuardianBoundaryProjectileCap() { return royalGuardianBoundaryProjectileCap; }
public static void setRoyalGuardianBoundaryProjectileCap(int v) { royalGuardianBoundaryProjectileCap = v; }
public static double royalBoltDamage() { return royalBoltDamage; }
public static void setRoyalBoltDamage(double v) { royalBoltDamage = v; }
@@ -2007,14 +2180,16 @@ public final class AntarchySettings {
public static void setQueenBlackHoleActiveTicks(int v) { queenBlackHoleActiveTicks = v; }
public static double queenBlackHolePullStrength() { return queenBlackHolePullStrength; }
public static void setQueenBlackHolePullStrength(double v) { queenBlackHolePullStrength = v; }
public static double queenFinalTimeFieldRadius() { return queenFinalTimeFieldRadius; }
public static void setQueenFinalTimeFieldRadius(double v) { queenFinalTimeFieldRadius = v; }
public static double queenFinalTimeFieldRate() { return queenFinalTimeFieldRate; }
public static void setQueenFinalTimeFieldRate(double v) { queenFinalTimeFieldRate = v; }
public static int queenFinalTimeFieldDurationTicks() { return queenFinalTimeFieldDurationTicks; }
public static void setQueenFinalTimeFieldDurationTicks(int v) { queenFinalTimeFieldDurationTicks = v; }
public static int queenFinalTimeFieldCooldownTicks() { return queenFinalTimeFieldCooldownTicks; }
public static void setQueenFinalTimeFieldCooldownTicks(int v) { queenFinalTimeFieldCooldownTicks = v; }
public static int queenBlackHoleBlockSuctionCap() { return queenBlackHoleBlockSuctionCap; }
public static void setQueenBlackHoleBlockSuctionCap(int v) { queenBlackHoleBlockSuctionCap = v; }
public static double axeBlackHoleRadius() { return axeBlackHoleRadius; }
public static void setAxeBlackHoleRadius(double v) { axeBlackHoleRadius = v; }
public static int axeBlackHoleActiveTicks() { return axeBlackHoleActiveTicks; }
public static void setAxeBlackHoleActiveTicks(int v) { axeBlackHoleActiveTicks = v; }
public static double axeBlackHolePullStrength() { return axeBlackHolePullStrength; }
public static void setAxeBlackHolePullStrength(double v) { axeBlackHolePullStrength = v; }
public static int axeBlackHoleBlockSuctionCap() { return axeBlackHoleBlockSuctionCap; }
public static void setAxeBlackHoleBlockSuctionCap(int v) { axeBlackHoleBlockSuctionCap = v; }
public static double kingFireballDamage() { return kingFireballDamage; }
public static void setKingFireballDamage(double v) { kingFireballDamage = v; }
public static double kingFireballRadius() { return kingFireballRadius; }
@@ -2036,6 +2211,8 @@ public final class AntarchySettings {
public static double royalBossArmor() { return royalBossArmor; }
public static void setRoyalBossArmor(double v) { royalBossArmor = v; }
public static double kingArmor() { return kingArmor; }
public static void setKingArmor(double v) { kingArmor = v; }
public static double royalBossFollowRange() { return royalBossFollowRange; }
public static void setRoyalBossFollowRange(double v) { royalBossFollowRange = v; }
@@ -2060,6 +2237,20 @@ public final class AntarchySettings {
public static int royalBossBiteCooldownTicks() { return royalBossBiteCooldownTicks; }
public static void setRoyalBossBiteCooldownTicks(int v) { royalBossBiteCooldownTicks = v; }
public static boolean royalBossMultiplayerScalingEnabled() { return royalBossMultiplayerScalingEnabled; }
public static void setRoyalBossMultiplayerScalingEnabled(boolean v) { royalBossMultiplayerScalingEnabled = v; }
public static int royalBossScalingMaxPlayers() { return royalBossScalingMaxPlayers; }
public static void setRoyalBossScalingMaxPlayers(int v) { royalBossScalingMaxPlayers = v; }
public static double royalBossHealthPerAdditionalPlayer() { return royalBossHealthPerAdditionalPlayer; }
public static void setRoyalBossHealthPerAdditionalPlayer(double v) { royalBossHealthPerAdditionalPlayer = v; }
public static double royalBossDamagePerAdditionalPlayer() { return royalBossDamagePerAdditionalPlayer; }
public static void setRoyalBossDamagePerAdditionalPlayer(double v) { royalBossDamagePerAdditionalPlayer = v; }
public static double royalBoundaryRadius() { return royalBoundaryRadius; }
public static void setRoyalBoundaryRadius(double v) { royalBoundaryRadius = v; }
public static double royalBoundaryWarningRadius() { return royalBoundaryWarningRadius; }
public static void setRoyalBoundaryWarningRadius(double v) { royalBoundaryWarningRadius = v; }
public static int royalBoundaryGraceTicks() { return royalBoundaryGraceTicks; }
public static void setRoyalBoundaryGraceTicks(int v) { royalBoundaryGraceTicks = v; }
public static double nightmareSwordBaseDamage() { return nightmareSwordBaseDamage; }
public static void setNightmareSwordBaseDamage(double v) { nightmareSwordBaseDamage = v; }
@@ -2307,7 +2498,6 @@ public final class AntarchySettings {
public static int reverieDuplicationCooldownTicks() { return reverieDuplicationCooldownTicks; }
public static void setReverieDuplicationCooldownTicks(int v){ reverieDuplicationCooldownTicks = v; }
// Hardcoded AI tuning — getters only, no config wiring
public static double reveriePreferredFollowMinDistance() { return reveriePreferredFollowMinDistance; }
public static double reveriePreferredFollowMaxDistance() { return reveriePreferredFollowMaxDistance; }
public static double reverieCatchUpDistance() { return reverieCatchUpDistance; }
@@ -2454,14 +2644,14 @@ public final class AntarchySettings {
public static double bedBugArmor() { return bedBugArmor; }
public static void setBedBugArmor(double value) { bedBugArmor = value; }
public static double jumpyBugHealth() { return jumpyBugHealth; }
public static void setJumpyBugHealth(double value) { jumpyBugHealth = value; }
public static double springbugHealth() { return springbugHealth; }
public static void setSpringbugHealth(double value) { springbugHealth = value; }
public static double jumpyBugPounceDamage() { return jumpyBugPounceDamage; }
public static void setJumpyBugPounceDamage(double value) { jumpyBugPounceDamage = value; }
public static double springbugPounceDamage() { return springbugPounceDamage; }
public static void setSpringbugPounceDamage(double value) { springbugPounceDamage = value; }
public static double jumpyBugCamouflageAlpha() { return jumpyBugCamouflageAlpha; }
public static void setJumpyBugCamouflageAlpha(double value) { jumpyBugCamouflageAlpha = value; }
public static double springbugCamouflageAlpha() { return springbugCamouflageAlpha; }
public static void setSpringbugCamouflageAlpha(double value) { springbugCamouflageAlpha = value; }
public static double brutalflyArmor() { return brutalflyArmor; }
public static void setBrutalflyArmor(double value) { brutalflyArmor = value; }
@@ -2478,20 +2668,20 @@ public final class AntarchySettings {
public static double brutalflyFlyingSpeed() { return brutalflyFlyingSpeed; }
public static void setBrutalflyFlyingSpeed(double value) { brutalflyFlyingSpeed = value; }
public static double cloudSharkHealth() { return cloudSharkHealth; }
public static void setCloudSharkHealth(double value) { cloudSharkHealth = value; }
public static double stratosharkHealth() { return stratosharkHealth; }
public static void setStratosharkHealth(double value) { stratosharkHealth = value; }
public static double cloudSharkAttackDamage() { return cloudSharkAttackDamage; }
public static void setCloudSharkAttackDamage(double value) { cloudSharkAttackDamage = value; }
public static double stratosharkAttackDamage() { return stratosharkAttackDamage; }
public static void setStratosharkAttackDamage(double value) { stratosharkAttackDamage = value; }
public static double cloudSharkMovementSpeed() { return cloudSharkMovementSpeed; }
public static void setCloudSharkMovementSpeed(double value) { cloudSharkMovementSpeed = value; }
public static double stratosharkMovementSpeed() { return stratosharkMovementSpeed; }
public static void setStratosharkMovementSpeed(double value) { stratosharkMovementSpeed = value; }
public static double cloudSharkFlyingSpeed() { return cloudSharkFlyingSpeed; }
public static void setCloudSharkFlyingSpeed(double value) { cloudSharkFlyingSpeed = value; }
public static double stratosharkFlyingSpeed() { return stratosharkFlyingSpeed; }
public static void setStratosharkFlyingSpeed(double value) { stratosharkFlyingSpeed = value; }
public static double cloudSharkKnockbackResistance() { return cloudSharkKnockbackResistance; }
public static void setCloudSharkKnockbackResistance(double value) { cloudSharkKnockbackResistance = value; }
public static double stratosharkKnockbackResistance() { return stratosharkKnockbackResistance; }
public static void setStratosharkKnockbackResistance(double value) { stratosharkKnockbackResistance = value; }
public static double krakenMovementSpeed() { return krakenMovementSpeed; }
public static void setKrakenMovementSpeed(double value) { krakenMovementSpeed = value; }
@@ -2655,17 +2845,17 @@ public final class AntarchySettings {
public static double molewormMovementSpeed() { return molewormMovementSpeed; }
public static void setMolewormMovementSpeed(double value) { molewormMovementSpeed = value; }
public static double triffidHealth() { return triffidHealth; }
public static void setTriffidHealth(double value) { triffidHealth = value; }
public static double flytrapHealth() { return flytrapHealth; }
public static void setFlytrapHealth(double value) { flytrapHealth = value; }
public static double triffidAttackDamage() { return triffidAttackDamage; }
public static void setTriffidAttackDamage(double value) { triffidAttackDamage = value; }
public static double flytrapAttackDamage() { return flytrapAttackDamage; }
public static void setFlytrapAttackDamage(double value) { flytrapAttackDamage = value; }
public static double triffidGrabDamage() { return triffidGrabDamage; }
public static void setTriffidGrabDamage(double value) { triffidGrabDamage = value; }
public static double flytrapGrabDamage() { return flytrapGrabDamage; }
public static void setFlytrapGrabDamage(double value) { flytrapGrabDamage = value; }
public static int triffidSweepHitCooldownTicks() { return triffidSweepHitCooldownTicks; }
public static void setTriffidSweepHitCooldownTicks(int value) { triffidSweepHitCooldownTicks = value; }
public static int flytrapSweepHitCooldownTicks() { return flytrapSweepHitCooldownTicks; }
public static void setFlytrapSweepHitCooldownTicks(int value) { flytrapSweepHitCooldownTicks = value; }
public static double molevoreHealth() { return molevoreHealth; }
public static void setMolevoreHealth(double value) { molevoreHealth = value; }
@@ -2673,21 +2863,6 @@ public final class AntarchySettings {
public static double molevoreAttackDamage() { return molevoreAttackDamage; }
public static void setMolevoreAttackDamage(double value) { molevoreAttackDamage = value; }
public static double molevoreMovementSpeed() { return molevoreMovementSpeed; }
public static void setMolevoreMovementSpeed(double value) { molevoreMovementSpeed = value; }
public static double molevoreArmor() { return molevoreArmor; }
public static void setMolevoreArmor(double value) { molevoreArmor = value; }
public static double molevoreKnockbackResistance() { return molevoreKnockbackResistance; }
public static void setMolevoreKnockbackResistance(double value) { molevoreKnockbackResistance = value; }
public static double molevoreFollowRange() { return molevoreFollowRange; }
public static void setMolevoreFollowRange(double value) { molevoreFollowRange = value; }
public static int molevoreXpReward() { return molevoreXpReward; }
public static void setMolevoreXpReward(int value) { molevoreXpReward = value; }
public static int molevoreSpinTicks() { return molevoreSpinTicks; }
public static void setMolevoreSpinTicks(int value) { molevoreSpinTicks = value; }
@@ -2839,14 +3014,14 @@ public final class AntarchySettings {
public static double waterBombKnockback() { return waterBombKnockback; }
public static void setWaterBombKnockback(double v) { waterBombKnockback = v; }
public static double creepingHorrorHealth() { return creepingHorrorHealth; }
public static void setCreepingHorrorHealth(double v) { creepingHorrorHealth = v; }
public static double creepingHorrorAttackDamage() { return creepingHorrorAttackDamage; }
public static void setCreepingHorrorAttackDamage(double v) { creepingHorrorAttackDamage = v; }
public static double lurkingTerrorHealth() { return lurkingTerrorHealth; }
public static void setLurkingTerrorHealth(double v) { lurkingTerrorHealth = v; }
public static double lurkingTerrorAttackDamage() { return lurkingTerrorAttackDamage; }
public static void setLurkingTerrorAttackDamage(double v) { lurkingTerrorAttackDamage = v; }
public static double crawlingBlightHealth() { return crawlingBlightHealth; }
public static void setCrawlingBlightHealth(double v) { crawlingBlightHealth = v; }
public static double crawlingBlightAttackDamage() { return crawlingBlightAttackDamage; }
public static void setCrawlingBlightAttackDamage(double v) { crawlingBlightAttackDamage = v; }
public static double skulkingFrightHealth() { return skulkingFrightHealth; }
public static void setSkulkingFrightHealth(double v) { skulkingFrightHealth = v; }
public static double skulkingFrightAttackDamage() { return skulkingFrightAttackDamage; }
public static void setSkulkingFrightAttackDamage(double v) { skulkingFrightAttackDamage = v; }
public static double manticoreHealth() { return manticoreHealth; }
public static void setManticoreHealth(double v) { manticoreHealth = v; }
public static double manticoreAttackDamage() { return manticoreAttackDamage; }
@@ -2865,6 +3040,8 @@ public final class AntarchySettings {
public static void setQueenBeamDamage(double v) { queenBeamDamage = v; }
public static double queenBeamRange() { return queenBeamRange; }
public static void setQueenBeamRange(double v) { queenBeamRange = v; }
public static double queenBeamMinimumRange() { return queenBeamMinimumRange; }
public static void setQueenBeamMinimumRange(double v) { queenBeamMinimumRange = v; }
public static int queenBeamDurationTicks() { return queenBeamDurationTicks; }
public static void setQueenBeamDurationTicks(int v) { queenBeamDurationTicks = v; }
public static int queenBeamWindupTicks() { return queenBeamWindupTicks; }
@@ -2879,10 +3056,20 @@ public final class AntarchySettings {
public static void setQueenBeamTerrainRadius(double v) { queenBeamTerrainRadius = v; }
public static int queenBeamTerrainCap() { return queenBeamTerrainCap; }
public static void setQueenBeamTerrainCap(int v) { queenBeamTerrainCap = v; }
public static int queenBeamPhaseOneCap() { return queenBeamPhaseOneCap; }
public static void setQueenBeamPhaseOneCap(int v) { queenBeamPhaseOneCap = v; }
public static int queenBeamPhaseTwoCap() { return queenBeamPhaseTwoCap; }
public static void setQueenBeamPhaseTwoCap(int v) { queenBeamPhaseTwoCap = v; }
public static int queenBeamPhaseThreeCap() { return queenBeamPhaseThreeCap; }
public static void setQueenBeamPhaseThreeCap(int v) { queenBeamPhaseThreeCap = v; }
public static double queenBeamPhaseThreeMaxVolleyChance() { return queenBeamPhaseThreeMaxVolleyChance; }
public static void setQueenBeamPhaseThreeMaxVolleyChance(double v) { queenBeamPhaseThreeMaxVolleyChance = v; }
public static double kingBeamDamage() { return kingBeamDamage; }
public static void setKingBeamDamage(double v) { kingBeamDamage = v; }
public static double kingBeamRange() { return kingBeamRange; }
public static void setKingBeamRange(double v) { kingBeamRange = v; }
public static double kingBeamMinimumRange() { return kingBeamMinimumRange; }
public static void setKingBeamMinimumRange(double v) { kingBeamMinimumRange = v; }
public static int kingBeamDurationTicks() { return kingBeamDurationTicks; }
public static void setKingBeamDurationTicks(int v) { kingBeamDurationTicks = v; }
public static int kingBeamWindupTicks() { return kingBeamWindupTicks; }
@@ -2943,7 +3130,7 @@ public final class AntarchySettings {
public static void setGlimmerHealth(double v) { glimmerHealth = v; }
public static double lucidArmorToughness() { return lucidArmorToughness; }
public static double triffidArmorToughness() { return triffidArmorToughness; }
public static double flytrapArmorToughness() { return flytrapArmorToughness; }
public static double basiliskArmorToughness() { return basiliskArmorToughness; }
public static double nightmareMobArmorToughness() { return nightmareMobArmorToughness; }
public static double alphaMantisArmorToughness() { return alphaMantisArmorToughness; }
@@ -2958,10 +3145,4 @@ public final class AntarchySettings {
public static int bossMagicWardDurationTicks() { return bossMagicWardDurationTicks; }
public static int bossMagicWardTriggerBreaches() { return bossMagicWardTriggerBreaches; }
public static java.util.List<String> magicBurstDamageNamespaces() { return magicBurstDamageNamespaces; }
public static double jumpyBugLatchDamage() { return jumpyBugLatchDamage; }
public static void setJumpyBugLatchDamage(double value) { jumpyBugLatchDamage = value; }
public static double waterCannonCooldownSeconds() { return waterCannonCooldownSeconds; }
public static void setWaterCannonCooldownSeconds(double value) { waterCannonCooldownSeconds = value; }
public static double toreterrorRangedWaterBombChance() { return toreterrorRangedWaterBombChance; }
public static void setToreterrorRangedWaterBombChance(double value) { toreterrorRangedWaterBombChance = value; }
}
@@ -6,31 +6,32 @@ import java.nio.file.Files;
import java.nio.file.Path;
public final class ConfigResetGuard {
public static final int RESET_GENERATION = 6;
public static final int RESET_GENERATION = 9;
private static final String MARKER_FILE_NAME = ".config_generation";
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;
@@ -0,0 +1,199 @@
package com.craisinlord.antarchy.content;
import net.minecraft.world.item.ItemStack;
import net.minecraft.client.player.LocalPlayer;
import java.util.UUID;
import com.craisinlord.anthology.portal.client.PortalClientHooks;
import com.craisinlord.anthology.portal.PortalSettings;
import com.craisinlord.anthology.portal.PortalHooks;
import com.craisinlord.anthology.portal.PortalEntity;
import com.craisinlord.antarchy.content.portalgun.PortalGunProjectileEntity;
import com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity;
import com.craisinlord.antarchy.content.item.PortalGunItem;
import com.craisinlord.antarchy.content.client.renderer.PortalGunMoonViewRenderer;
import com.craisinlord.antarchy.config.AntarchySettings;
import com.craisinlord.antarchy.content.item.BigBerthaItem;
import com.craisinlord.antarchy.content.client.HerculesBeetleImpactShakeClientState;
import com.craisinlord.anthology.shake.client.CameraShake;
import com.craisinlord.antarchy.Antarchy;
import com.craisinlord.antarchy.content.effect.RoyalEffectEligibility;
import com.craisinlord.antarchy.content.enchantment.AntarchyEnchantments;
import com.craisinlord.antarchy.content.entity.BedBugEntity;
import com.craisinlord.antarchy.content.entity.portal.DimensionalTearEntity;
import com.craisinlord.antarchy.content.entity.royal.QueenEntity;
import com.craisinlord.antarchy.content.entity.royal.RoyalBlackHoleEntity;
import com.craisinlord.antarchy.content.fluid.AntarchyFluidChecks;
import com.craisinlord.antarchy.content.item.TemporalTunerAccess;
import com.craisinlord.antarchy.content.item.TemporalTunerItem;
import com.craisinlord.antarchy.content.worldgen.thoraxis.ThoraxisUndersideManager;
import com.craisinlord.anthology.effect.AnthologyEffects;
import com.craisinlord.anthology.gravity.GravityApi;
import com.craisinlord.anthology.gravity.GravityHooks;
import com.craisinlord.anthology.time.TimeDilationApi;
import com.craisinlord.anthology.time.TimeDilationFieldEntity;
import com.craisinlord.anthology.time.TimeDilationHooks;
import com.craisinlord.anthology.time.TimeDilationMath;
import com.craisinlord.anthology.time.client.ContractionAfterimages;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.effect.MobEffectInstance;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.Mob;
import net.minecraft.world.entity.player.Player;
public final class AntarchyAnthologyHooks {
private AntarchyAnthologyHooks() {
}
public static void register() {
GravityHooks.addInvertedRegion(ThoraxisUndersideManager::isInUnderside);
GravityHooks.addInvertedFluid(AntarchyFluidChecks::isAntiwater);
GravityHooks.addLoadListener(AntarchyAnthologyHooks::clearStaleUndersideInversion);
TimeDilationHooks.addTrackedPredicate(entity -> entity instanceof Player player && TemporalTunerItem.isAvailable(player));
TimeDilationHooks.addRateSource(AntarchyAnthologyHooks::temporalTunerRate);
TimeDilationHooks.addFieldExclusion(AntarchyAnthologyHooks::excludedFromField);
TimeDilationHooks.addChronosphereLevelSource(AntarchyEnchantments::chronosphereLevel);
PortalHooks.setSettings(new AntarchyPortalSettings());
PortalHooks.addTransitExclusion(entity -> entity instanceof PortalGunProjectileEntity);
}
public static void registerClient() {
PortalClientHooks.registerCustomView(portal -> portal instanceof PortalGunPortalEntity gunPortal && gunPortal.isMoonPortal(), PortalGunMoonViewRenderer::render);
PortalClientHooks.setOutlinePolicy(AntarchyAnthologyHooks::outlinesPortal);
CameraShake.addStrengthSource(cameraPos -> HerculesBeetleImpactShakeClientState.getTicks() / 25.0F * 2.0F);
CameraShake.addStrengthSource(cameraPos -> BigBerthaItem.clientShakeTicks / 25.0F * 2.0F);
ContractionAfterimages.addActivator(living -> living instanceof Player player
&& TemporalTunerItem.isAvailable(player)
&& TimeDilationApi.getRate(living) > 1.0D);
}
private static boolean outlinesPortal(PortalEntity portal, LocalPlayer viewer) {
if (!(portal instanceof PortalGunPortalEntity gunPortal) || gunPortal.getOwnerId() == null) {
return false;
}
UUID owner = gunPortal.getOwnerId();
return owner.equals(viewer.getUUID())
|| owner.equals(heldGunOwner(viewer.getMainHandItem(), viewer.getUUID()))
|| owner.equals(heldGunOwner(viewer.getOffhandItem(), viewer.getUUID()));
}
private static UUID heldGunOwner(ItemStack stack, UUID fallback) {
return stack.getItem() instanceof PortalGunItem ? PortalGunItem.getPortalOwnerId(stack, fallback) : null;
}
private static final class AntarchyPortalSettings implements PortalSettings {
@Override
public boolean seeThroughEnabled() {
return AntarchySettings.portalGunSeeThroughPortals();
}
@Override
public int maxLiveViews() {
return AntarchySettings.portalGunMaxLiveViews();
}
@Override
public double minFloorExitSpeed() {
return AntarchySettings.portalGunMinFloorExitSpeed();
}
@Override
public double maxExitSpeed() {
return AntarchySettings.portalGunMaxExitSpeed();
}
@Override
public boolean fancySurfaces() {
return AntarchySettings.portalGunFancyPortals();
}
}
private static double temporalTunerRate(Entity entity) {
if (!(entity instanceof Player player) || !(player instanceof TemporalTunerAccess access)) {
return TimeDilationMath.NORMAL_RATE;
}
if (TemporalTunerItem.isAvailable(player)) {
return TemporalTunerItem.getRate(player);
}
access.antarchy$setTemporalTunerRate(TimeDilationMath.NORMAL_RATE);
TemporalTunerItem.syncTooltipRate(player, TimeDilationMath.NORMAL_RATE);
return TimeDilationMath.NORMAL_RATE;
}
private static boolean excludedFromField(TimeDilationFieldEntity field, Entity entity) {
if (!field.isOwnerAnchored() && entity instanceof RoyalBlackHoleEntity) {
return true;
}
if (!(field.owner() instanceof QueenEntity)) {
return false;
}
if (entity instanceof LivingEntity living && !RoyalEffectEligibility.canApplyDilated(living)) {
return true;
}
return !field.isOwnerAnchored() && entity.getType().is(AntarchyTags.Entities.QUEEN_DOES_NOT_ATTACK);
}
private static void clearStaleUndersideInversion(Entity entity) {
if (!(entity instanceof Mob mob)
|| !GravityApi.isGravityInverted(mob)
|| !GravityApi.isGravityForced(mob)) {
return;
}
MobEffectInstance invertedEffect = mob.getEffect(AnthologyEffects.inverted());
boolean aboveUndersideExit = ThoraxisUndersideManager.isAboveUndersideExit(mob);
int nearbyDimensionalTears = countNearbyDimensionalTears(mob);
if (invertedEffect != null
&& invertedEffect.isAmbient()
&& !invertedEffect.isVisible()
&& aboveUndersideExit) {
clearInversion(mob);
Antarchy.LOGGER.warn(
"[antarchy-gravity] cleared stale underside inversion from loaded mob type={} uuid={} pos=({}, {}, {}) dim={} effectDuration={} nearbyDimensionalTears={}",
mob.getType(), mob.getUUID(), mob.getX(), mob.getY(), mob.getZ(),
mob.level() != null ? mob.level().dimension().location() : "?",
invertedEffect.getDuration(), nearbyDimensionalTears
);
return;
}
if (invertedEffect != null
&& mob instanceof BedBugEntity
&& aboveUndersideExit
&& ThoraxisUndersideManager.isThoraxis(mob.level())) {
clearInversion(mob);
Antarchy.LOGGER.warn(
"[antarchy-gravity] cleared loaded inverted bed bug above underside exit type={} uuid={} pos=({}, {}, {}) dim={} effectDuration={} effectAmbient={} effectVisible={} nearbyDimensionalTears={}",
mob.getType(), mob.getUUID(), mob.getX(), mob.getY(), mob.getZ(),
mob.level() != null ? mob.level().dimension().location() : "?",
invertedEffect.getDuration(), invertedEffect.isAmbient(), invertedEffect.isVisible(), nearbyDimensionalTears
);
return;
}
Antarchy.LOGGER.info(
"[antarchy-gravity] loaded inverted mob type={} uuid={} pos=({}, {}, {}) dim={} hasInvertedEffect={} effectDuration={} effectAmbient={} effectVisible={} aboveUndersideExit={} nearbyDimensionalTears={}",
mob.getType(), mob.getUUID(), mob.getX(), mob.getY(), mob.getZ(),
mob.level() != null ? mob.level().dimension().location() : "?",
invertedEffect != null,
invertedEffect != null ? invertedEffect.getDuration() : -1,
invertedEffect != null && invertedEffect.isAmbient(),
invertedEffect != null && invertedEffect.isVisible(),
aboveUndersideExit,
nearbyDimensionalTears
);
}
private static void clearInversion(Mob mob) {
mob.removeEffect(AnthologyEffects.inverted());
GravityApi.resetGravityState(mob);
}
private static int countNearbyDimensionalTears(Mob mob) {
if (!(mob.level() instanceof ServerLevel serverLevel)) {
return -1;
}
return serverLevel.getEntitiesOfClass(
DimensionalTearEntity.class,
mob.getBoundingBox().inflate(48.0D),
Entity::isAlive
).size();
}
}
@@ -10,9 +10,9 @@ import com.craisinlord.antarchy.content.entity.HerculesBeetleEntity;
import com.craisinlord.antarchy.content.entity.HushProjectileEntity;
import com.craisinlord.antarchy.content.entity.StinkBugEntity;
import com.craisinlord.antarchy.content.entity.WaterBombEntity;
import com.craisinlord.antarchy.content.entity.TriffidEntity;
import com.craisinlord.antarchy.content.entity.CreepingHorrorEntity;
import com.craisinlord.antarchy.content.entity.LurkingTerrorEntity;
import com.craisinlord.antarchy.content.entity.FlytrapEntity;
import com.craisinlord.antarchy.content.entity.CrawlingBlightEntity;
import com.craisinlord.antarchy.content.entity.SkulkingFrightEntity;
import com.craisinlord.antarchy.content.entity.ToreterrorEntity;
import com.craisinlord.antarchy.content.entity.WaspEntity;
import com.craisinlord.antarchy.content.entity.brutalfly.BrutalflyEntity;
@@ -33,7 +33,6 @@ import com.craisinlord.antarchy.content.entity.lucid.LucidEntity;
import com.craisinlord.antarchy.content.entity.vortex.VortexChargeProjectileEntity;
import com.craisinlord.antarchy.content.entity.vortex.VortexEntity;
import com.craisinlord.antarchy.content.entity.vortex.WindVortexEntity;
import com.craisinlord.antarchy.content.time.TimeDilationFieldEntity;
import com.craisinlord.antarchy.content.entity.nightmare.NightmareEntity;
import com.craisinlord.antarchy.content.entity.nightmare.NightmareBiteEntity;
import com.craisinlord.antarchy.content.entity.nightmare.NightmarePortalEntity;
@@ -99,8 +98,6 @@ public final class AntarchyObjects {
@SuppressWarnings("unchecked")
public static Supplier<EntityType<VortexChargeProjectileEntity>> VORTEX_CHARGE_PROJECTILE = (Supplier<EntityType<VortexChargeProjectileEntity>>) UNBOUND;
@SuppressWarnings("unchecked")
public static Supplier<EntityType<TimeDilationFieldEntity>> TIME_DILATION_FIELD = (Supplier<EntityType<TimeDilationFieldEntity>>) UNBOUND;
@SuppressWarnings("unchecked")
public static Supplier<EntityType<com.craisinlord.antarchy.content.entity.royal.RoyalBlackHoleEntity>> ROYAL_BLACK_HOLE = (Supplier<EntityType<com.craisinlord.antarchy.content.entity.royal.RoyalBlackHoleEntity>>) UNBOUND;
@SuppressWarnings("unchecked")
public static Supplier<EntityType<com.craisinlord.antarchy.content.entity.royal.RoyalAssailantBlackHoleEntity>> ROYAL_ASSAILANT_BLACK_HOLE = (Supplier<EntityType<com.craisinlord.antarchy.content.entity.royal.RoyalAssailantBlackHoleEntity>>) UNBOUND;
@@ -127,7 +124,7 @@ public final class AntarchyObjects {
@SuppressWarnings("unchecked")
public static Supplier<EntityType<ReverieEntity>> REVERIE = (Supplier<EntityType<ReverieEntity>>) UNBOUND;
@SuppressWarnings("unchecked")
public static Supplier<EntityType<TriffidEntity>> TRIFFID = (Supplier<EntityType<TriffidEntity>>) UNBOUND;
public static Supplier<EntityType<FlytrapEntity>> FLYTRAP = (Supplier<EntityType<FlytrapEntity>>) UNBOUND;
@SuppressWarnings("unchecked")
public static Supplier<EntityType<BrutalflyEntity>> BRUTALFLY = (Supplier<EntityType<BrutalflyEntity>>) UNBOUND;
@SuppressWarnings("unchecked")
@@ -139,9 +136,9 @@ public final class AntarchyObjects {
@SuppressWarnings("unchecked")
public static Supplier<EntityType<WaterBombEntity>> WATER_BOMB = (Supplier<EntityType<WaterBombEntity>>) UNBOUND;
@SuppressWarnings("unchecked")
public static Supplier<EntityType<CreepingHorrorEntity>> CREEPING_HORROR = (Supplier<EntityType<CreepingHorrorEntity>>) UNBOUND;
public static Supplier<EntityType<CrawlingBlightEntity>> CRAWLING_BLIGHT = (Supplier<EntityType<CrawlingBlightEntity>>) UNBOUND;
@SuppressWarnings("unchecked")
public static Supplier<EntityType<LurkingTerrorEntity>> LURKING_TERROR = (Supplier<EntityType<LurkingTerrorEntity>>) UNBOUND;
public static Supplier<EntityType<SkulkingFrightEntity>> SKULKING_FRIGHT = (Supplier<EntityType<SkulkingFrightEntity>>) UNBOUND;
@SuppressWarnings("unchecked")
public static Supplier<EntityType<com.craisinlord.antarchy.content.entity.ManticoreEntity>> MANTICORE = (Supplier<EntityType<com.craisinlord.antarchy.content.entity.ManticoreEntity>>) UNBOUND;
@SuppressWarnings("unchecked")
@@ -215,6 +212,8 @@ public final class AntarchyObjects {
@SuppressWarnings("unchecked")
public static Supplier<MenuType<com.craisinlord.antarchy.content.menu.RoyalJudgmentMenu>> ROYAL_JUDGMENT_MENU = (Supplier<MenuType<com.craisinlord.antarchy.content.menu.RoyalJudgmentMenu>>) UNBOUND;
@SuppressWarnings("unchecked")
public static Supplier<MenuType<com.craisinlord.antarchy.content.menu.GiantFryingPanMenu>> GIANT_FRYING_PAN_MENU = (Supplier<MenuType<com.craisinlord.antarchy.content.menu.GiantFryingPanMenu>>) UNBOUND;
@SuppressWarnings("unchecked")
public static Supplier<net.minecraft.world.item.Item> CHEEP_ITEM = (Supplier<net.minecraft.world.item.Item>) UNBOUND;
@SuppressWarnings("unchecked")
public static Supplier<Block> DUPLICATOR_LOG = (Supplier<Block>) UNBOUND;
@@ -267,7 +266,7 @@ public final class AntarchyObjects {
@SuppressWarnings("unchecked")
public static Supplier<Block> CLOUD_BLOCK = (Supplier<Block>) UNBOUND;
@SuppressWarnings("unchecked")
public static Supplier<Block> TRIFFID_GOO_BLOCK = (Supplier<Block>) UNBOUND;
public static Supplier<Block> FLYTRAP_GOO_BLOCK = (Supplier<Block>) UNBOUND;
@SuppressWarnings("unchecked")
public static Supplier<Block> PALE_NYXITE = (Supplier<Block>) UNBOUND;
@SuppressWarnings("unchecked")
@@ -314,6 +313,7 @@ public final class AntarchyObjects {
public static Supplier<Holder<Attribute>> DOUBLE_DAMAGE_CHANCE = (Supplier<Holder<Attribute>>) UNBOUND;
@SuppressWarnings("unchecked")
public static Supplier<Holder<Attribute>> BLOODGLASS_MAX_HEARTS = (Supplier<Holder<Attribute>>) UNBOUND;
public static Supplier<Attribute> ENTITY_REACH = () -> null;
@SuppressWarnings("unchecked")
public static Supplier<MobEffect> BLOODGLASS_WARD = (Supplier<MobEffect>) UNBOUND;
@SuppressWarnings("unchecked")
@@ -324,9 +324,11 @@ public final class AntarchyObjects {
public static Supplier<MobEffect> STINKY_EFFECT = (Supplier<MobEffect>) UNBOUND;
@SuppressWarnings("unchecked")
public static Supplier<MobEffect> GLIMMERING_EFFECT = (Supplier<MobEffect>) UNBOUND;
public static Supplier<MobEffect> DILATED_EFFECT = (Supplier<MobEffect>) UNBOUND;
public static Supplier<MobEffect> ROYAL_VITALITY_PENALTY_EFFECT = (Supplier<MobEffect>) UNBOUND;
@SuppressWarnings("unchecked")
public static Supplier<MobEffect> CONTRACTED_EFFECT = (Supplier<MobEffect>) UNBOUND;
public static Supplier<MobEffect> BLEEDING_EFFECT = (Supplier<MobEffect>) UNBOUND;
@SuppressWarnings("unchecked")
public static Supplier<MobEffect> BLOOD_MOON_EFFECT = (Supplier<MobEffect>) UNBOUND;
@SuppressWarnings("unchecked")
public static Supplier<Fluid> LUMEN = (Supplier<Fluid>) UNBOUND;
@SuppressWarnings("unchecked")
@@ -352,9 +354,9 @@ public final class AntarchyObjects {
@SuppressWarnings("unchecked")
public static Supplier<Block> BED_BUG_EGG = (Supplier<Block>) UNBOUND;
@SuppressWarnings("unchecked")
public static Supplier<Block> CREEPING_HORROR_EGGS = (Supplier<Block>) UNBOUND;
public static Supplier<Block> CRAWLING_BLIGHT_EGGS = (Supplier<Block>) UNBOUND;
@SuppressWarnings("unchecked")
public static Supplier<Block> LURKING_TERROR_EGGS = (Supplier<Block>) UNBOUND;
public static Supplier<Block> SKULKING_FRIGHT_EGGS = (Supplier<Block>) UNBOUND;
@SuppressWarnings("unchecked")
public static Supplier<Block> WASP_NEST = (Supplier<Block>) UNBOUND;
@SuppressWarnings("unchecked")
@@ -400,6 +402,8 @@ public final class AntarchyObjects {
@SuppressWarnings("unchecked")
public static Supplier<SimpleParticleType> STINKY_GAS = (Supplier<SimpleParticleType>) UNBOUND;
@SuppressWarnings("unchecked")
public static Supplier<SimpleParticleType> BUG_SPRAY_PARTICLE = (Supplier<SimpleParticleType>) UNBOUND;
@SuppressWarnings("unchecked")
public static Supplier<SimpleParticleType> NIGHTMARE_FIRE_FLAME = (Supplier<SimpleParticleType>) UNBOUND;
@SuppressWarnings("unchecked")
public static Supplier<SimpleParticleType> STINKY_FLY = (Supplier<SimpleParticleType>) UNBOUND;
@@ -432,11 +436,6 @@ public final class AntarchyObjects {
private AntarchyObjects() {
}
/** Compatibility entry point for loader registries that bind the expanded 1.21 object set. */
public static void bind(Supplier<?>... ignored) {
// Loader-specific registrations populate the fields through the explicit setters below.
}
public static void bind(
Supplier<EntityType<EasterBunnyEntity>> easterBunny,
Supplier<EntityType<FlyingSquirrelEntity>> flyingSquirrel,
@@ -451,14 +450,14 @@ public final class AntarchyObjects {
Supplier<EntityType<CaterpillarEntity>> caterpillar,
Supplier<EntityType<ButterflyEntity>> butterfly,
Supplier<EntityType<ReverieEntity>> reverie,
Supplier<EntityType<TriffidEntity>> triffid,
Supplier<EntityType<FlytrapEntity>> flytrap,
Supplier<EntityType<BrutalflyEntity>> brutalfly,
Supplier<EntityType<BrutalflyOrbEntity>> brutalflyOrb,
Supplier<EntityType<HushProjectileEntity>> hushProjectile,
Supplier<EntityType<ToreterrorEntity>> toreterror,
Supplier<EntityType<WaterBombEntity>> waterBomb,
Supplier<EntityType<CreepingHorrorEntity>> creepingHorror,
Supplier<EntityType<LurkingTerrorEntity>> lurkingTerror,
Supplier<EntityType<CrawlingBlightEntity>> crawlingBlight,
Supplier<EntityType<SkulkingFrightEntity>> skulkingFright,
Supplier<EntityType<StinkBugEntity>> stinkBug,
Supplier<EntityType<com.craisinlord.antarchy.content.entity.CheepEntity>> cheep,
Supplier<EntityType<com.craisinlord.antarchy.content.entity.DorrieEntity>> dorrie,
@@ -487,7 +486,7 @@ public final class AntarchyObjects {
Supplier<Block> shellstoneBrickSlab,
Supplier<Block> shellstoneBrickWall,
Supplier<Block> cloudBlock,
Supplier<Block> triffidGooBlock,
Supplier<Block> flytrapGooBlock,
Supplier<Block> paleNyxite,
Supplier<Block> nyxiteSpike,
Supplier<Block> chitinBlock,
@@ -519,8 +518,8 @@ public final class AntarchyObjects {
Supplier<Block> orangeMilkweed,
Supplier<Block> pinkMilkweed,
Supplier<Block> bedBugEggs,
Supplier<Block> creepingHorrorEggs,
Supplier<Block> lurkingTerrorEggs,
Supplier<Block> crawlingBlightEggs,
Supplier<Block> skulkingFrightEggs,
Supplier<Block> waspNest,
Supplier<Block> hushweed,
Supplier<Block> squirrelNestBlock,
@@ -552,14 +551,14 @@ public final class AntarchyObjects {
CATERPILLAR = caterpillar;
BUTTERFLY = butterfly;
REVERIE = reverie;
TRIFFID = triffid;
FLYTRAP = flytrap;
BRUTALFLY = brutalfly;
BRUTALFLY_ORB = brutalflyOrb;
HUSH_PROJECTILE = hushProjectile;
TORETERROR = toreterror;
WATER_BOMB = waterBomb;
CREEPING_HORROR = creepingHorror;
LURKING_TERROR = lurkingTerror;
CRAWLING_BLIGHT = crawlingBlight;
SKULKING_FRIGHT = skulkingFright;
STINK_BUG = stinkBug;
CHEEP = cheep;
DORRIE = dorrie;
@@ -588,7 +587,7 @@ public final class AntarchyObjects {
SHELLSTONE_BRICK_SLAB = shellstoneBrickSlab;
SHELLSTONE_BRICK_WALL = shellstoneBrickWall;
CLOUD_BLOCK = cloudBlock;
TRIFFID_GOO_BLOCK = triffidGooBlock;
FLYTRAP_GOO_BLOCK = flytrapGooBlock;
PALE_NYXITE = paleNyxite;
NYXITE_SPIKE = nyxiteSpike;
CHITIN_BLOCK = chitinBlock;
@@ -620,8 +619,8 @@ public final class AntarchyObjects {
ORANGE_MILKWEED = orangeMilkweed;
PINK_MILKWEED = pinkMilkweed;
BED_BUG_EGG = bedBugEggs;
CREEPING_HORROR_EGGS = creepingHorrorEggs;
LURKING_TERROR_EGGS = lurkingTerrorEggs;
CRAWLING_BLIGHT_EGGS = crawlingBlightEggs;
SKULKING_FRIGHT_EGGS = skulkingFrightEggs;
WASP_NEST = waspNest;
HUSHWEED = hushweed;
SQUIRREL_NEST_BLOCK = squirrelNestBlock;
@@ -649,6 +648,10 @@ public final class AntarchyObjects {
OCTOPUS_BOMB = supplier;
}
public static void setBugSprayParticle(Supplier<SimpleParticleType> supplier) {
BUG_SPRAY_PARTICLE = supplier;
}
public static void setTentacle(Supplier<EntityType<TentacleEntity>> supplier) {
TENTACLE = supplier;
}
@@ -695,10 +698,6 @@ public final class AntarchyObjects {
VORTEX_CHARGE_PROJECTILE = supplier;
}
public static void setTimeDilationField(Supplier<EntityType<TimeDilationFieldEntity>> supplier) {
TIME_DILATION_FIELD = supplier;
}
public static void setRoyalBlackHole(Supplier<EntityType<com.craisinlord.antarchy.content.entity.royal.RoyalBlackHoleEntity>> supplier) {
ROYAL_BLACK_HOLE = supplier;
}
@@ -823,12 +822,16 @@ public final class AntarchyObjects {
GLIMMERING_EFFECT = supplier;
}
public static void setDilatedEffect(Supplier<MobEffect> supplier) {
DILATED_EFFECT = supplier;
public static void setRoyalVitalityPenaltyEffect(Supplier<MobEffect> supplier) {
ROYAL_VITALITY_PENALTY_EFFECT = supplier;
}
public static void setContractedEffect(Supplier<MobEffect> supplier) {
CONTRACTED_EFFECT = supplier;
public static void setBleedingEffect(Supplier<MobEffect> supplier) {
BLEEDING_EFFECT = supplier;
}
public static void setBloodMoonEffect(Supplier<MobEffect> supplier) {
BLOOD_MOON_EFFECT = supplier;
}
public static void setElka(Supplier<EntityType<com.craisinlord.antarchy.content.entity.ElkaEntity>> supplier) {
@@ -907,6 +910,10 @@ public final class AntarchyObjects {
LUCID_BOLT_IMPACT_LARGE = supplier;
}
public static void setEntityReach(Supplier<Attribute> supplier) {
ENTITY_REACH = supplier;
}
public static void setPeachLeavesParticle(Supplier<SimpleParticleType> supplier) {
PEACH_LEAVES_PARTICLE = supplier;
}
@@ -923,6 +930,10 @@ public final class AntarchyObjects {
ROYAL_JUDGMENT_MENU = supplier;
}
public static void setGiantFryingPanMenu(Supplier<MenuType<com.craisinlord.antarchy.content.menu.GiantFryingPanMenu>> supplier) {
GIANT_FRYING_PAN_MENU = supplier;
}
public static void setAmericanComponent(Supplier<AntarchyItemComponent<net.minecraft.util.Unit>> supplier) {
AMERICAN_COMPONENT = supplier.get();
}
@@ -26,11 +26,11 @@ public final class AntarchySoundEvents {
public static Supplier<SoundEvent> ANT_BITE = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> ANT_GATHER = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> ANT_NEST = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> CLOUD_SHARK_BITE = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> CLOUD_SHARK_IDLE = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> CLOUD_SHARK_HURT = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> CLOUD_SHARK_DEATH = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> CLOUD_SHARK_FLY = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> STRATOSHARK_BITE = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> STRATOSHARK_IDLE = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> STRATOSHARK_HURT = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> STRATOSHARK_DEATH = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> STRATOSHARK_FLY = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> CATERPILLAR_IDLE = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> CATERPILLAR_HURT = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> CATERPILLAR_CRAWL = (Supplier<SoundEvent>) UNBOUND;
@@ -94,16 +94,20 @@ public final class AntarchySoundEvents {
public static Supplier<SoundEvent> NIGHTMARE_DEATH = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> NIGHTMARE_BITE = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> NIGHTMARE_FLAP = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> TRIFFID_ATTACK = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> TRIFFID_GRAB = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> TRIFFID_HURT = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> TRIFFID_DEATH = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> TRIFFID_HISS = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> TRIFFID_GROWL = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> FLYTRAP_ATTACK = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> FLYTRAP_GRAB = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> FLYTRAP_HURT = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> FLYTRAP_DEATH = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> FLYTRAP_HISS = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> FLYTRAP_GROWL = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> MANTIS_AMBIENT = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> MANTIS_HURT = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> MANTIS_ATTACK = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> MANTIS_FLY_LOOP = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> MANTICORE_IDLE = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> MANTICORE_WALK = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> MANTICORE_FLY = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> MANTICORE_BITE = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> GRAVITY_GUN_PICKUP = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> GRAVITY_GUN_DROP = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> GRAVITY_GUN_HOLD_LOOP = (Supplier<SoundEvent>) UNBOUND;
@@ -148,13 +152,13 @@ public final class AntarchySoundEvents {
public static Supplier<SoundEvent> STINK_BUG_IDLE = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> CAVARYN_HEARTBEAT = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> ROLLY_POLLY_IDLE = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> CREEPING_HORROR_GROWL = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> CREEPING_HORROR_HURT = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> CREEPING_HORROR_BITE = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> LURKING_TERROR_SNARL = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> LURKING_TERROR_HURT = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> LURKING_TERROR_BITE = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> LURKING_TERROR_FLY_LOOP = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> CRAWLING_BLIGHT_GROWL = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> CRAWLING_BLIGHT_HURT = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> CRAWLING_BLIGHT_BITE = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> SKULKING_FRIGHT_SNARL = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> SKULKING_FRIGHT_HURT = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> SKULKING_FRIGHT_BITE = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> SKULKING_FRIGHT_FLY_LOOP = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> DUCT_TAPE_USE = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> HERCULES_BEETLE_IDLE = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> HERCULES_BEETLE_HURT = (Supplier<SoundEvent>) UNBOUND;
@@ -162,10 +166,10 @@ public final class AntarchySoundEvents {
public static Supplier<SoundEvent> HERCULES_BEETLE_CRY = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> HERCULES_BEETLE_CHARGE_START = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> HERCULES_BEETLE_KNOCKED_DOWN = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> JUMPY_BUG_IDLE = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> SPRINGBUG_IDLE = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> SPIT_BUG_IDLE = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> JUMPY_BUG_HURT = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> JUMPY_BUG_JUMP = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> SPRINGBUG_HURT = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> SPRINGBUG_JUMP = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> SPIT_BUG_HURT = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> SPIT_BUG_SPIT = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> BRUTALFLY_SPIT = (Supplier<SoundEvent>) UNBOUND;
@@ -196,6 +200,7 @@ public final class AntarchySoundEvents {
public static Supplier<SoundEvent> QUEEN_ROAR = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> KING_BEAM_SHOOT = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> KING_FIREBALL_SHOOT = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> KING_ICE_SPIKES = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> KING_DECREE_CAST = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> QUEEN_BEAM_SHOOT = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> QUEEN_BEAM_START = (Supplier<SoundEvent>) UNBOUND;
@@ -216,6 +221,12 @@ public final class AntarchySoundEvents {
public static Supplier<SoundEvent> PRINCESS_SHOOT = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> PRINCESS_FLY = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> PRINCESS_STEP = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> KING_DECREE = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> KING_SUCCESS = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> KING_JUDGEMENT = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> HOVERBOARD_MOUNT = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> HOVERBOARD_DISMOUNT = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> HOVERBOARD_IDLE = (Supplier<SoundEvent>) UNBOUND;
private AntarchySoundEvents() {
}
@@ -238,11 +249,11 @@ public final class AntarchySoundEvents {
Supplier<SoundEvent> antBite,
Supplier<SoundEvent> antGather,
Supplier<SoundEvent> antNest,
Supplier<SoundEvent> cloudSharkBite,
Supplier<SoundEvent> cloudSharkIdle,
Supplier<SoundEvent> cloudSharkHurt,
Supplier<SoundEvent> cloudSharkDeath,
Supplier<SoundEvent> cloudSharkFly,
Supplier<SoundEvent> stratosharkBite,
Supplier<SoundEvent> stratosharkIdle,
Supplier<SoundEvent> stratosharkHurt,
Supplier<SoundEvent> stratosharkDeath,
Supplier<SoundEvent> stratosharkFly,
Supplier<SoundEvent> caterpillarIdle,
Supplier<SoundEvent> caterpillarHurt,
Supplier<SoundEvent> caterpillarCrawl,
@@ -302,16 +313,20 @@ public final class AntarchySoundEvents {
Supplier<SoundEvent> nightmareDeath,
Supplier<SoundEvent> nightmareBite,
Supplier<SoundEvent> nightmareFlap,
Supplier<SoundEvent> triffidAttack,
Supplier<SoundEvent> triffidGrab,
Supplier<SoundEvent> triffidHurt,
Supplier<SoundEvent> triffidDeath,
Supplier<SoundEvent> triffidHiss,
Supplier<SoundEvent> triffidGrowl,
Supplier<SoundEvent> flytrapAttack,
Supplier<SoundEvent> flytrapGrab,
Supplier<SoundEvent> flytrapHurt,
Supplier<SoundEvent> flytrapDeath,
Supplier<SoundEvent> flytrapHiss,
Supplier<SoundEvent> flytrapGrowl,
Supplier<SoundEvent> mantisAmbient,
Supplier<SoundEvent> mantisHurt,
Supplier<SoundEvent> mantisAttack,
Supplier<SoundEvent> mantisFlyLoop,
Supplier<SoundEvent> manticoreIdle,
Supplier<SoundEvent> manticoreWalk,
Supplier<SoundEvent> manticoreFly,
Supplier<SoundEvent> manticoreBite,
Supplier<SoundEvent> gravityGunPickup,
Supplier<SoundEvent> gravityGunDrop,
Supplier<SoundEvent> gravityGunHoldLoop,
@@ -356,13 +371,13 @@ public final class AntarchySoundEvents {
Supplier<SoundEvent> stinkBugIdle,
Supplier<SoundEvent> cavarynHeartbeat,
Supplier<SoundEvent> rollyPollyIdle,
Supplier<SoundEvent> creepingHorrorGrowl,
Supplier<SoundEvent> creepingHorrorHurt,
Supplier<SoundEvent> creepingHorrorBite,
Supplier<SoundEvent> lurkingTerrorSnarl,
Supplier<SoundEvent> lurkingTerrorHurt,
Supplier<SoundEvent> lurkingTerrorBite,
Supplier<SoundEvent> lurkingTerrorFlyLoop,
Supplier<SoundEvent> crawlingBlightGrowl,
Supplier<SoundEvent> crawlingBlightHurt,
Supplier<SoundEvent> crawlingBlightBite,
Supplier<SoundEvent> skulkingFrightSnarl,
Supplier<SoundEvent> skulkingFrightHurt,
Supplier<SoundEvent> skulkingFrightBite,
Supplier<SoundEvent> skulkingFrightFlyLoop,
Supplier<SoundEvent> ductTapeUse,
Supplier<SoundEvent> herculesBeetleIdle,
Supplier<SoundEvent> herculesBeetleHurt,
@@ -370,10 +385,10 @@ public final class AntarchySoundEvents {
Supplier<SoundEvent> herculesBeetleCry,
Supplier<SoundEvent> herculesBeetleChargeStart,
Supplier<SoundEvent> herculesBeetleKnockedDown,
Supplier<SoundEvent> jumpyBugIdle,
Supplier<SoundEvent> springbugIdle,
Supplier<SoundEvent> spitBugIdle,
Supplier<SoundEvent> jumpyBugHurt,
Supplier<SoundEvent> jumpyBugJump,
Supplier<SoundEvent> springbugHurt,
Supplier<SoundEvent> springbugJump,
Supplier<SoundEvent> spitBugHurt,
Supplier<SoundEvent> spitBugSpit,
Supplier<SoundEvent> brutalflySpit,
@@ -404,6 +419,7 @@ public final class AntarchySoundEvents {
Supplier<SoundEvent> queenRoar,
Supplier<SoundEvent> kingBeamShoot,
Supplier<SoundEvent> kingFireballShoot,
Supplier<SoundEvent> kingIceSpikes,
Supplier<SoundEvent> kingDecreeCast,
Supplier<SoundEvent> queenBeamShoot,
Supplier<SoundEvent> queenBeamStart,
@@ -423,7 +439,10 @@ public final class AntarchySoundEvents {
Supplier<SoundEvent> princessBite,
Supplier<SoundEvent> princessShoot,
Supplier<SoundEvent> princessFly,
Supplier<SoundEvent> princessStep
Supplier<SoundEvent> princessStep,
Supplier<SoundEvent> kingDecree,
Supplier<SoundEvent> kingSuccess,
Supplier<SoundEvent> kingJudgement
) {
SQUIDZOOKA_FIRE = squidzookaFire;
SHRINK_RAY_CHARGE = shrinkRayCharge;
@@ -439,11 +458,11 @@ public final class AntarchySoundEvents {
ANT_BITE = antBite;
ANT_GATHER = antGather;
ANT_NEST = antNest;
CLOUD_SHARK_BITE = cloudSharkBite;
CLOUD_SHARK_IDLE = cloudSharkIdle;
CLOUD_SHARK_HURT = cloudSharkHurt;
CLOUD_SHARK_DEATH = cloudSharkDeath;
CLOUD_SHARK_FLY = cloudSharkFly;
STRATOSHARK_BITE = stratosharkBite;
STRATOSHARK_IDLE = stratosharkIdle;
STRATOSHARK_HURT = stratosharkHurt;
STRATOSHARK_DEATH = stratosharkDeath;
STRATOSHARK_FLY = stratosharkFly;
CATERPILLAR_IDLE = caterpillarIdle;
CATERPILLAR_HURT = caterpillarHurt;
CATERPILLAR_CRAWL = caterpillarCrawl;
@@ -503,16 +522,20 @@ public final class AntarchySoundEvents {
NIGHTMARE_DEATH = nightmareDeath;
NIGHTMARE_BITE = nightmareBite;
NIGHTMARE_FLAP = nightmareFlap;
TRIFFID_ATTACK = triffidAttack;
TRIFFID_GRAB = triffidGrab;
TRIFFID_HURT = triffidHurt;
TRIFFID_DEATH = triffidDeath;
TRIFFID_HISS = triffidHiss;
TRIFFID_GROWL = triffidGrowl;
FLYTRAP_ATTACK = flytrapAttack;
FLYTRAP_GRAB = flytrapGrab;
FLYTRAP_HURT = flytrapHurt;
FLYTRAP_DEATH = flytrapDeath;
FLYTRAP_HISS = flytrapHiss;
FLYTRAP_GROWL = flytrapGrowl;
MANTIS_AMBIENT = mantisAmbient;
MANTIS_HURT = mantisHurt;
MANTIS_ATTACK = mantisAttack;
MANTIS_FLY_LOOP = mantisFlyLoop;
MANTICORE_IDLE = manticoreIdle;
MANTICORE_WALK = manticoreWalk;
MANTICORE_FLY = manticoreFly;
MANTICORE_BITE = manticoreBite;
GRAVITY_GUN_PICKUP = gravityGunPickup;
GRAVITY_GUN_DROP = gravityGunDrop;
GRAVITY_GUN_HOLD_LOOP = gravityGunHoldLoop;
@@ -557,13 +580,13 @@ public final class AntarchySoundEvents {
STINK_BUG_IDLE = stinkBugIdle;
CAVARYN_HEARTBEAT = cavarynHeartbeat;
ROLLY_POLLY_IDLE = rollyPollyIdle;
CREEPING_HORROR_GROWL = creepingHorrorGrowl;
CREEPING_HORROR_HURT = creepingHorrorHurt;
CREEPING_HORROR_BITE = creepingHorrorBite;
LURKING_TERROR_SNARL = lurkingTerrorSnarl;
LURKING_TERROR_HURT = lurkingTerrorHurt;
LURKING_TERROR_BITE = lurkingTerrorBite;
LURKING_TERROR_FLY_LOOP = lurkingTerrorFlyLoop;
CRAWLING_BLIGHT_GROWL = crawlingBlightGrowl;
CRAWLING_BLIGHT_HURT = crawlingBlightHurt;
CRAWLING_BLIGHT_BITE = crawlingBlightBite;
SKULKING_FRIGHT_SNARL = skulkingFrightSnarl;
SKULKING_FRIGHT_HURT = skulkingFrightHurt;
SKULKING_FRIGHT_BITE = skulkingFrightBite;
SKULKING_FRIGHT_FLY_LOOP = skulkingFrightFlyLoop;
DUCT_TAPE_USE = ductTapeUse;
HERCULES_BEETLE_IDLE = herculesBeetleIdle;
HERCULES_BEETLE_HURT = herculesBeetleHurt;
@@ -571,10 +594,10 @@ public final class AntarchySoundEvents {
HERCULES_BEETLE_CRY = herculesBeetleCry;
HERCULES_BEETLE_CHARGE_START = herculesBeetleChargeStart;
HERCULES_BEETLE_KNOCKED_DOWN = herculesBeetleKnockedDown;
JUMPY_BUG_IDLE = jumpyBugIdle;
SPRINGBUG_IDLE = springbugIdle;
SPIT_BUG_IDLE = spitBugIdle;
JUMPY_BUG_HURT = jumpyBugHurt;
JUMPY_BUG_JUMP = jumpyBugJump;
SPRINGBUG_HURT = springbugHurt;
SPRINGBUG_JUMP = springbugJump;
SPIT_BUG_HURT = spitBugHurt;
SPIT_BUG_SPIT = spitBugSpit;
BRUTALFLY_SPIT = brutalflySpit;
@@ -605,6 +628,7 @@ public final class AntarchySoundEvents {
QUEEN_ROAR = queenRoar;
KING_BEAM_SHOOT = kingBeamShoot;
KING_FIREBALL_SHOOT = kingFireballShoot;
KING_ICE_SPIKES = kingIceSpikes;
KING_DECREE_CAST = kingDecreeCast;
QUEEN_BEAM_SHOOT = queenBeamShoot;
QUEEN_BEAM_START = queenBeamStart;
@@ -625,6 +649,9 @@ public final class AntarchySoundEvents {
PRINCESS_SHOOT = princessShoot;
PRINCESS_FLY = princessFly;
PRINCESS_STEP = princessStep;
KING_DECREE = kingDecree;
KING_SUCCESS = kingSuccess;
KING_JUDGEMENT = kingJudgement;
}
public static void bindDimensionalTearSounds(
@@ -638,4 +665,10 @@ public final class AntarchySoundEvents {
DIMENSIONAL_TEAR_CREATURE_LEAVES = dimensionalTearCreatureLeaves;
DIMENSIONAL_TEAR_CLOSE = dimensionalTearClose;
}
public static void bindHoverboardSounds(Supplier<SoundEvent> mount, Supplier<SoundEvent> dismount, Supplier<SoundEvent> idle) {
HOVERBOARD_MOUNT = mount;
HOVERBOARD_DISMOUNT = dismount;
HOVERBOARD_IDLE = idle;
}
}
@@ -1,5 +1,6 @@
package com.craisinlord.antarchy.content;
import com.craisinlord.anthology.time.TimeDilationTags;
import com.craisinlord.antarchy.Antarchy;
import net.minecraft.core.registries.Registries;
import net.minecraft.resources.ResourceLocation;
@@ -23,6 +24,7 @@ public final class AntarchyTags {
public static final class Blocks {
public static final TagKey<Block> DUPLICATOR_TREE_BLACKLIST = TagKey.create(Registries.BLOCK, id("duplicator_tree_blacklist"));
public static final TagKey<Block> GRAVITY_GUN_BLACKLIST = TagKey.create(Registries.BLOCK, id("gravity_gun_blacklist"));
public static final TagKey<Block> QUEEN_GRABBABLE_BLOCKS = TagKey.create(Registries.BLOCK, id("queen_grabbable_blocks"));
public static final TagKey<Block> DREAM_FIRE_BASE_BLOCKS = TagKey.create(Registries.BLOCK, id("dream_fire_base_blocks"));
public static final TagKey<Block> POTENT_NYXITE_ACTIVATION_BLOCKS = TagKey.create(Registries.BLOCK, id("potent_nyxite_activation_blocks"));
public static final TagKey<Block> CORNEA_STALK_PLANTABLE = TagKey.create(Registries.BLOCK, id("cornea_stalk_plantable"));
@@ -43,8 +45,8 @@ public final class AntarchyTags {
public static final TagKey<Block> BLUESTONE_COMPONENTS = TagKey.create(Registries.BLOCK, id("bluestone_components"));
public static final TagKey<Block> BLUESTONE_CONNECTABLE = TagKey.create(Registries.BLOCK, id("bluestone_connectable"));
public static final TagKey<Block> ANTIMETAL_INVERSION_BLOCKS = TagKey.create(Registries.BLOCK, id("antimetal_inversion_blocks"));
public static final TagKey<Block> LUCID_ANCHOR_BASE_BLOCKS = TagKey.create(Registries.BLOCK, id("lucid_anchor_base_blocks"));
public static final TagKey<Block> TIGER_EYE_CAMOUFLAGE_BLACKLIST = TagKey.create(Registries.BLOCK, id("tiger_eye_camouflage_blacklist"));
public static final TagKey<Block> LUCID_ANCHOR_BASE_BLOCKS = TagKey.create(Registries.BLOCK, id("lucid_anchor_base_blocks"));
public static final TagKey<Block> NYXIUM = TagKey.create(Registries.BLOCK, id("nyxium"));
private Blocks() {
@@ -54,6 +56,8 @@ public final class AntarchyTags {
public static final class DamageType {
public static final TagKey<net.minecraft.world.damagesource.DamageType> BYPASSES_BLOODGLASS =
TagKey.create(net.minecraft.core.registries.Registries.DAMAGE_TYPE, new ResourceLocation("antarchy", "bypasses_bloodglass"));
public static final TagKey<net.minecraft.world.damagesource.DamageType> ANTARCHY_MAGIC_BURST =
TagKey.create(net.minecraft.core.registries.Registries.DAMAGE_TYPE, new ResourceLocation("antarchy", "antarchy_magic_burst"));
private DamageType() {
}
@@ -61,28 +65,32 @@ public final class AntarchyTags {
public static final class Entities {
public static final TagKey<EntityType<?>> BASILISK_PREY = TagKey.create(Registries.ENTITY_TYPE, id("basilisk_prey"));
public static final TagKey<EntityType<?>> SPELL_RESISTANT_BOSS = TagKey.create(Registries.ENTITY_TYPE, id("spell_resistant_boss"));
public static final TagKey<EntityType<?>> DREAM_SAND_LOW_GRAVITY_BLACKLIST = TagKey.create(Registries.ENTITY_TYPE, id("dream_sand_low_gravity_blacklist"));
public static final TagKey<EntityType<?>> GRAVITY_GUN_BLACKLIST = TagKey.create(Registries.ENTITY_TYPE, id("gravity_gun_blacklist"));
public static final TagKey<EntityType<?>> INVERTED_IMMUNE = TagKey.create(Registries.ENTITY_TYPE, id("inverted_immune"));
public static final TagKey<EntityType<?>> LUCID_BOLT_IMMUNE = TagKey.create(Registries.ENTITY_TYPE, id("lucid_bolt_immune"));
public static final TagKey<EntityType<?>> WIND_VORTEX_IMMUNE = TagKey.create(Registries.ENTITY_TYPE, id("wind_vortex_immune"));
public static final TagKey<EntityType<?>> DUCT_TAPE_STICK_BLACKLIST = TagKey.create(Registries.ENTITY_TYPE, id("duct_tape_stick_blacklist"));
public static final TagKey<EntityType<?>> NIGHTMARE_NO_ATTACK = TagKey.create(Registries.ENTITY_TYPE, id("nightmare_no_attack"));
public static final TagKey<EntityType<?>> SIZE_CHANGING_IMMUNE = TagKey.create(Registries.ENTITY_TYPE, id("size_changing_immune"));
public static final TagKey<EntityType<?>> CRITTER_CAGE_BLACKLIST = TagKey.create(Registries.ENTITY_TYPE, id("critter_cage_blacklist"));
public static final TagKey<EntityType<?>> PARALYSIS_IMMUNE = TagKey.create(Registries.ENTITY_TYPE, id("paralysis_immune"));
public static final TagKey<EntityType<?>> UNDERSIDE_CEILING_SPAWNERS = TagKey.create(Registries.ENTITY_TYPE, id("underside_ceiling_spawners"));
public static final TagKey<EntityType<?>> UNDERSIDE_CEILING_SHORT_GAP = TagKey.create(Registries.ENTITY_TYPE, id("underside_ceiling_short_gap"));
public static final TagKey<EntityType<?>> HUSHWEED_TARGETS = TagKey.create(Registries.ENTITY_TYPE, id("hushweed_targets"));
public static final TagKey<EntityType<?>> TRIFFID_PREY = TagKey.create(Registries.ENTITY_TYPE, id("triffid_prey"));
public static final TagKey<EntityType<?>> FLYTRAP_PREY = TagKey.create(Registries.ENTITY_TYPE, id("flytrap_prey"));
public static final TagKey<EntityType<?>> STINKY_REPELLED_HOSTILES = TagKey.create(Registries.ENTITY_TYPE, id("stinky_repelled_hostiles"));
public static final TagKey<EntityType<?>> SCORPION_WHIP_IMMUNE = TagKey.create(Registries.ENTITY_TYPE, id("scorpion_whip_immune"));
public static final TagKey<EntityType<?>> SCORPION_WHIP_BLACKLIST = TagKey.create(Registries.ENTITY_TYPE, id("scorpion_whip_blacklist"));
public static final TagKey<EntityType<?>> LUCID_ANCHOR_IMMUNE = TagKey.create(Registries.ENTITY_TYPE, id("lucid_anchor_immune"));
public static final TagKey<EntityType<?>> TIGER_EYE_DETECTION_IMMUNE = TagKey.create(Registries.ENTITY_TYPE, id("tiger_eye_detection_immune"));
public static final TagKey<EntityType<?>> LUCID_ANCHOR_IMMUNE = TagKey.create(Registries.ENTITY_TYPE, id("lucid_anchor_immune"));
public static final TagKey<EntityType<?>> DIMENSIONAL_TEAR_COMMON_SPAWNS = TagKey.create(Registries.ENTITY_TYPE, id("dimensional_tear_common_spawns"));
public static final TagKey<EntityType<?>> DIMENSIONAL_TEAR_RARE_SPAWNS = TagKey.create(Registries.ENTITY_TYPE, id("dimensional_tear_rare_spawns"));
public static final TagKey<EntityType<?>> TIME_DILATION_IMMUNE = TagKey.create(Registries.ENTITY_TYPE, id("time_dilation_immune"));
public static final TagKey<EntityType<?>> TIME_DILATION_IMMUNE = TimeDilationTags.TIME_DILATION_IMMUNE;
public static final TagKey<EntityType<?>> QUEEN_DOES_NOT_ATTACK = TagKey.create(Registries.ENTITY_TYPE, id("queen_does_not_attack"));
public static final TagKey<EntityType<?>> COMMANDED_BLACKLIST = TagKey.create(Registries.ENTITY_TYPE, id("commanded_blacklist"));
public static final TagKey<EntityType<?>> DILATED_BLACKLIST = TagKey.create(Registries.ENTITY_TYPE, id("dilated_blacklist"));
public static final TagKey<EntityType<?>> WIND_VORTEX_IMMUNE = TagKey.create(Registries.ENTITY_TYPE, id("wind_vortex_immune"));
private Entities() {
}
@@ -124,6 +132,7 @@ public final class AntarchyTags {
public static final TagKey<Item> GLIMMER_AUGMENT_ELKA = TagKey.create(Registries.ITEM, id("glimmer_augment_elka"));
public static final TagKey<Item> HFCS_CANNOT_AMERICANIZE = TagKey.create(Registries.ITEM, id("hfcs_cannot_americanize"));
public static final TagKey<Item> TIGER_EYE_ARMOR = TagKey.create(Registries.ITEM, id("tigers_eye_armor"));
public static final TagKey<Item> BIG_BERTHA_INGREDIENT = TagKey.create(Registries.ITEM, id("big_bertha_ingredient"));
private Items() {
}
@@ -0,0 +1,132 @@
package com.craisinlord.antarchy.content.advancement;
import com.craisinlord.antarchy.mixins.CriteriaTriggersAccessor;
import com.google.gson.JsonObject;
import java.util.Optional;
import java.util.function.BiConsumer;
import net.minecraft.advancements.CriterionTrigger;
import net.minecraft.advancements.critereon.AbstractCriterionTriggerInstance;
import net.minecraft.advancements.critereon.ContextAwarePredicate;
import net.minecraft.advancements.critereon.DeserializationContext;
import net.minecraft.advancements.critereon.SerializationContext;
import net.minecraft.advancements.critereon.SimpleCriterionTrigger;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.util.GsonHelper;
public final class AntarchyAdvancementTriggers {
private static final CrossedGravityThroat CROSSING = new CrossedGravityThroat(new ResourceLocation("antarchy", "crossed_gravity_throat"));
private static final HordePhaseChanged HORDE_PHASE = new HordePhaseChanged(new ResourceLocation("antarchy", "horde_phase_changed"));
private static final TamedGlimmer TAMED_GLIMMER = new TamedGlimmer(new ResourceLocation("antarchy", "tamed_glimmer"));
private AntarchyAdvancementTriggers() {
}
public static void register() {
register((id, trigger) -> CriteriaTriggersAccessor.antarchy$register(trigger));
}
public static void register(BiConsumer<ResourceLocation, CriterionTrigger<?>> registrar) {
registrar.accept(CROSSING.getId(), CROSSING);
registrar.accept(HORDE_PHASE.getId(), HORDE_PHASE);
registrar.accept(TAMED_GLIMMER.getId(), TAMED_GLIMMER);
}
public static void crossedGravityThroat(ServerPlayer player, String direction) {
CROSSING.fire(player, direction);
}
public static void hordePhaseChanged(ServerPlayer player, String phase) {
HORDE_PHASE.fire(player, phase);
}
public static void tamedGlimmer(ServerPlayer player, String variant) {
TAMED_GLIMMER.fire(player, variant);
}
private static Optional<String> optionalString(JsonObject json, String key) {
return json.has(key) ? Optional.of(GsonHelper.getAsString(json, key)) : Optional.empty();
}
private static final class EventInstance extends AbstractCriterionTriggerInstance {
private final Optional<String> direction;
private final Optional<String> phase;
private final Optional<String> variant;
private EventInstance(ResourceLocation id, ContextAwarePredicate player, Optional<String> direction, Optional<String> phase, Optional<String> variant) {
super(id, player);
this.direction = direction;
this.phase = phase;
this.variant = variant;
}
Optional<String> direction() {
return this.direction;
}
Optional<String> phase() {
return this.phase;
}
Optional<String> variant() {
return this.variant;
}
@Override
public JsonObject serializeToJson(SerializationContext context) {
JsonObject json = super.serializeToJson(context);
this.direction.ifPresent(value -> json.addProperty("direction", value));
this.phase.ifPresent(value -> json.addProperty("phase", value));
this.variant.ifPresent(value -> json.addProperty("variant", value));
return json;
}
}
private abstract static class EventTrigger extends SimpleCriterionTrigger<EventInstance> {
private final ResourceLocation id;
EventTrigger(ResourceLocation id) {
this.id = id;
}
@Override
public ResourceLocation getId() {
return this.id;
}
@Override
protected EventInstance createInstance(JsonObject json, ContextAwarePredicate player, DeserializationContext context) {
return new EventInstance(this.id, player, optionalString(json, "direction"), optionalString(json, "phase"), optionalString(json, "variant"));
}
}
private static final class CrossedGravityThroat extends EventTrigger {
CrossedGravityThroat(ResourceLocation id) {
super(id);
}
private void fire(ServerPlayer player, String direction) {
trigger(player, instance -> instance.direction().map(direction::equals).orElse(true));
}
}
private static final class HordePhaseChanged extends EventTrigger {
HordePhaseChanged(ResourceLocation id) {
super(id);
}
private void fire(ServerPlayer player, String phase) {
trigger(player, instance -> instance.phase().map(phase::equals).orElse(true));
}
}
private static final class TamedGlimmer extends EventTrigger {
TamedGlimmer(ResourceLocation id) {
super(id);
}
private void fire(ServerPlayer player, String variant) {
trigger(player, instance -> instance.variant().map(variant::equals).orElse(true));
}
}
}
@@ -85,7 +85,7 @@ public final class AntDimensionPortalBlock extends Block {
if (level.isClientSide() || !entity.isAlive() || !entity.canChangeDimensions() || entity.isOnPortalCooldown()) {
return;
}
if (!(level instanceof ServerLevel serverLevel)) {
if (!(level instanceof ServerLevel)) {
return;
}
@@ -0,0 +1,52 @@
package com.craisinlord.antarchy.content.block;
import com.craisinlord.antarchy.content.block.entity.AntNestBlockEntity;
import com.craisinlord.antarchy.content.entity.ant.BaseAntEntity;
import java.util.List;
import java.util.function.Supplier;
import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.VoxelShape;
import net.minecraft.world.level.BlockGetter;
public final class AntTrapBlock extends AntNestBlock {
private static final VoxelShape SHAPE = Block.box(3.0D, 0.0D, 3.0D, 13.0D, 5.0D, 13.0D);
public AntTrapBlock(Supplier<List<EntityType<? extends BaseAntEntity>>> antTypesSupplier,
Supplier<? extends BlockEntityType<AntNestBlockEntity>> blockEntityTypeSupplier,
BlockBehaviour.Properties properties) {
super(antTypesSupplier, blockEntityTypeSupplier, 10, false, false, properties);
}
@Override
public VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) {
return SHAPE;
}
@Override
public void onRemove(BlockState state, Level level, BlockPos pos, BlockState newState, boolean movedByPiston) {
if (!state.is(newState.getBlock()) && level instanceof ServerLevel serverLevel
&& level.getBlockEntity(pos) instanceof AntNestBlockEntity trap) {
trap.releaseAll(serverLevel);
}
super.onRemove(state, level, pos, newState, movedByPiston);
}
@Override
public void playerDestroy(Level level, Player player, BlockPos pos, BlockState state, BlockEntity blockEntity, ItemStack stack) {
if (blockEntity instanceof AntNestBlockEntity trap && level instanceof ServerLevel serverLevel) {
trap.releaseAll(serverLevel);
}
super.playerDestroy(level, player, pos, state, blockEntity, stack);
}
}
@@ -12,6 +12,8 @@ import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.world.level.block.state.BlockState;
public class AntigravelBlock extends Block {
private static final int SETTLE_DELAY = 2;
public AntigravelBlock(BlockBehaviour.Properties properties) {
super(properties);
}
@@ -20,13 +22,13 @@ public class AntigravelBlock extends Block {
public void onPlace(BlockState state, Level level, BlockPos pos, BlockState oldState, boolean movedByPiston) {
super.onPlace(state, level, pos, oldState, movedByPiston);
if (!level.isClientSide) {
level.scheduleTick(pos, this, 2);
level.scheduleTick(pos, this, SETTLE_DELAY);
}
}
@Override
public BlockState updateShape(BlockState state, Direction direction, BlockState neighborState, LevelAccessor level, BlockPos pos, BlockPos neighborPos) {
level.scheduleTick(pos, this, 2);
level.scheduleTick(pos, this, SETTLE_DELAY);
return super.updateShape(state, direction, neighborState, level, pos, neighborPos);
}
@@ -38,8 +40,9 @@ public class AntigravelBlock extends Block {
BlockPos abovePos = pos.above();
BlockState above = level.getBlockState(abovePos);
boolean passable = above.isAir() || above.getCollisionShape(level, abovePos).isEmpty();
if (passable) {
UpwardFallingBlockEntity.fallUp(level, pos, state);
if (!passable) {
return;
}
UpwardFallingBlockEntity.fallUp(level, pos, state, false);
}
}
@@ -1,7 +1,7 @@
package com.craisinlord.antarchy.content.block;
import com.craisinlord.antarchy.content.entity.UpwardFallingBlockEntity;
import com.craisinlord.antarchy.content.gravity.AntarchyGravityApi;
import com.craisinlord.anthology.gravity.GravityApi;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.server.level.ServerLevel;
@@ -64,7 +64,7 @@ public class AntimetalScaffoldingBlock extends ScaffoldingBlock {
public BlockState getStateForPlacement(BlockPlaceContext context) {
BlockPos pos = context.getClickedPos();
Level level = context.getLevel();
boolean inverted = context.getPlayer() != null && AntarchyGravityApi.isGravityInverted(context.getPlayer());
boolean inverted = context.getPlayer() != null && GravityApi.isGravityInverted(context.getPlayer());
int distance = getDistance(level, pos, inverted);
if (inverted && distance >= 7) {
return null;
@@ -31,7 +31,7 @@ import net.minecraft.util.RandomSource;
import net.minecraft.world.ticks.TickPriority;
import org.joml.Vector3f;
public class BluestoneComparatorBlock extends ComparatorBlock {
public class BluestoneComparatorBlock extends ComparatorBlock implements BluestoneSignalSource {
private static final VoxelShape SHAPE = Block.box(0.0D, 14.0D, 0.0D, 16.0D, 16.0D, 16.0D);
private static final DustParticleOptions BLUESTONE_PARTICLE = new DustParticleOptions(new Vector3f(0.18F, 0.54F, 1.0F), 1.0F);
private final Supplier<? extends BlockEntityType<BluestoneComparatorBlockEntity>> blockEntityTypeSupplier;
@@ -127,6 +127,11 @@ public class BluestoneComparatorBlock extends ComparatorBlock {
return true;
}
@Override
public int getBluestoneSignal(LevelReader level, BlockPos pos, BlockState state, Direction direction) {
return level == null ? 0 : state.getSignal(level, pos, direction);
}
@Override
public void checkTickOnNeighbor(Level level, BlockPos pos, BlockState state) {
if (level.getBlockTicks().willTickThisTick(pos, this)) {
@@ -19,7 +19,7 @@ import net.minecraft.world.phys.shapes.VoxelShape;
import net.minecraft.util.RandomSource;
import org.joml.Vector3f;
public class BluestoneRepeaterBlock extends RepeaterBlock {
public class BluestoneRepeaterBlock extends RepeaterBlock implements BluestoneSignalSource {
private static final VoxelShape SHAPE = Block.box(0.0D, 14.0D, 0.0D, 16.0D, 16.0D, 16.0D);
private static final DustParticleOptions BLUESTONE_PARTICLE = new DustParticleOptions(new Vector3f(0.18F, 0.54F, 1.0F), 1.0F);
@@ -95,6 +95,11 @@ public class BluestoneRepeaterBlock extends RepeaterBlock {
return true;
}
@Override
public int getBluestoneSignal(LevelReader level, BlockPos pos, BlockState state, Direction direction) {
return level == null ? 0 : state.getSignal(level, pos, direction);
}
@Override
public void animateTick(BlockState state, net.minecraft.world.level.Level level, BlockPos pos, RandomSource random) {
if (!state.getValue(POWERED)) {
@@ -1,11 +1,11 @@
package com.craisinlord.antarchy.content.block;
import com.craisinlord.antarchy.content.entity.royal.KingEntity;
import java.util.Optional;
import java.util.function.Supplier;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
@@ -40,7 +40,8 @@ public final class CloudBlock extends Block implements BucketPickup {
public VoxelShape getCollisionShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) {
if (context instanceof EntityCollisionContext ecc) {
Entity entity = ecc.getEntity();
if (entity instanceof net.minecraft.world.entity.LivingEntity le && le.isShiftKeyDown()) {
if (entity instanceof KingEntity
|| entity instanceof net.minecraft.world.entity.LivingEntity le && le.isShiftKeyDown()) {
return EMPTY;
}
}
@@ -73,6 +74,9 @@ public final class CloudBlock extends Block implements BucketPickup {
}
public void entityInside(BlockState state, Level level, BlockPos pos, Entity entity) {
if (entity instanceof KingEntity) {
return;
}
Vec3 motion = entity.getDeltaMovement();
double slowedY = motion.y < 0.0D ? Math.max(motion.y * 0.4D, -0.08D) : motion.y;
entity.setDeltaMovement(motion.x * 0.92D, slowedY, motion.z * 0.92D);
@@ -0,0 +1,244 @@
package com.craisinlord.antarchy.content.block;
import com.craisinlord.antarchy.content.AntarchyObjects;
import com.craisinlord.antarchy.content.entity.CrawlingBlightEntity;
import com.craisinlord.antarchy.content.block.AmberMossBlock;
import com.craisinlord.antarchy.content.block.SkulkingFrightEggBlock;
import com.craisinlord.antarchy.content.horde.CavarynHordeManager;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.sounds.SoundSource;
import net.minecraft.tags.BlockTags;
import net.minecraft.util.RandomSource;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.ambient.Bat;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.GameRules;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.LevelReader;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.StateDefinition;
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
import net.minecraft.world.level.block.state.properties.IntegerProperty;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.VoxelShape;
public class CrawlingBlightEggBlock extends Block {
public static final IntegerProperty HATCH = BlockStateProperties.HATCH;
public static final IntegerProperty EGGS = BlockStateProperties.EGGS;
private static final float PANIC_HATCH_CHANCE = 0.25F;
private static final double HORDE_ACCELERATION_RADIUS = 5.0D;
private static final int MAX_HATCH = 2;
private static final int MAX_EGGS = 4;
private static final VoxelShape SINGLE_SHAPE = Block.box(3.0D, 0.0D, 3.0D, 13.0D, 7.0D, 13.0D);
private static final VoxelShape MULTI_SHAPE = Block.box(1.0D, 0.0D, 1.0D, 15.0D, 7.0D, 15.0D);
public CrawlingBlightEggBlock(BlockBehaviour.Properties properties) {
super(properties);
this.registerDefaultState(this.stateDefinition.any().setValue(HATCH, 0).setValue(EGGS, 1));
}
@Override
public VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) {
return state.getValue(EGGS) > 1 ? MULTI_SHAPE : SINGLE_SHAPE;
}
@Override
public boolean canSurvive(BlockState state, LevelReader level, BlockPos pos) {
return level.getBlockState(pos.below()).isFaceSturdy(level, pos.below(), Direction.UP);
}
@Override
public BlockState updateShape(BlockState state, Direction direction, BlockState neighborState, LevelAccessor level, BlockPos pos, BlockPos neighborPos) {
return state.canSurvive(level, pos) ? super.updateShape(state, direction, neighborState, level, pos, neighborPos) : Blocks.AIR.defaultBlockState();
}
@Override
public boolean canBeReplaced(BlockState state, BlockPlaceContext context) {
return !context.isSecondaryUseActive()
&& context.getItemInHand().is(this.asItem())
&& state.getValue(EGGS) < MAX_EGGS;
}
@Override
public BlockState getStateForPlacement(BlockPlaceContext context) {
BlockState existingState = context.getLevel().getBlockState(context.getClickedPos());
if (existingState.is(this)) {
return existingState.setValue(EGGS, Math.min(MAX_EGGS, existingState.getValue(EGGS) + 1));
}
return this.defaultBlockState();
}
@Override
public boolean isRandomlyTicking(BlockState state) {
return true;
}
@Override
public void randomTick(BlockState state, ServerLevel level, BlockPos pos, RandomSource random) {
if (!this.shouldUpdateHatchLevel(level, pos)) {
return;
}
int hatch = state.getValue(HATCH);
if (hatch < MAX_HATCH) {
level.setBlock(pos, state.setValue(HATCH, hatch + 1), 2);
level.playSound(null, pos, SoundEvents.TURTLE_EGG_CRACK, SoundSource.BLOCKS, 0.7F, 0.95F + random.nextFloat() * 0.1F);
return;
}
this.hatchEggs(level, pos, state, random, state.getValue(EGGS));
}
@Override
public void stepOn(Level level, BlockPos pos, BlockState state, Entity entity) {
if (entity instanceof Player && !entity.isSteppingCarefully() && this.destroyEgg(level, state, pos, entity, 100)) {
if (level instanceof ServerLevel serverLevel) {
panicHatchNearby(serverLevel, pos);
}
}
super.stepOn(level, pos, state, entity);
}
@Override
public void fallOn(Level level, BlockState state, BlockPos pos, Entity entity, float fallDistance) {
this.destroyEgg(level, state, pos, entity, 3);
super.fallOn(level, state, pos, entity, fallDistance);
}
@Override
public void onPlace(BlockState state, Level level, BlockPos pos, BlockState oldState, boolean isMoving) {
if (hasHatchSupport(level, pos) && !level.isClientSide) {
level.levelEvent(2012, pos, 15);
}
}
@Override
public void playerDestroy(Level level, Player player, BlockPos pos, BlockState state, BlockEntity blockEntity, net.minecraft.world.item.ItemStack stack) {
super.playerDestroy(level, player, pos, state, blockEntity, stack);
this.decreaseEggs(level, pos, state);
}
private static void panicHatchNearby(ServerLevel level, BlockPos origin) {
int radius = 6;
BlockPos.MutableBlockPos mutable = new BlockPos.MutableBlockPos();
for (int dx = -radius; dx <= radius; dx++) {
for (int dy = -radius; dy <= radius; dy++) {
for (int dz = -radius; dz <= radius; dz++) {
if (dx == 0 && dy == 0 && dz == 0) continue;
mutable.set(origin.getX() + dx, origin.getY() + dy, origin.getZ() + dz);
BlockState nearby = level.getBlockState(mutable);
BlockPos immutable = mutable.immutable();
if (nearby.getBlock() instanceof CrawlingBlightEggBlock b) {
b.hatchPanickedEggs(level, immutable, nearby, level.random);
} else if (nearby.getBlock() instanceof SkulkingFrightEggBlock b) {
b.hatchPanickedEggs(level, immutable, nearby, level.random);
}
}
}
}
}
@Override
public void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
builder.add(HATCH, EGGS);
}
public static boolean hasHatchSupport(BlockGetter level, BlockPos pos) {
return isSupportedGround(level, pos.below());
}
public static boolean isSupportedGround(BlockGetter level, BlockPos pos) {
return level.getBlockState(pos).is(BlockTags.SAND) || level.getBlockState(pos).blocksMotion();
}
private boolean destroyEgg(Level level, BlockState state, BlockPos pos, Entity entity, int chance) {
if (level.isClientSide || !this.canDestroyEgg(level, entity) || level.random.nextInt(chance) != 0) {
return false;
}
level.playSound(null, pos, SoundEvents.TURTLE_EGG_BREAK, SoundSource.BLOCKS, 0.7F, 0.9F + level.random.nextFloat() * 0.2F);
this.decreaseEggs(level, pos, state);
return true;
}
void hatchPanickedEggs(ServerLevel level, BlockPos pos, BlockState state, RandomSource random) {
int hatchedEggs = rollPanickedEggs(state.getValue(EGGS), random);
if (hatchedEggs <= 0) {
return;
}
this.hatchEggs(level, pos, state, random, hatchedEggs);
}
void hatchEggs(ServerLevel level, BlockPos pos, BlockState state, RandomSource random, int eggsToHatch) {
if (eggsToHatch >= state.getValue(EGGS)) {
level.removeBlock(pos, false);
} else {
level.setBlock(pos, state.setValue(EGGS, state.getValue(EGGS) - eggsToHatch), 2);
}
level.playSound(null, pos, SoundEvents.TURTLE_EGG_HATCH, SoundSource.BLOCKS, 0.75F, 0.95F + random.nextFloat() * 0.1F);
for (int i = 0; i < eggsToHatch; i++) {
CrawlingBlightEntity entity = AntarchyObjects.CRAWLING_BLIGHT.get().create(level);
if (entity == null) {
continue;
}
double spawnX = pos.getX() + 0.25D + random.nextDouble() * 0.5D;
double spawnZ = pos.getZ() + 0.25D + random.nextDouble() * 0.5D;
entity.moveTo(spawnX, pos.getY() + 0.1D, spawnZ, random.nextFloat() * 360.0F, 0.0F);
level.addFreshEntity(entity);
}
}
public void hatchWithNucleus(ServerLevel level, BlockPos pos, BlockState state) {
this.hatchEggs(level, pos, state, level.random, state.getValue(EGGS));
}
private static int rollPanickedEggs(int eggs, RandomSource random) {
int hatchedEggs = 0;
for (int i = 0; i < eggs; i++) {
if (random.nextFloat() < PANIC_HATCH_CHANCE) {
hatchedEggs++;
}
}
return hatchedEggs;
}
private void decreaseEggs(Level level, BlockPos pos, BlockState state) {
int eggs = state.getValue(EGGS);
if (eggs <= 1) {
level.destroyBlock(pos, false);
return;
}
level.setBlock(pos, state.setValue(EGGS, eggs - 1), 2);
}
private boolean shouldUpdateHatchLevel(Level level, BlockPos pos) {
if (level instanceof ServerLevel serverLevel
&& CavarynHordeManager.shouldAccelerateNearbyEggHatching(serverLevel, pos, HORDE_ACCELERATION_RADIUS, level.random)) {
return true;
}
boolean onAmberMoss = level.getBlockState(pos.below()).getBlock() instanceof AmberMossBlock;
return level.random.nextInt(onAmberMoss ? 150 : 500) == 0;
}
private boolean canDestroyEgg(Level level, Entity entity) {
if (entity instanceof CrawlingBlightEntity || entity instanceof Bat) {
return false;
}
if (entity instanceof net.minecraft.world.entity.LivingEntity) {
return entity instanceof Player || level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING);
}
return false;
}
}
@@ -1,244 +0,0 @@
package com.craisinlord.antarchy.content.block;
import com.craisinlord.antarchy.content.AntarchyObjects;
import com.craisinlord.antarchy.content.entity.CreepingHorrorEntity;
import com.craisinlord.antarchy.content.block.AmberMossBlock;
import com.craisinlord.antarchy.content.block.LurkingTerrorEggBlock;
import com.craisinlord.antarchy.content.horde.CavarynHordeManager;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.sounds.SoundSource;
import net.minecraft.tags.BlockTags;
import net.minecraft.util.RandomSource;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.ambient.Bat;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.GameRules;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.LevelReader;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.StateDefinition;
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
import net.minecraft.world.level.block.state.properties.IntegerProperty;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.VoxelShape;
public class CreepingHorrorEggBlock extends Block {
public static final IntegerProperty HATCH = BlockStateProperties.HATCH;
public static final IntegerProperty EGGS = BlockStateProperties.EGGS;
private static final float PANIC_HATCH_CHANCE = 0.25F;
private static final double HORDE_ACCELERATION_RADIUS = 5.0D;
private static final int MAX_HATCH = 2;
private static final int MAX_EGGS = 4;
private static final VoxelShape SINGLE_SHAPE = Block.box(3.0D, 0.0D, 3.0D, 13.0D, 7.0D, 13.0D);
private static final VoxelShape MULTI_SHAPE = Block.box(1.0D, 0.0D, 1.0D, 15.0D, 7.0D, 15.0D);
public CreepingHorrorEggBlock(BlockBehaviour.Properties properties) {
super(properties);
this.registerDefaultState(this.stateDefinition.any().setValue(HATCH, 0).setValue(EGGS, 1));
}
@Override
public VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) {
return state.getValue(EGGS) > 1 ? MULTI_SHAPE : SINGLE_SHAPE;
}
@Override
public boolean canSurvive(BlockState state, LevelReader level, BlockPos pos) {
return level.getBlockState(pos.below()).isFaceSturdy(level, pos.below(), Direction.UP);
}
@Override
public BlockState updateShape(BlockState state, Direction direction, BlockState neighborState, LevelAccessor level, BlockPos pos, BlockPos neighborPos) {
return state.canSurvive(level, pos) ? super.updateShape(state, direction, neighborState, level, pos, neighborPos) : Blocks.AIR.defaultBlockState();
}
@Override
public boolean canBeReplaced(BlockState state, BlockPlaceContext context) {
return !context.isSecondaryUseActive()
&& context.getItemInHand().is(this.asItem())
&& state.getValue(EGGS) < MAX_EGGS;
}
@Override
public BlockState getStateForPlacement(BlockPlaceContext context) {
BlockState existingState = context.getLevel().getBlockState(context.getClickedPos());
if (existingState.is(this)) {
return existingState.setValue(EGGS, Math.min(MAX_EGGS, existingState.getValue(EGGS) + 1));
}
return this.defaultBlockState();
}
@Override
public boolean isRandomlyTicking(BlockState state) {
return true;
}
@Override
public void randomTick(BlockState state, ServerLevel level, BlockPos pos, RandomSource random) {
if (!this.shouldUpdateHatchLevel(level, pos)) {
return;
}
int hatch = state.getValue(HATCH);
if (hatch < MAX_HATCH) {
level.setBlock(pos, state.setValue(HATCH, hatch + 1), 2);
level.playSound(null, pos, SoundEvents.TURTLE_EGG_CRACK, SoundSource.BLOCKS, 0.7F, 0.95F + random.nextFloat() * 0.1F);
return;
}
this.hatchEggs(level, pos, state, random, state.getValue(EGGS));
}
@Override
public void stepOn(Level level, BlockPos pos, BlockState state, Entity entity) {
if (entity instanceof Player && !entity.isSteppingCarefully() && this.destroyEgg(level, state, pos, entity, 100)) {
if (level instanceof ServerLevel serverLevel) {
panicHatchNearby(serverLevel, pos);
}
}
super.stepOn(level, pos, state, entity);
}
@Override
public void fallOn(Level level, BlockState state, BlockPos pos, Entity entity, float fallDistance) {
this.destroyEgg(level, state, pos, entity, 3);
super.fallOn(level, state, pos, entity, fallDistance);
}
@Override
public void onPlace(BlockState state, Level level, BlockPos pos, BlockState oldState, boolean isMoving) {
if (hasHatchSupport(level, pos) && !level.isClientSide) {
level.levelEvent(2012, pos, 15);
}
}
@Override
public void playerDestroy(Level level, Player player, BlockPos pos, BlockState state, BlockEntity blockEntity, net.minecraft.world.item.ItemStack stack) {
super.playerDestroy(level, player, pos, state, blockEntity, stack);
this.decreaseEggs(level, pos, state);
}
private static void panicHatchNearby(ServerLevel level, BlockPos origin) {
int radius = 6;
BlockPos.MutableBlockPos mutable = new BlockPos.MutableBlockPos();
for (int dx = -radius; dx <= radius; dx++) {
for (int dy = -radius; dy <= radius; dy++) {
for (int dz = -radius; dz <= radius; dz++) {
if (dx == 0 && dy == 0 && dz == 0) continue;
mutable.set(origin.getX() + dx, origin.getY() + dy, origin.getZ() + dz);
BlockState nearby = level.getBlockState(mutable);
BlockPos immutable = mutable.immutable();
if (nearby.getBlock() instanceof CreepingHorrorEggBlock b) {
b.hatchPanickedEggs(level, immutable, nearby, level.random);
} else if (nearby.getBlock() instanceof LurkingTerrorEggBlock b) {
b.hatchPanickedEggs(level, immutable, nearby, level.random);
}
}
}
}
}
@Override
public void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
builder.add(HATCH, EGGS);
}
public static boolean hasHatchSupport(BlockGetter level, BlockPos pos) {
return isSupportedGround(level, pos.below());
}
public static boolean isSupportedGround(BlockGetter level, BlockPos pos) {
return level.getBlockState(pos).is(BlockTags.SAND) || level.getBlockState(pos).blocksMotion();
}
private boolean destroyEgg(Level level, BlockState state, BlockPos pos, Entity entity, int chance) {
if (level.isClientSide || !this.canDestroyEgg(level, entity) || level.random.nextInt(chance) != 0) {
return false;
}
level.playSound(null, pos, SoundEvents.TURTLE_EGG_BREAK, SoundSource.BLOCKS, 0.7F, 0.9F + level.random.nextFloat() * 0.2F);
this.decreaseEggs(level, pos, state);
return true;
}
void hatchPanickedEggs(ServerLevel level, BlockPos pos, BlockState state, RandomSource random) {
int hatchedEggs = rollPanickedEggs(state.getValue(EGGS), random);
if (hatchedEggs <= 0) {
return;
}
this.hatchEggs(level, pos, state, random, hatchedEggs);
}
void hatchEggs(ServerLevel level, BlockPos pos, BlockState state, RandomSource random, int eggsToHatch) {
if (eggsToHatch >= state.getValue(EGGS)) {
level.removeBlock(pos, false);
} else {
level.setBlock(pos, state.setValue(EGGS, state.getValue(EGGS) - eggsToHatch), 2);
}
level.playSound(null, pos, SoundEvents.TURTLE_EGG_HATCH, SoundSource.BLOCKS, 0.75F, 0.95F + random.nextFloat() * 0.1F);
for (int i = 0; i < eggsToHatch; i++) {
CreepingHorrorEntity entity = AntarchyObjects.CREEPING_HORROR.get().create(level);
if (entity == null) {
continue;
}
double spawnX = pos.getX() + 0.25D + random.nextDouble() * 0.5D;
double spawnZ = pos.getZ() + 0.25D + random.nextDouble() * 0.5D;
entity.moveTo(spawnX, pos.getY() + 0.1D, spawnZ, random.nextFloat() * 360.0F, 0.0F);
level.addFreshEntity(entity);
}
}
public void hatchWithNucleus(ServerLevel level, BlockPos pos, BlockState state) {
this.hatchEggs(level, pos, state, level.random, state.getValue(EGGS));
}
private static int rollPanickedEggs(int eggs, RandomSource random) {
int hatchedEggs = 0;
for (int i = 0; i < eggs; i++) {
if (random.nextFloat() < PANIC_HATCH_CHANCE) {
hatchedEggs++;
}
}
return hatchedEggs;
}
private void decreaseEggs(Level level, BlockPos pos, BlockState state) {
int eggs = state.getValue(EGGS);
if (eggs <= 1) {
level.destroyBlock(pos, false);
return;
}
level.setBlock(pos, state.setValue(EGGS, eggs - 1), 2);
}
private boolean shouldUpdateHatchLevel(Level level, BlockPos pos) {
if (level instanceof ServerLevel serverLevel
&& CavarynHordeManager.shouldAccelerateNearbyEggHatching(serverLevel, pos, HORDE_ACCELERATION_RADIUS, level.random)) {
return true;
}
boolean onAmberMoss = level.getBlockState(pos.below()).getBlock() instanceof AmberMossBlock;
return level.random.nextInt(onAmberMoss ? 150 : 500) == 0;
}
private boolean canDestroyEgg(Level level, Entity entity) {
if (entity instanceof CreepingHorrorEntity || entity instanceof Bat) {
return false;
}
if (entity instanceof net.minecraft.world.entity.LivingEntity) {
return entity instanceof Player || level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING);
}
return false;
}
}
@@ -4,6 +4,8 @@ import com.craisinlord.antarchy.content.AntarchyTags;
import com.craisinlord.antarchy.mixins.AccessorFireBlock;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.util.RandomSource;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.FireBlock;
import net.minecraft.world.level.block.state.BlockBehaviour;
@@ -17,6 +19,21 @@ public class DreamFireBlock extends FireBlock {
this.copyVanillaFlammability();
}
@Override
public void tick(BlockState state, ServerLevel level, BlockPos pos, RandomSource random) {
if (level.getBlockState(pos.below()).is(AntarchyTags.Blocks.DREAM_FIRE_BASE_BLOCKS)) {
if (!this.canSurvive(state, level, pos)) {
level.removeBlock(pos, false);
return;
}
level.scheduleTick(pos, this, 30 + random.nextInt(10));
return;
}
super.tick(state, level, pos, random);
}
@Override
public boolean canSurvive(BlockState state, LevelReader level, BlockPos pos) {
return level.getBlockState(pos.below()).is(AntarchyTags.Blocks.DREAM_FIRE_BASE_BLOCKS)
@@ -1,15 +1,18 @@
package com.craisinlord.antarchy.content.block;
import com.craisinlord.antarchy.content.entity.UpwardFallingBlockEntity;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.util.RandomSource;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.FallingBlock;
import net.minecraft.world.level.block.state.BlockBehaviour;
public class DreamSandBlock extends FallingBlock {
@SuppressWarnings("rawtypes")
public DreamSandBlock(BlockBehaviour.Properties properties) {
super(properties);
}
@@ -18,4 +21,30 @@ public class DreamSandBlock extends FallingBlock {
public void fallOn(Level level, BlockState state, BlockPos pos, Entity entity, float fallDistance) {
entity.causeFallDamage(fallDistance, 0.0F, level.damageSources().fall());
}
@Override
public void onPlace(BlockState state, Level level, BlockPos pos, BlockState oldState, boolean movedByPiston) {
if (!level.isClientSide) {
level.scheduleTick(pos, this, 2);
}
}
@Override
public BlockState updateShape(BlockState state, Direction direction, BlockState neighborState, LevelAccessor level, BlockPos pos, BlockPos neighborPos) {
level.scheduleTick(pos, this, 2);
return state;
}
@Override
public void tick(BlockState state, ServerLevel level, BlockPos pos, RandomSource random) {
if (!level.getBlockState(pos).is(this)) {
return;
}
BlockPos abovePos = pos.above();
BlockState above = level.getBlockState(abovePos);
boolean passable = above.isAir() || above.getCollisionShape(level, abovePos).isEmpty();
if (passable) {
UpwardFallingBlockEntity.fallUp(level, pos, state, false);
}
}
}
@@ -5,23 +5,14 @@ import com.craisinlord.antarchy.content.advancement.AntarchyAdvancements;
import com.craisinlord.antarchy.content.AntarchyObjects;
import com.craisinlord.antarchy.content.AntarchySoundEvents;
import com.craisinlord.antarchy.content.AntarchyTags;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.sounds.SoundSource;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.item.ItemEntity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.DiggerItem;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.ShearsItem;
import net.minecraft.world.item.SwordItem;
import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.Level;
@@ -37,9 +28,7 @@ import net.minecraft.world.level.block.state.properties.BlockStateProperties;
import net.minecraft.world.level.block.state.properties.DirectionProperty;
import net.minecraft.world.level.block.state.properties.EnumProperty;
import net.minecraft.world.level.block.state.properties.IntegerProperty;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.Vec3;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.Shapes;
import net.minecraft.world.phys.shapes.VoxelShape;
@@ -52,24 +41,6 @@ public class DuctTapeBlock extends Block {
public static final DirectionProperty FACING = BlockStateProperties.HORIZONTAL_FACING;
private static final int TAPE_HEIGHT = 2;
private static final long STICK_SOUND_COOLDOWN_TICKS = 10L;
private static final long TAPE_DECAY_INTERVAL_TICKS = 30L;
private static final double FLOOR_TRIGGER_HEIGHT = 0.30D;
private static final double WALL_TRIGGER_DEPTH = 0.25D;
private static final double TRIGGER_PAD = 0.12D;
private static final double WALL_Y_PAD = 0.12D;
private static final double WALL_SIDE_PAD = 0.08D;
private static final double WALL_INNER_INSET = 0.03D;
private static final double TRIGGER_INFLATE_DEFAULT = 0.15D;
private static final int SEARCH_INTERVAL_TICKS = 4;
private static final double SEARCH_MOTION_THRESHOLD_SQR = 1.0E-8D;
private static final double SEARCH_PAD_XZ = Math.max(WALL_TRIGGER_DEPTH, TRIGGER_PAD);
private static final double SEARCH_PAD_Y = FLOOR_TRIGGER_HEIGHT;
private static final Map<UUID, Long> STICK_SOUND_TIMES = new HashMap<>();
private static final Map<UUID, String> STICK_CONTACT_KEYS = new HashMap<>();
private static final Map<String, Long> TAPE_DECAY_TIMES = new HashMap<>();
private static final Map<UUID, StuckContact> STUCK_ENTITIES = new HashMap<>();
public DuctTapeBlock(BlockBehaviour.Properties properties) {
super(properties);
@@ -131,14 +102,6 @@ public class DuctTapeBlock extends Block {
return state.canSurvive(context.getLevel(), context.getClickedPos()) ? state : null;
}
@Override
public void onRemove(BlockState state, Level level, BlockPos pos, BlockState newState, boolean movedByPiston) {
if (!state.is(newState.getBlock())) {
releaseEntitiesForTape(level, pos);
}
super.onRemove(state, level, pos, newState, movedByPiston);
}
@Override
public InteractionResult use(
BlockState state,
@@ -153,14 +116,6 @@ public class DuctTapeBlock extends Block {
return InteractionResult.PASS;
}
if (isReleaseTool(stack) && (isStuckToTape(player) || isTouchingTape(level, player))) {
if (!level.isClientSide) {
consumeTapeUse(level, pos, state);
level.playSound(null, pos, SoundEvents.WOOL_BREAK, SoundSource.BLOCKS, 0.8F, 0.9F + level.random.nextFloat() * 0.2F);
}
return InteractionResult.sidedSuccess(level.isClientSide);
}
if (!stack.isDamageableItem() || !stack.isDamaged() || stack.is(AntarchyTags.Items.DUCT_TAPE_BLACKLIST)) {
return InteractionResult.PASS;
}
@@ -187,24 +142,6 @@ public class DuctTapeBlock extends Block {
return InteractionResult.sidedSuccess(level.isClientSide);
}
@Override
public void entityInside(BlockState state, Level level, BlockPos pos, Entity entity) {
if (level.isClientSide || entity.isSpectator() || !touchesTape(level, entity, pos, state)) {
return;
}
rememberStuckContact(level, pos, entity);
if (!(entity instanceof ItemEntity)) {
maybeConsumeTapeDecay(level, pos, state);
}
long gameTime = level.getGameTime();
String tapeKey = tapeKey(level, pos);
if (shouldPlayStickSound(entity.getUUID(), tapeKey, gameTime)) {
level.playSound(null, pos, AntarchySoundEvents.DUCT_TAPE_USE.get(), SoundSource.BLOCKS, 0.85F, 0.9F + level.random.nextFloat() * 0.15F);
}
applyStick(state, pos, entity);
}
@Override
public void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
builder.add(USES, FACE, FACING);
@@ -237,12 +174,6 @@ public class DuctTapeBlock extends Block {
};
}
private static boolean isReleaseTool(ItemStack stack) {
return stack.getItem() instanceof DiggerItem
|| stack.getItem() instanceof SwordItem
|| stack.getItem() instanceof ShearsItem;
}
private static void consumeTapeUse(Level level, BlockPos pos, BlockState state) {
int remainingUses = state.getValue(USES) - 1;
if (remainingUses > 0) {
@@ -250,322 +181,6 @@ public class DuctTapeBlock extends Block {
return;
}
releaseEntitiesForTape(level, pos);
TAPE_DECAY_TIMES.remove(tapeDecayKey(level, pos));
level.removeBlock(pos, false);
}
private static void removeTape(Level level, BlockPos pos, BlockState state) {
releaseEntitiesForTape(level, pos);
TAPE_DECAY_TIMES.remove(tapeDecayKey(level, pos));
level.removeBlock(pos, false);
}
private static void maybeConsumeTapeDecay(Level level, BlockPos pos, BlockState state) {
long gameTime = level.getGameTime();
String key = tapeDecayKey(level, pos);
Long lastConsumed = TAPE_DECAY_TIMES.get(key);
if (lastConsumed == null) {
TAPE_DECAY_TIMES.put(key, gameTime);
return;
}
if (gameTime - lastConsumed < TAPE_DECAY_INTERVAL_TICKS) {
return;
}
TAPE_DECAY_TIMES.put(key, gameTime);
consumeTapeUse(level, pos, state);
}
public static void tickStuckEntity(Entity entity) {
if (entity == null || entity.level().isClientSide() || isStickyBlacklist(entity)) {
return;
}
if (!entity.isAlive() || entity.isRemoved()) {
releaseEntity(entity);
return;
}
StuckContact contact = STUCK_ENTITIES.get(entity.getUUID());
if (contact != null && isContactValid(entity.level(), contact)) {
BlockPos pos = BlockPos.of(contact.blockPos());
BlockState state = entity.level().getBlockState(pos);
applyStick(state, pos, entity);
if (!(entity instanceof ItemEntity)) {
maybeConsumeTapeDecay(entity.level(), pos, state);
}
return;
}
if (contact != null) {
releaseEntity(entity);
}
if (!shouldSearchForTape(entity)) {
return;
}
TapeHit hit = findTouchedTape(entity.level(), entity);
if (hit == null) {
if (entity.isNoGravity()) {
entity.setNoGravity(false);
}
return;
}
rememberStuckContact(entity.level(), hit.pos(), entity);
applyStick(hit.state(), hit.pos(), entity);
if (!(entity instanceof ItemEntity)) {
maybeConsumeTapeDecay(entity.level(), hit.pos(), hit.state());
}
}
public static boolean isStuckToTape(Entity entity) {
if (entity == null || entity.level().isClientSide() || isStickyBlacklist(entity)) {
return false;
}
StuckContact contact = STUCK_ENTITIES.get(entity.getUUID());
return contact != null && isContactValid(entity.level(), contact);
}
public static boolean isTouchingTape(Entity entity) {
return entity != null && isTouchingTape(entity.level(), entity);
}
public static boolean isTouchingTape(Level level, Entity entity) {
if (isStickyBlacklist(entity)) {
return false;
}
return findTouchedTape(level, entity) != null;
}
public static boolean shouldTickStuckEntity(Entity entity) {
if (entity == null || entity.level().isClientSide() || isStickyBlacklist(entity)) {
return false;
}
if (!entity.isAlive() || entity.isRemoved()) {
return true;
}
StuckContact contact = STUCK_ENTITIES.get(entity.getUUID());
if (contact != null) {
return true;
}
return shouldSearchForTape(entity);
}
private static boolean isStickyBlacklist(Entity entity) {
return entity != null && entity.getType().is(AntarchyTags.Entities.DUCT_TAPE_STICK_BLACKLIST);
}
private static void applyStick(BlockState state, BlockPos pos, Entity entity) {
entity.setDeltaMovement(Vec3.ZERO);
entity.resetFallDistance();
entity.hasImpulse = true;
entity.setNoGravity(true);
Vec3 anchor = getTapeAnchor(state, pos, entity);
entity.setPos(anchor.x, anchor.y, anchor.z);
if (state.getValue(FACE) == AttachFace.FLOOR) {
entity.setOnGround(true);
}
}
private static void rememberStuckContact(Level level, BlockPos pos, Entity entity) {
UUID entityId = entity.getUUID();
String key = tapeKey(level, pos);
StuckContact existing = STUCK_ENTITIES.get(entityId);
if (existing != null && existing.tapeKey().equals(key)) {
return;
}
double offsetX = entity.getX() - pos.getX();
double offsetY = entity.getY() - pos.getY();
double offsetZ = entity.getZ() - pos.getZ();
STUCK_ENTITIES.put(entityId, new StuckContact(
key,
level.dimension().location().toString(),
pos.asLong(),
clamp(offsetX, 0.05D, 0.95D),
clamp(offsetY, 0.0D, 0.98D),
clamp(offsetZ, 0.05D, 0.95D)
));
}
private static Vec3 getTapeAnchor(BlockState state, BlockPos pos, Entity entity) {
StuckContact contact = STUCK_ENTITIES.get(entity.getUUID());
double offsetX = contact != null ? contact.offsetX() : 0.5D;
double offsetY = contact != null ? contact.offsetY() : 0.0D;
double offsetZ = contact != null ? contact.offsetZ() : 0.5D;
double wallOffset = entity instanceof Player ? 0.06D : -0.28D;
double centerX = pos.getX() + 0.5D;
double centerZ = pos.getZ() + 0.5D;
return switch (state.getValue(FACE)) {
case FLOOR -> new Vec3(
pos.getX() + offsetX,
pos.getY() + 0.01D,
pos.getZ() + offsetZ
);
case WALL -> switch (state.getValue(FACING)) {
case NORTH -> new Vec3(centerX, pos.getY() + offsetY, pos.getZ() - wallOffset);
case SOUTH -> new Vec3(centerX, pos.getY() + offsetY, pos.getZ() + 1.0D + wallOffset);
case EAST -> new Vec3(pos.getX() + 1.0D + wallOffset, pos.getY() + offsetY, centerZ);
case WEST -> new Vec3(pos.getX() - wallOffset, pos.getY() + offsetY, centerZ);
default -> new Vec3(centerX, pos.getY() + offsetY, centerZ);
};
default -> new Vec3(pos.getX() + offsetX, pos.getY() + 0.01D, pos.getZ() + offsetZ);
};
}
private static boolean isContactValid(Level level, StuckContact contact) {
if (!level.dimension().location().toString().equals(contact.dimensionId())) {
return false;
}
BlockPos pos = BlockPos.of(contact.blockPos());
BlockState state = level.getBlockState(pos);
return state.getBlock() instanceof DuctTapeBlock
&& tapeKey(level, pos).equals(contact.tapeKey());
}
private static boolean shouldSearchForTape(Entity entity) {
Vec3 velocity = entity.getDeltaMovement();
if (velocity.lengthSqr() < SEARCH_MOTION_THRESHOLD_SQR) {
return false;
}
return (entity.tickCount + entity.getId()) % SEARCH_INTERVAL_TICKS == 0;
}
private static TapeHit findTouchedTape(Level level, Entity entity) {
AABB box = entity.getBoundingBox().inflate(SEARCH_PAD_XZ, SEARCH_PAD_Y, SEARCH_PAD_XZ);
BlockPos min = BlockPos.containing(box.minX, box.minY, box.minZ);
BlockPos max = BlockPos.containing(box.maxX, box.maxY, box.maxZ);
if (!level.hasChunksAt(min, max)) {
return null;
}
BlockPos.MutableBlockPos cursor = new BlockPos.MutableBlockPos();
for (int x = min.getX(); x <= max.getX(); x++) {
for (int y = min.getY(); y <= max.getY(); y++) {
for (int z = min.getZ(); z <= max.getZ(); z++) {
cursor.set(x, y, z);
BlockState state = level.getBlockState(cursor);
if (state.isAir()) {
continue;
}
if (state.getBlock() instanceof DuctTapeBlock && touchesTape(level, entity, cursor, state)) {
return new TapeHit(cursor.immutable(), state);
}
}
}
}
return null;
}
private static boolean touchesTape(Level level, Entity entity, BlockPos pos, BlockState state) {
return entity.getBoundingBox().intersects(getStickyTriggerBounds(state, pos));
}
private static AABB getStickyTriggerBounds(BlockState state, BlockPos pos) {
AABB bounds = getShapeFor(state).bounds().move(pos);
return switch (state.getValue(FACE)) {
case FLOOR -> new AABB(
bounds.minX - TRIGGER_PAD,
pos.getY() - 0.02D,
bounds.minZ - TRIGGER_PAD,
bounds.maxX + TRIGGER_PAD,
pos.getY() + FLOOR_TRIGGER_HEIGHT,
bounds.maxZ + TRIGGER_PAD
);
case WALL -> {
yield switch (state.getValue(FACING)) {
case NORTH -> new AABB(bounds.minX - WALL_SIDE_PAD, bounds.minY - WALL_Y_PAD, bounds.minZ - WALL_TRIGGER_DEPTH, bounds.maxX + WALL_SIDE_PAD, bounds.maxY + WALL_Y_PAD, bounds.maxZ + WALL_INNER_INSET);
case SOUTH -> new AABB(bounds.minX - WALL_SIDE_PAD, bounds.minY - WALL_Y_PAD, bounds.minZ - WALL_INNER_INSET, bounds.maxX + WALL_SIDE_PAD, bounds.maxY + WALL_Y_PAD, bounds.maxZ + WALL_TRIGGER_DEPTH);
case EAST -> new AABB(bounds.minX - WALL_TRIGGER_DEPTH, bounds.minY - WALL_Y_PAD, bounds.minZ - WALL_SIDE_PAD, bounds.maxX + WALL_INNER_INSET, bounds.maxY + WALL_Y_PAD, bounds.maxZ + WALL_SIDE_PAD);
case WEST -> new AABB(bounds.minX - WALL_INNER_INSET, bounds.minY - WALL_Y_PAD, bounds.minZ - WALL_SIDE_PAD, bounds.maxX + WALL_TRIGGER_DEPTH, bounds.maxY + WALL_Y_PAD, bounds.maxZ + WALL_SIDE_PAD);
default -> bounds.inflate(TRIGGER_INFLATE_DEFAULT);
};
}
default -> bounds.inflate(TRIGGER_INFLATE_DEFAULT);
};
}
private static void releaseEntitiesForTape(Level level, BlockPos pos) {
String key = tapeKey(level, pos);
AABB searchBox = AABB.unitCubeFromLowerCorner(Vec3.atLowerCornerOf(pos)).inflate(2.0D);
STUCK_ENTITIES.entrySet().removeIf(entry -> {
StuckContact contact = entry.getValue();
if (!key.equals(contact.tapeKey())) {
return false;
}
level.getEntitiesOfClass(Entity.class, searchBox, candidate -> candidate.getUUID().equals(entry.getKey()))
.forEach(candidate -> candidate.setNoGravity(false));
STICK_SOUND_TIMES.remove(entry.getKey());
STICK_CONTACT_KEYS.remove(entry.getKey());
return true;
});
}
private static void releaseEntity(Entity entity) {
if (entity == null) {
return;
}
STUCK_ENTITIES.remove(entity.getUUID());
STICK_SOUND_TIMES.remove(entity.getUUID());
STICK_CONTACT_KEYS.remove(entity.getUUID());
entity.setNoGravity(false);
}
private static String tapeDecayKey(Level level, BlockPos pos) {
return level.dimension().location() + ":" + pos.asLong();
}
private static String tapeKey(Level level, BlockPos pos) {
return level.dimension().location() + ":" + pos.asLong();
}
private static boolean shouldPlayStickSound(UUID entityId, String tapeKey, long gameTime) {
String lastKey = STICK_CONTACT_KEYS.get(entityId);
Long lastContact = STICK_SOUND_TIMES.get(entityId);
if (lastKey == null || !tapeKey.equals(lastKey)) {
STICK_CONTACT_KEYS.put(entityId, tapeKey);
STICK_SOUND_TIMES.put(entityId, gameTime);
return true;
}
if (lastContact == null || gameTime - lastContact > STICK_SOUND_COOLDOWN_TICKS) {
STICK_SOUND_TIMES.put(entityId, gameTime);
return true;
}
return false;
}
private static double clamp(double value, double min, double max) {
return Math.max(min, Math.min(max, value));
}
private record StuckContact(
String tapeKey,
String dimensionId,
long blockPos,
double offsetX,
double offsetY,
double offsetZ
) {
}
private record TapeHit(BlockPos pos, BlockState state) {
}
}
@@ -25,7 +25,6 @@ public final class DuplicatorTreeLogic {
private static final int LOG_SEARCH_BELOW = 5;
private static final int LOG_SEARCH_ABOVE = 1;
// Cache center log pos per chunk. WeakHashMap lets levels be GC'd when unloaded.
private static final WeakHashMap<ServerLevel, HashMap<Long, BlockPos>> CENTER_LOG_CACHE = new WeakHashMap<>();
private DuplicatorTreeLogic() {
@@ -0,0 +1,76 @@
package com.craisinlord.antarchy.content.block;
import net.minecraft.core.BlockPos;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.Vec3;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.EntityCollisionContext;
import net.minecraft.world.phys.shapes.Shapes;
import net.minecraft.world.phys.shapes.VoxelShape;
public class FlytrapGooBlock extends Block {
private static final double BOUNCE_VELOCITY_THRESHOLD = 0.6;
private static final double MAX_SINK_SPEED = 0.05;
private static final double SPRINT_VELOCITY_THRESHOLD = 0.1;
public FlytrapGooBlock(BlockBehaviour.Properties properties) {
super(properties);
}
public boolean isStickyBlock(BlockState state) {
return true;
}
public boolean canStickTo(BlockState state, BlockState other) {
return !other.is(Blocks.SLIME_BLOCK) && !other.is(Blocks.HONEY_BLOCK);
}
@Override
public VoxelShape getCollisionShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) {
if (context instanceof EntityCollisionContext ecc) {
Entity entity = ecc.getEntity();
if (entity != null) {
Vec3 delta = entity.getDeltaMovement();
double vy = delta.y;
double hSpeed = Math.sqrt(delta.x * delta.x + delta.z * delta.z);
if (vy <= 0.0 && vy > -BOUNCE_VELOCITY_THRESHOLD && hSpeed < SPRINT_VELOCITY_THRESHOLD) {
return Shapes.empty();
}
}
}
return super.getCollisionShape(state, level, pos, context);
}
@Override
public void entityInside(BlockState state, Level level, BlockPos pos, Entity entity) {
Vec3 delta = entity.getDeltaMovement();
if (delta.y < -MAX_SINK_SPEED) {
entity.setDeltaMovement(delta.x * 0.9, -MAX_SINK_SPEED, delta.z * 0.9);
} else {
entity.setDeltaMovement(delta.x * 0.9, delta.y, delta.z * 0.9);
}
entity.fallDistance = 0.0F;
}
@Override
public void fallOn(Level level, BlockState state, BlockPos pos, Entity entity, float fallDistance) {
entity.causeFallDamage(fallDistance, 0.0F, level.damageSources().fall());
}
@Override
public void updateEntityAfterFallOn(BlockGetter level, Entity entity) {
Vec3 delta = entity.getDeltaMovement();
if (delta.y < 0.0) {
double coefficient = entity instanceof LivingEntity ? 1.0 : 0.8;
entity.setDeltaMovement(delta.x, -delta.y * coefficient, delta.z);
}
}
}
@@ -19,7 +19,6 @@ import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.properties.DoubleBlockHalf;
import net.minecraft.world.phys.BlockHitResult;
/** A two-block-tall, light-emitting reed found around Lumen pools. */
public final class GlimmeringReedBlock extends DoublePlantBlock implements BonemealableBlock {
public GlimmeringReedBlock(BlockBehaviour.Properties properties) {
@@ -1,160 +0,0 @@
package com.craisinlord.antarchy.content.block;
import com.craisinlord.antarchy.Antarchy;
import com.craisinlord.antarchy.content.entity.JumpyBugEntity;
import com.craisinlord.antarchy.content.horde.CavarynHordeManager;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.EntityType;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.sounds.SoundSource;
import net.minecraft.util.RandomSource;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.LevelReader;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.Mirror;
import net.minecraft.world.level.block.Rotation;
import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.StateDefinition;
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
import net.minecraft.world.level.block.state.properties.BooleanProperty;
import net.minecraft.world.level.block.state.properties.IntegerProperty;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.VoxelShape;
public class JumpyBugEggBlock extends Block {
public static final IntegerProperty HATCH = BlockStateProperties.HATCH;
public static final BooleanProperty HANGING = BlockStateProperties.HANGING;
public static final BooleanProperty ROTATED = BooleanProperty.create("rotated");
private static final int MAX_HATCH = 2;
private static final double PLAYER_PROXIMITY_RADIUS = 12.0D;
private static final double HORDE_ACCELERATION_RADIUS = 5.0D;
private static final float PLAYER_PROXIMITY_HATCH_CHANCE = 0.25F;
private static final int PLAYER_PROXIMITY_CHECK_INTERVAL = 200;
private static final VoxelShape FLOOR_SHAPE = Block.box(1.5D, 0.0D, 1.5D, 14.5D, 16.0D, 14.5D);
private static final VoxelShape CEILING_SHAPE = Block.box(1.5D, 0.0D, 1.5D, 14.5D, 16.0D, 14.5D);
private static final ResourceLocation JUMPY_BUG_ID = new ResourceLocation(Antarchy.MODID, "jumpy_bug");
public JumpyBugEggBlock(BlockBehaviour.Properties properties) {
super(properties);
this.registerDefaultState(this.stateDefinition.any().setValue(HATCH, 0).setValue(HANGING, false).setValue(ROTATED, false));
}
@Override
public VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) {
return state.getValue(HANGING) ? CEILING_SHAPE : FLOOR_SHAPE;
}
@Override
public BlockState getStateForPlacement(BlockPlaceContext context) {
boolean hanging = context.getClickedFace() == Direction.DOWN
|| (context.getClickedFace() != Direction.UP && !canSupportFloor(context.getLevel(), context.getClickedPos()) && canSupportCeiling(context.getLevel(), context.getClickedPos()));
boolean rotated = context.getHorizontalDirection().getAxis() == Direction.Axis.X;
return this.defaultBlockState().setValue(HANGING, hanging).setValue(ROTATED, rotated);
}
@Override
public BlockState rotate(BlockState state, Rotation rotation) {
return rotation == Rotation.CLOCKWISE_90 || rotation == Rotation.COUNTERCLOCKWISE_90
? state.setValue(ROTATED, !state.getValue(ROTATED))
: state;
}
@Override
public BlockState mirror(BlockState state, Mirror mirror) {
return state;
}
@Override
public boolean canSurvive(BlockState state, LevelReader level, BlockPos pos) {
return state.getValue(HANGING) ? canSupportCeiling(level, pos) : canSupportFloor(level, pos);
}
@Override
public BlockState updateShape(BlockState state, Direction direction, BlockState neighborState, LevelAccessor level, BlockPos pos, BlockPos neighborPos) {
return state.canSurvive(level, pos) ? super.updateShape(state, direction, neighborState, level, pos, neighborPos) : Blocks.AIR.defaultBlockState();
}
private static boolean canSupportFloor(LevelReader level, BlockPos pos) {
return level.getBlockState(pos.below()).isFaceSturdy(level, pos.below(), Direction.UP);
}
private static boolean canSupportCeiling(LevelReader level, BlockPos pos) {
return level.getBlockState(pos.above()).isFaceSturdy(level, pos.above(), Direction.DOWN);
}
@Override
public boolean isRandomlyTicking(BlockState state) {
return true;
}
@Override
public void randomTick(BlockState state, ServerLevel level, BlockPos pos, RandomSource random) {
if (!this.shouldUpdateHatchLevel(level, pos, state)) {
return;
}
int hatch = state.getValue(HATCH);
if (hatch < MAX_HATCH) {
level.setBlock(pos, state.setValue(HATCH, hatch + 1), 2);
level.playSound(null, pos, SoundEvents.TURTLE_EGG_CRACK, SoundSource.BLOCKS, 0.7F, 0.95F + random.nextFloat() * 0.1F);
return;
}
this.hatch(level, pos, random);
}
private boolean shouldUpdateHatchLevel(Level level, BlockPos pos, BlockState state) {
if (level instanceof ServerLevel serverLevel
&& CavarynHordeManager.shouldAccelerateNearbyEggHatching(serverLevel, pos, HORDE_ACCELERATION_RADIUS, level.random)) {
return true;
}
if (level.random.nextInt(PLAYER_PROXIMITY_CHECK_INTERVAL) == 0) {
Player nearbyPlayer = level.getNearestPlayer(pos.getX() + 0.5D, pos.getY() + 0.5D, pos.getZ() + 0.5D, PLAYER_PROXIMITY_RADIUS, false);
if (nearbyPlayer != null && level.random.nextFloat() < PLAYER_PROXIMITY_HATCH_CHANCE) {
return true;
}
}
BlockPos checkPos = state.getValue(HANGING) ? pos.above() : pos.below();
boolean onAmberMoss = level.getBlockState(checkPos).getBlock() instanceof AmberMossBlock;
return level.random.nextInt(onAmberMoss ? 150 : 500) == 0;
}
private void hatch(ServerLevel level, BlockPos pos, RandomSource random) {
level.removeBlock(pos, false);
level.playSound(null, pos, SoundEvents.TURTLE_EGG_HATCH, SoundSource.BLOCKS, 0.75F, 0.95F + random.nextFloat() * 0.1F);
@SuppressWarnings("unchecked")
EntityType<JumpyBugEntity> jumpyBugType = (EntityType<JumpyBugEntity>) (EntityType<?>) BuiltInRegistries.ENTITY_TYPE.getOptional(JUMPY_BUG_ID).orElse(null);
if (jumpyBugType == null) {
return;
}
JumpyBugEntity entity = jumpyBugType.create(level);
if (entity == null) {
return;
}
double spawnX = pos.getX() + 0.25D + random.nextDouble() * 0.5D;
double spawnZ = pos.getZ() + 0.25D + random.nextDouble() * 0.5D;
entity.moveTo(spawnX, pos.getY() + 0.1D, spawnZ, random.nextFloat() * 360.0F, 0.0F);
level.addFreshEntity(entity);
}
public void hatchWithNucleus(ServerLevel level, BlockPos pos) {
this.hatch(level, pos, level.random);
}
@Override
public void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
builder.add(HATCH, HANGING, ROTATED);
}
}
@@ -1,244 +0,0 @@
package com.craisinlord.antarchy.content.block;
import com.craisinlord.antarchy.content.AntarchyObjects;
import com.craisinlord.antarchy.content.entity.LurkingTerrorEntity;
import com.craisinlord.antarchy.content.block.AmberMossBlock;
import com.craisinlord.antarchy.content.block.CreepingHorrorEggBlock;
import com.craisinlord.antarchy.content.horde.CavarynHordeManager;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.sounds.SoundSource;
import net.minecraft.tags.BlockTags;
import net.minecraft.util.RandomSource;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.ambient.Bat;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.GameRules;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.LevelReader;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.StateDefinition;
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
import net.minecraft.world.level.block.state.properties.IntegerProperty;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.VoxelShape;
public class LurkingTerrorEggBlock extends Block {
public static final IntegerProperty HATCH = BlockStateProperties.HATCH;
public static final IntegerProperty EGGS = BlockStateProperties.EGGS;
private static final float PANIC_HATCH_CHANCE = 0.25F;
private static final double HORDE_ACCELERATION_RADIUS = 5.0D;
private static final int MAX_HATCH = 2;
private static final int MAX_EGGS = 4;
private static final VoxelShape SINGLE_SHAPE = Block.box(3.0D, 0.0D, 3.0D, 13.0D, 7.0D, 13.0D);
private static final VoxelShape MULTI_SHAPE = Block.box(1.0D, 0.0D, 1.0D, 15.0D, 7.0D, 15.0D);
public LurkingTerrorEggBlock(BlockBehaviour.Properties properties) {
super(properties);
this.registerDefaultState(this.stateDefinition.any().setValue(HATCH, 0).setValue(EGGS, 1));
}
@Override
public VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) {
return state.getValue(EGGS) > 1 ? MULTI_SHAPE : SINGLE_SHAPE;
}
@Override
public boolean canSurvive(BlockState state, LevelReader level, BlockPos pos) {
return level.getBlockState(pos.below()).isFaceSturdy(level, pos.below(), Direction.UP);
}
@Override
public BlockState updateShape(BlockState state, Direction direction, BlockState neighborState, LevelAccessor level, BlockPos pos, BlockPos neighborPos) {
return state.canSurvive(level, pos) ? super.updateShape(state, direction, neighborState, level, pos, neighborPos) : Blocks.AIR.defaultBlockState();
}
@Override
public boolean canBeReplaced(BlockState state, BlockPlaceContext context) {
return !context.isSecondaryUseActive()
&& context.getItemInHand().is(this.asItem())
&& state.getValue(EGGS) < MAX_EGGS;
}
@Override
public BlockState getStateForPlacement(BlockPlaceContext context) {
BlockState existingState = context.getLevel().getBlockState(context.getClickedPos());
if (existingState.is(this)) {
return existingState.setValue(EGGS, Math.min(MAX_EGGS, existingState.getValue(EGGS) + 1));
}
return this.defaultBlockState();
}
@Override
public boolean isRandomlyTicking(BlockState state) {
return true;
}
@Override
public void randomTick(BlockState state, ServerLevel level, BlockPos pos, RandomSource random) {
if (!this.shouldUpdateHatchLevel(level, pos)) {
return;
}
int hatch = state.getValue(HATCH);
if (hatch < MAX_HATCH) {
level.setBlock(pos, state.setValue(HATCH, hatch + 1), 2);
level.playSound(null, pos, SoundEvents.TURTLE_EGG_CRACK, SoundSource.BLOCKS, 0.7F, 0.95F + random.nextFloat() * 0.1F);
return;
}
this.hatchEggs(level, pos, state, random, state.getValue(EGGS));
}
@Override
public void stepOn(Level level, BlockPos pos, BlockState state, Entity entity) {
if (entity instanceof Player && !entity.isSteppingCarefully() && this.destroyEgg(level, state, pos, entity, 100)) {
if (level instanceof ServerLevel serverLevel) {
panicHatchNearby(serverLevel, pos);
}
}
super.stepOn(level, pos, state, entity);
}
@Override
public void fallOn(Level level, BlockState state, BlockPos pos, Entity entity, float fallDistance) {
this.destroyEgg(level, state, pos, entity, 3);
super.fallOn(level, state, pos, entity, fallDistance);
}
@Override
public void onPlace(BlockState state, Level level, BlockPos pos, BlockState oldState, boolean isMoving) {
if (hasHatchSupport(level, pos) && !level.isClientSide) {
level.levelEvent(2012, pos, 15);
}
}
@Override
public void playerDestroy(Level level, Player player, BlockPos pos, BlockState state, BlockEntity blockEntity, net.minecraft.world.item.ItemStack stack) {
super.playerDestroy(level, player, pos, state, blockEntity, stack);
this.decreaseEggs(level, pos, state);
}
private static void panicHatchNearby(ServerLevel level, BlockPos origin) {
int radius = 6;
BlockPos.MutableBlockPos mutable = new BlockPos.MutableBlockPos();
for (int dx = -radius; dx <= radius; dx++) {
for (int dy = -radius; dy <= radius; dy++) {
for (int dz = -radius; dz <= radius; dz++) {
if (dx == 0 && dy == 0 && dz == 0) continue;
mutable.set(origin.getX() + dx, origin.getY() + dy, origin.getZ() + dz);
BlockState nearby = level.getBlockState(mutable);
BlockPos immutable = mutable.immutable();
if (nearby.getBlock() instanceof LurkingTerrorEggBlock b) {
b.hatchPanickedEggs(level, immutable, nearby, level.random);
} else if (nearby.getBlock() instanceof CreepingHorrorEggBlock b) {
b.hatchPanickedEggs(level, immutable, nearby, level.random);
}
}
}
}
}
@Override
public void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
builder.add(HATCH, EGGS);
}
public static boolean hasHatchSupport(BlockGetter level, BlockPos pos) {
return isSupportedGround(level, pos.below());
}
public static boolean isSupportedGround(BlockGetter level, BlockPos pos) {
return level.getBlockState(pos).is(BlockTags.SAND) || level.getBlockState(pos).blocksMotion();
}
private boolean destroyEgg(Level level, BlockState state, BlockPos pos, Entity entity, int chance) {
if (level.isClientSide || !this.canDestroyEgg(level, entity) || level.random.nextInt(chance) != 0) {
return false;
}
level.playSound(null, pos, SoundEvents.TURTLE_EGG_BREAK, SoundSource.BLOCKS, 0.7F, 0.9F + level.random.nextFloat() * 0.2F);
this.decreaseEggs(level, pos, state);
return true;
}
void hatchPanickedEggs(ServerLevel level, BlockPos pos, BlockState state, RandomSource random) {
int hatchedEggs = rollPanickedEggs(state.getValue(EGGS), random);
if (hatchedEggs <= 0) {
return;
}
this.hatchEggs(level, pos, state, random, hatchedEggs);
}
void hatchEggs(ServerLevel level, BlockPos pos, BlockState state, RandomSource random, int eggsToHatch) {
if (eggsToHatch >= state.getValue(EGGS)) {
level.removeBlock(pos, false);
} else {
level.setBlock(pos, state.setValue(EGGS, state.getValue(EGGS) - eggsToHatch), 2);
}
level.playSound(null, pos, SoundEvents.TURTLE_EGG_HATCH, SoundSource.BLOCKS, 0.75F, 0.95F + random.nextFloat() * 0.1F);
for (int i = 0; i < eggsToHatch; i++) {
LurkingTerrorEntity entity = AntarchyObjects.LURKING_TERROR.get().create(level);
if (entity == null) {
continue;
}
double spawnX = pos.getX() + 0.25D + random.nextDouble() * 0.5D;
double spawnZ = pos.getZ() + 0.25D + random.nextDouble() * 0.5D;
entity.moveTo(spawnX, pos.getY() + 0.1D, spawnZ, random.nextFloat() * 360.0F, 0.0F);
level.addFreshEntity(entity);
}
}
public void hatchWithNucleus(ServerLevel level, BlockPos pos, BlockState state) {
this.hatchEggs(level, pos, state, level.random, state.getValue(EGGS));
}
private static int rollPanickedEggs(int eggs, RandomSource random) {
int hatchedEggs = 0;
for (int i = 0; i < eggs; i++) {
if (random.nextFloat() < PANIC_HATCH_CHANCE) {
hatchedEggs++;
}
}
return hatchedEggs;
}
private void decreaseEggs(Level level, BlockPos pos, BlockState state) {
int eggs = state.getValue(EGGS);
if (eggs <= 1) {
level.destroyBlock(pos, false);
return;
}
level.setBlock(pos, state.setValue(EGGS, eggs - 1), 2);
}
private boolean shouldUpdateHatchLevel(Level level, BlockPos pos) {
if (level instanceof ServerLevel serverLevel
&& CavarynHordeManager.shouldAccelerateNearbyEggHatching(serverLevel, pos, HORDE_ACCELERATION_RADIUS, level.random)) {
return true;
}
boolean onAmberMoss = level.getBlockState(pos.below()).getBlock() instanceof AmberMossBlock;
return level.random.nextInt(onAmberMoss ? 150 : 500) == 0;
}
private boolean canDestroyEgg(Level level, Entity entity) {
if (entity instanceof LurkingTerrorEntity || entity instanceof Bat) {
return false;
}
if (entity instanceof net.minecraft.world.entity.LivingEntity) {
return entity instanceof Player || level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING);
}
return false;
}
}
@@ -1,5 +1,6 @@
package com.craisinlord.antarchy.content.block;
import com.craisinlord.antarchy.content.entity.WormEntity;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.world.entity.Entity;
@@ -16,9 +17,9 @@ import net.minecraft.world.phys.shapes.VoxelShape;
public class MucusBlock extends GlowLichenBlock {
private static final VoxelShape DOWN_COLLISION_SHAPE = Shapes.box(0.0D, 0.0D, 0.0D, 1.0D, 0.0625D, 1.0D);
private static final double SLIDE_ACCELERATION = 0.025D;
private static final double SLIDE_MAX_SPEED = 0.6D;
private static final double SLIDE_MIN_SPEED = 0.02D;
private static final double SLIDE_ACCELERATION = 0.045D;
private static final double SLIDE_MAX_SPEED = 1.2D;
private static final double SLIDE_MIN_SPEED = 0.01D;
public MucusBlock(BlockBehaviour.Properties properties) {
super(properties);
@@ -30,9 +31,12 @@ public class MucusBlock extends GlowLichenBlock {
}
@Override
public void entityInside(BlockState state, Level level, BlockPos pos, Entity entity) {
super.entityInside(state, level, pos, entity);
if (level.isClientSide || !state.getValue(MultifaceBlock.getFaceProperty(Direction.DOWN))) {
public void stepOn(Level level, BlockPos pos, BlockState state, Entity entity) {
super.stepOn(level, pos, state, entity);
if (level.isClientSide
|| entity instanceof WormEntity
|| entity.isShiftKeyDown()
|| !state.getValue(MultifaceBlock.getFaceProperty(Direction.DOWN))) {
return;
}
@@ -1,20 +1,35 @@
package com.craisinlord.antarchy.content.block;
import com.craisinlord.antarchy.Antarchy;
import com.craisinlord.antarchy.content.worldgen.thoraxis.NadirTreeGrowers;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.tags.BlockTags;
import net.minecraft.core.Holder;
import net.minecraft.core.registries.Registries;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.tags.TagKey;
import net.minecraft.util.RandomSource;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.LevelReader;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.BonemealableBlock;
import net.minecraft.world.level.block.BushBlock;
import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.levelgen.feature.ConfiguredFeature;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.VoxelShape;
public class NadirSaplingBlock extends BushBlock {
private static final VoxelShape SHAPE = Block.box(2.0D, 0.0D, 2.0D, 14.0D, 12.0D, 14.0D);
public class NadirSaplingBlock extends BushBlock implements BonemealableBlock {
private static final TagKey<Block> SUPPORTS = TagKey.create(
Registries.BLOCK,
new ResourceLocation(Antarchy.MODID, "nadir_sapling_supports")
);
private static final VoxelShape SHAPE = Block.box(2.0D, 4.0D, 2.0D, 14.0D, 16.0D, 14.0D);
public NadirSaplingBlock(BlockBehaviour.Properties properties) {
super(properties);
@@ -27,7 +42,21 @@ public class NadirSaplingBlock extends BushBlock {
@Override
public boolean mayPlaceOn(BlockState state, BlockGetter level, BlockPos pos) {
return state.is(BlockTags.DIRT) || state.is(Blocks.FARMLAND);
return state.is(SUPPORTS);
}
@Override
public boolean canSurvive(BlockState state, LevelReader level, BlockPos pos) {
BlockPos supportPos = pos.above();
BlockState supportState = level.getBlockState(supportPos);
return supportState.is(SUPPORTS) && supportState.isFaceSturdy(level, supportPos, Direction.DOWN);
}
@Override
public void randomTick(BlockState state, ServerLevel level, BlockPos pos, RandomSource random) {
if (random.nextInt(7) == 0) {
this.growConfiguredTree(level, pos, state, random);
}
}
@Override
@@ -43,4 +72,32 @@ public class NadirSaplingBlock extends BushBlock {
? super.updateShape(state, direction, neighborState, level, pos, neighborPos)
: Blocks.AIR.defaultBlockState();
}
@Override
public boolean isValidBonemealTarget(LevelReader level, BlockPos pos, BlockState state, boolean isClient) {
return true;
}
@Override
public boolean isBonemealSuccess(Level level, RandomSource random, BlockPos pos, BlockState state) {
return random.nextFloat() < 0.45F;
}
@Override
public void performBonemeal(ServerLevel level, RandomSource random, BlockPos pos, BlockState state) {
this.growConfiguredTree(level, pos, state, random);
}
private void growConfiguredTree(ServerLevel level, BlockPos pos, BlockState state, RandomSource random) {
Holder.Reference<ConfiguredFeature<?, ?>> feature = level.registryAccess()
.lookupOrThrow(Registries.CONFIGURED_FEATURE)
.getOrThrow(NadirTreeGrowers.NADIR_TREE);
level.removeBlock(pos, false);
if (feature.value().place(level, level.getChunkSource().getGenerator(), random, pos)) {
return;
}
level.setBlock(pos, state, Block.UPDATE_ALL);
}
}
@@ -23,12 +23,6 @@ import net.minecraft.world.level.material.Fluids;
public class OuranwoodLeavesBlock extends LeavesBlock implements BonemealableBlock {
/*
* We still include vanilla LeavesBlock.DISTANCE in the state definition because
* LeavesBlock's constructor sets it.
*
* This is the real Ouranwood decay distance.
*/
public static final IntegerProperty OURANWOOD_DISTANCE = IntegerProperty.create("ouranwood_distance", 1, 17);
public static final BooleanProperty PERSISTENT = LeavesBlock.PERSISTENT;
@@ -13,12 +13,6 @@ import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.StateDefinition;
import net.minecraft.world.level.block.state.properties.IntegerProperty;
/**
* Invisible world-gen-only marker placed inside a flying squirrel hollow.
* Schedules itself to replace with COARSE_DIRT and spawn squirrels shortly
* after the chunk loads, so entity creation never blocks the gen thread.
* No BlockItem is registered, so this block never appears in creative or JEI.
*/
public class OuranwoodSquirrelNestBlock extends Block {
public static final IntegerProperty SQUIRREL_COUNT = IntegerProperty.create("squirrels", 1, 3);
@@ -14,7 +14,6 @@ import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.world.level.block.state.BlockState;
/** Invisible worldgen marker which activates the Queen at a generated trail terminus. */
public final class QueenTrailSpawnMarkerBlock extends BaseEntityBlock {
private final Supplier<? extends BlockEntityType<QueenTrailSpawnMarkerBlockEntity>> type;
@@ -3,6 +3,7 @@ package com.craisinlord.antarchy.content.block;
import com.craisinlord.antarchy.config.AntarchySettings;
import com.craisinlord.antarchy.content.block.entity.RoyalEggBlockEntity;
import com.craisinlord.antarchy.content.entity.royal.RoyalMountEntity;
import com.craisinlord.antarchy.content.worldgen.thoraxis.ThoraxisUndersideManager;
import java.util.UUID;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
@@ -18,6 +19,7 @@ import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.MobSpawnType;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.world.level.Level;
import net.minecraft.world.entity.item.FallingBlockEntity;
import net.minecraft.world.level.LevelAccessor;
@@ -30,6 +32,7 @@ import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.StateDefinition;
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
import net.minecraft.world.level.block.state.properties.BooleanProperty;
import net.minecraft.world.level.block.state.properties.IntegerProperty;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.phys.shapes.CollisionContext;
@@ -39,6 +42,7 @@ import org.jetbrains.annotations.Nullable;
public abstract class RoyalEggBlock extends BaseEntityBlock {
public static final IntegerProperty HATCH = BlockStateProperties.HATCH;
public static final BooleanProperty INVERTED = BooleanProperty.create("inverted");
private static final int MAX_HATCH = 2;
private static final int FALL_DELAY = 2;
@@ -49,16 +53,23 @@ public abstract class RoyalEggBlock extends BaseEntityBlock {
Block.box(5.0D, 14.0D, 5.0D, 11.0D, 15.0D, 11.0D),
Block.box(6.0D, 15.0D, 6.0D, 10.0D, 16.0D, 10.0D));
public RoyalEggBlock(Properties properties) {
private static final VoxelShape INVERTED_SHAPE = Shapes.or(
Block.box(3.0D, 3.0D, 3.0D, 13.0D, 16.0D, 13.0D),
Block.box(2.0D, 5.0D, 2.0D, 14.0D, 15.0D, 14.0D),
Block.box(4.0D, 2.0D, 4.0D, 12.0D, 3.0D, 12.0D),
Block.box(5.0D, 1.0D, 5.0D, 11.0D, 2.0D, 11.0D),
Block.box(6.0D, 0.0D, 6.0D, 10.0D, 1.0D, 10.0D));
protected RoyalEggBlock(Properties properties) {
super(properties);
this.registerDefaultState(this.stateDefinition.any().setValue(HATCH, 0));
this.registerDefaultState(this.stateDefinition.any().setValue(HATCH, 0).setValue(INVERTED, false));
}
public abstract EntityType<? extends RoyalMountEntity> mountType();
protected abstract EntityType<? extends RoyalMountEntity> mountType();
@Override
public void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
builder.add(HATCH);
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
builder.add(HATCH, INVERTED);
}
@Override
@@ -68,7 +79,16 @@ public abstract class RoyalEggBlock extends BaseEntityBlock {
@Override
public VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) {
return SHAPE;
return state.getValue(INVERTED) ? INVERTED_SHAPE : SHAPE;
}
@Override
public BlockState getStateForPlacement(BlockPlaceContext context) {
return this.defaultBlockState().setValue(INVERTED, isInvertedGravityAt(context.getLevel(), context.getClickedPos()));
}
private static boolean isInvertedGravityAt(Level level, BlockPos pos) {
return ThoraxisUndersideManager.isThoraxis(level) && pos.getY() < ThoraxisUndersideManager.GRAVITY_FLIP_Y;
}
@Nullable
@@ -105,7 +125,8 @@ public abstract class RoyalEggBlock extends BaseEntityBlock {
UUID placer = source == null ? null : source.getPlacerUuid();
int hatch = state.getValue(HATCH);
level.setBlock(pos, Blocks.AIR.defaultBlockState(), 2);
level.setBlock(target, state.setValue(HATCH, Math.max(0, hatch - 1)), 2);
level.setBlock(target, state.setValue(HATCH, Math.max(0, hatch - 1))
.setValue(INVERTED, isInvertedGravityAt(level, target)), 2);
if (level.getBlockEntity(target) instanceof RoyalEggBlockEntity moved) {
moved.setPlacerUuid(placer);
}
@@ -129,6 +150,12 @@ public abstract class RoyalEggBlock extends BaseEntityBlock {
@Override
public void tick(BlockState state, ServerLevel level, BlockPos pos, RandomSource random) {
boolean inverted = state.getValue(INVERTED);
if (inverted) {
tickInvertedFall(state, level, pos);
return;
}
if (!FallingBlock.isFree(level.getBlockState(pos.below())) || pos.getY() < level.getMinBuildHeight()) {
return;
}
@@ -141,6 +168,21 @@ public abstract class RoyalEggBlock extends BaseEntityBlock {
}
}
private void tickInvertedFall(BlockState state, ServerLevel level, BlockPos pos) {
BlockPos above = pos.above();
if (!FallingBlock.isFree(level.getBlockState(above)) || above.getY() > level.getMaxBuildHeight()) {
return;
}
UUID placer = level.getBlockEntity(pos) instanceof RoyalEggBlockEntity egg ? egg.getPlacerUuid() : null;
level.removeBlock(pos, false);
level.setBlock(above, state, Block.UPDATE_ALL);
if (level.getBlockEntity(above) instanceof RoyalEggBlockEntity moved) {
moved.setPlacerUuid(placer);
}
level.scheduleTick(above, this, FALL_DELAY);
}
@Override
public boolean isRandomlyTicking(BlockState state) {
return true;
@@ -170,7 +212,7 @@ public abstract class RoyalEggBlock extends BaseEntityBlock {
return;
}
mount.moveTo(pos.getX() + 0.5D, pos.getY(), pos.getZ() + 0.5D, level.random.nextFloat() * 360.0F, 0.0F);
mount.setBaby(true);
mount.setGrowthProgress(0.0F);
mount.finalizeSpawn(level, level.getCurrentDifficultyAt(pos), MobSpawnType.BREEDING, null, null);
if (placer != null) {
mount.setTame(true);
@@ -0,0 +1,244 @@
package com.craisinlord.antarchy.content.block;
import com.craisinlord.antarchy.content.AntarchyObjects;
import com.craisinlord.antarchy.content.entity.SkulkingFrightEntity;
import com.craisinlord.antarchy.content.block.AmberMossBlock;
import com.craisinlord.antarchy.content.block.CrawlingBlightEggBlock;
import com.craisinlord.antarchy.content.horde.CavarynHordeManager;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.sounds.SoundSource;
import net.minecraft.tags.BlockTags;
import net.minecraft.util.RandomSource;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.ambient.Bat;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.GameRules;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.LevelReader;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.StateDefinition;
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
import net.minecraft.world.level.block.state.properties.IntegerProperty;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.VoxelShape;
public class SkulkingFrightEggBlock extends Block {
public static final IntegerProperty HATCH = BlockStateProperties.HATCH;
public static final IntegerProperty EGGS = BlockStateProperties.EGGS;
private static final float PANIC_HATCH_CHANCE = 0.25F;
private static final double HORDE_ACCELERATION_RADIUS = 5.0D;
private static final int MAX_HATCH = 2;
private static final int MAX_EGGS = 4;
private static final VoxelShape SINGLE_SHAPE = Block.box(3.0D, 0.0D, 3.0D, 13.0D, 7.0D, 13.0D);
private static final VoxelShape MULTI_SHAPE = Block.box(1.0D, 0.0D, 1.0D, 15.0D, 7.0D, 15.0D);
public SkulkingFrightEggBlock(BlockBehaviour.Properties properties) {
super(properties);
this.registerDefaultState(this.stateDefinition.any().setValue(HATCH, 0).setValue(EGGS, 1));
}
@Override
public VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) {
return state.getValue(EGGS) > 1 ? MULTI_SHAPE : SINGLE_SHAPE;
}
@Override
public boolean canSurvive(BlockState state, LevelReader level, BlockPos pos) {
return level.getBlockState(pos.below()).isFaceSturdy(level, pos.below(), Direction.UP);
}
@Override
public BlockState updateShape(BlockState state, Direction direction, BlockState neighborState, LevelAccessor level, BlockPos pos, BlockPos neighborPos) {
return state.canSurvive(level, pos) ? super.updateShape(state, direction, neighborState, level, pos, neighborPos) : Blocks.AIR.defaultBlockState();
}
@Override
public boolean canBeReplaced(BlockState state, BlockPlaceContext context) {
return !context.isSecondaryUseActive()
&& context.getItemInHand().is(this.asItem())
&& state.getValue(EGGS) < MAX_EGGS;
}
@Override
public BlockState getStateForPlacement(BlockPlaceContext context) {
BlockState existingState = context.getLevel().getBlockState(context.getClickedPos());
if (existingState.is(this)) {
return existingState.setValue(EGGS, Math.min(MAX_EGGS, existingState.getValue(EGGS) + 1));
}
return this.defaultBlockState();
}
@Override
public boolean isRandomlyTicking(BlockState state) {
return true;
}
@Override
public void randomTick(BlockState state, ServerLevel level, BlockPos pos, RandomSource random) {
if (!this.shouldUpdateHatchLevel(level, pos)) {
return;
}
int hatch = state.getValue(HATCH);
if (hatch < MAX_HATCH) {
level.setBlock(pos, state.setValue(HATCH, hatch + 1), 2);
level.playSound(null, pos, SoundEvents.TURTLE_EGG_CRACK, SoundSource.BLOCKS, 0.7F, 0.95F + random.nextFloat() * 0.1F);
return;
}
this.hatchEggs(level, pos, state, random, state.getValue(EGGS));
}
@Override
public void stepOn(Level level, BlockPos pos, BlockState state, Entity entity) {
if (entity instanceof Player && !entity.isSteppingCarefully() && this.destroyEgg(level, state, pos, entity, 100)) {
if (level instanceof ServerLevel serverLevel) {
panicHatchNearby(serverLevel, pos);
}
}
super.stepOn(level, pos, state, entity);
}
@Override
public void fallOn(Level level, BlockState state, BlockPos pos, Entity entity, float fallDistance) {
this.destroyEgg(level, state, pos, entity, 3);
super.fallOn(level, state, pos, entity, fallDistance);
}
@Override
public void onPlace(BlockState state, Level level, BlockPos pos, BlockState oldState, boolean isMoving) {
if (hasHatchSupport(level, pos) && !level.isClientSide) {
level.levelEvent(2012, pos, 15);
}
}
@Override
public void playerDestroy(Level level, Player player, BlockPos pos, BlockState state, BlockEntity blockEntity, net.minecraft.world.item.ItemStack stack) {
super.playerDestroy(level, player, pos, state, blockEntity, stack);
this.decreaseEggs(level, pos, state);
}
private static void panicHatchNearby(ServerLevel level, BlockPos origin) {
int radius = 6;
BlockPos.MutableBlockPos mutable = new BlockPos.MutableBlockPos();
for (int dx = -radius; dx <= radius; dx++) {
for (int dy = -radius; dy <= radius; dy++) {
for (int dz = -radius; dz <= radius; dz++) {
if (dx == 0 && dy == 0 && dz == 0) continue;
mutable.set(origin.getX() + dx, origin.getY() + dy, origin.getZ() + dz);
BlockState nearby = level.getBlockState(mutable);
BlockPos immutable = mutable.immutable();
if (nearby.getBlock() instanceof SkulkingFrightEggBlock b) {
b.hatchPanickedEggs(level, immutable, nearby, level.random);
} else if (nearby.getBlock() instanceof CrawlingBlightEggBlock b) {
b.hatchPanickedEggs(level, immutable, nearby, level.random);
}
}
}
}
}
@Override
public void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
builder.add(HATCH, EGGS);
}
public static boolean hasHatchSupport(BlockGetter level, BlockPos pos) {
return isSupportedGround(level, pos.below());
}
public static boolean isSupportedGround(BlockGetter level, BlockPos pos) {
return level.getBlockState(pos).is(BlockTags.SAND) || level.getBlockState(pos).blocksMotion();
}
private boolean destroyEgg(Level level, BlockState state, BlockPos pos, Entity entity, int chance) {
if (level.isClientSide || !this.canDestroyEgg(level, entity) || level.random.nextInt(chance) != 0) {
return false;
}
level.playSound(null, pos, SoundEvents.TURTLE_EGG_BREAK, SoundSource.BLOCKS, 0.7F, 0.9F + level.random.nextFloat() * 0.2F);
this.decreaseEggs(level, pos, state);
return true;
}
void hatchPanickedEggs(ServerLevel level, BlockPos pos, BlockState state, RandomSource random) {
int hatchedEggs = rollPanickedEggs(state.getValue(EGGS), random);
if (hatchedEggs <= 0) {
return;
}
this.hatchEggs(level, pos, state, random, hatchedEggs);
}
void hatchEggs(ServerLevel level, BlockPos pos, BlockState state, RandomSource random, int eggsToHatch) {
if (eggsToHatch >= state.getValue(EGGS)) {
level.removeBlock(pos, false);
} else {
level.setBlock(pos, state.setValue(EGGS, state.getValue(EGGS) - eggsToHatch), 2);
}
level.playSound(null, pos, SoundEvents.TURTLE_EGG_HATCH, SoundSource.BLOCKS, 0.75F, 0.95F + random.nextFloat() * 0.1F);
for (int i = 0; i < eggsToHatch; i++) {
SkulkingFrightEntity entity = AntarchyObjects.SKULKING_FRIGHT.get().create(level);
if (entity == null) {
continue;
}
double spawnX = pos.getX() + 0.25D + random.nextDouble() * 0.5D;
double spawnZ = pos.getZ() + 0.25D + random.nextDouble() * 0.5D;
entity.moveTo(spawnX, pos.getY() + 0.1D, spawnZ, random.nextFloat() * 360.0F, 0.0F);
level.addFreshEntity(entity);
}
}
public void hatchWithNucleus(ServerLevel level, BlockPos pos, BlockState state) {
this.hatchEggs(level, pos, state, level.random, state.getValue(EGGS));
}
private static int rollPanickedEggs(int eggs, RandomSource random) {
int hatchedEggs = 0;
for (int i = 0; i < eggs; i++) {
if (random.nextFloat() < PANIC_HATCH_CHANCE) {
hatchedEggs++;
}
}
return hatchedEggs;
}
private void decreaseEggs(Level level, BlockPos pos, BlockState state) {
int eggs = state.getValue(EGGS);
if (eggs <= 1) {
level.destroyBlock(pos, false);
return;
}
level.setBlock(pos, state.setValue(EGGS, eggs - 1), 2);
}
private boolean shouldUpdateHatchLevel(Level level, BlockPos pos) {
if (level instanceof ServerLevel serverLevel
&& CavarynHordeManager.shouldAccelerateNearbyEggHatching(serverLevel, pos, HORDE_ACCELERATION_RADIUS, level.random)) {
return true;
}
boolean onAmberMoss = level.getBlockState(pos.below()).getBlock() instanceof AmberMossBlock;
return level.random.nextInt(onAmberMoss ? 150 : 500) == 0;
}
private boolean canDestroyEgg(Level level, Entity entity) {
if (entity instanceof SkulkingFrightEntity || entity instanceof Bat) {
return false;
}
if (entity instanceof net.minecraft.world.entity.LivingEntity) {
return entity instanceof Player || level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING);
}
return false;
}
}
@@ -0,0 +1,160 @@
package com.craisinlord.antarchy.content.block;
import com.craisinlord.antarchy.Antarchy;
import com.craisinlord.antarchy.content.entity.SpringbugEntity;
import com.craisinlord.antarchy.content.horde.CavarynHordeManager;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.EntityType;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.sounds.SoundSource;
import net.minecraft.util.RandomSource;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.LevelReader;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.Mirror;
import net.minecraft.world.level.block.Rotation;
import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.StateDefinition;
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
import net.minecraft.world.level.block.state.properties.BooleanProperty;
import net.minecraft.world.level.block.state.properties.IntegerProperty;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.VoxelShape;
public class SpringbugEggBlock extends Block {
public static final IntegerProperty HATCH = BlockStateProperties.HATCH;
public static final BooleanProperty HANGING = BlockStateProperties.HANGING;
public static final BooleanProperty ROTATED = BooleanProperty.create("rotated");
private static final int MAX_HATCH = 2;
private static final double PLAYER_PROXIMITY_RADIUS = 12.0D;
private static final double HORDE_ACCELERATION_RADIUS = 5.0D;
private static final float PLAYER_PROXIMITY_HATCH_CHANCE = 0.25F;
private static final int PLAYER_PROXIMITY_CHECK_INTERVAL = 200;
private static final VoxelShape FLOOR_SHAPE = Block.box(1.5D, 0.0D, 1.5D, 14.5D, 16.0D, 14.5D);
private static final VoxelShape CEILING_SHAPE = Block.box(1.5D, 0.0D, 1.5D, 14.5D, 16.0D, 14.5D);
private static final ResourceLocation SPRINGBUG_ID = new ResourceLocation(Antarchy.MODID, "springbug");
public SpringbugEggBlock(BlockBehaviour.Properties properties) {
super(properties);
this.registerDefaultState(this.stateDefinition.any().setValue(HATCH, 0).setValue(HANGING, false).setValue(ROTATED, false));
}
@Override
public VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) {
return state.getValue(HANGING) ? CEILING_SHAPE : FLOOR_SHAPE;
}
@Override
public BlockState getStateForPlacement(BlockPlaceContext context) {
boolean hanging = context.getClickedFace() == Direction.DOWN
|| (context.getClickedFace() != Direction.UP && !canSupportFloor(context.getLevel(), context.getClickedPos()) && canSupportCeiling(context.getLevel(), context.getClickedPos()));
boolean rotated = context.getHorizontalDirection().getAxis() == Direction.Axis.X;
return this.defaultBlockState().setValue(HANGING, hanging).setValue(ROTATED, rotated);
}
@Override
public BlockState rotate(BlockState state, Rotation rotation) {
return rotation == Rotation.CLOCKWISE_90 || rotation == Rotation.COUNTERCLOCKWISE_90
? state.setValue(ROTATED, !state.getValue(ROTATED))
: state;
}
@Override
public BlockState mirror(BlockState state, Mirror mirror) {
return state;
}
@Override
public boolean canSurvive(BlockState state, LevelReader level, BlockPos pos) {
return state.getValue(HANGING) ? canSupportCeiling(level, pos) : canSupportFloor(level, pos);
}
@Override
public BlockState updateShape(BlockState state, Direction direction, BlockState neighborState, LevelAccessor level, BlockPos pos, BlockPos neighborPos) {
return state.canSurvive(level, pos) ? super.updateShape(state, direction, neighborState, level, pos, neighborPos) : Blocks.AIR.defaultBlockState();
}
private static boolean canSupportFloor(LevelReader level, BlockPos pos) {
return level.getBlockState(pos.below()).isFaceSturdy(level, pos.below(), Direction.UP);
}
private static boolean canSupportCeiling(LevelReader level, BlockPos pos) {
return level.getBlockState(pos.above()).isFaceSturdy(level, pos.above(), Direction.DOWN);
}
@Override
public boolean isRandomlyTicking(BlockState state) {
return true;
}
@Override
public void randomTick(BlockState state, ServerLevel level, BlockPos pos, RandomSource random) {
if (!this.shouldUpdateHatchLevel(level, pos, state)) {
return;
}
int hatch = state.getValue(HATCH);
if (hatch < MAX_HATCH) {
level.setBlock(pos, state.setValue(HATCH, hatch + 1), 2);
level.playSound(null, pos, SoundEvents.TURTLE_EGG_CRACK, SoundSource.BLOCKS, 0.7F, 0.95F + random.nextFloat() * 0.1F);
return;
}
this.hatch(level, pos, random);
}
private boolean shouldUpdateHatchLevel(Level level, BlockPos pos, BlockState state) {
if (level instanceof ServerLevel serverLevel
&& CavarynHordeManager.shouldAccelerateNearbyEggHatching(serverLevel, pos, HORDE_ACCELERATION_RADIUS, level.random)) {
return true;
}
if (level.random.nextInt(PLAYER_PROXIMITY_CHECK_INTERVAL) == 0) {
Player nearbyPlayer = level.getNearestPlayer(pos.getX() + 0.5D, pos.getY() + 0.5D, pos.getZ() + 0.5D, PLAYER_PROXIMITY_RADIUS, false);
if (nearbyPlayer != null && level.random.nextFloat() < PLAYER_PROXIMITY_HATCH_CHANCE) {
return true;
}
}
BlockPos checkPos = state.getValue(HANGING) ? pos.above() : pos.below();
boolean onAmberMoss = level.getBlockState(checkPos).getBlock() instanceof AmberMossBlock;
return level.random.nextInt(onAmberMoss ? 150 : 500) == 0;
}
private void hatch(ServerLevel level, BlockPos pos, RandomSource random) {
level.removeBlock(pos, false);
level.playSound(null, pos, SoundEvents.TURTLE_EGG_HATCH, SoundSource.BLOCKS, 0.75F, 0.95F + random.nextFloat() * 0.1F);
@SuppressWarnings("unchecked")
EntityType<SpringbugEntity> springbugType = (EntityType<SpringbugEntity>) (EntityType<?>) BuiltInRegistries.ENTITY_TYPE.getOptional(SPRINGBUG_ID).orElse(null);
if (springbugType == null) {
return;
}
SpringbugEntity entity = springbugType.create(level);
if (entity == null) {
return;
}
double spawnX = pos.getX() + 0.25D + random.nextDouble() * 0.5D;
double spawnZ = pos.getZ() + 0.25D + random.nextDouble() * 0.5D;
entity.moveTo(spawnX, pos.getY() + 0.1D, spawnZ, random.nextFloat() * 360.0F, 0.0F);
level.addFreshEntity(entity);
}
public void hatchWithNucleus(ServerLevel level, BlockPos pos) {
this.hatch(level, pos, level.random);
}
@Override
public void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
builder.add(HATCH, HANGING, ROTATED);
}
}
@@ -1,76 +0,0 @@
package com.craisinlord.antarchy.content.block;
import net.minecraft.core.BlockPos;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.Vec3;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.EntityCollisionContext;
import net.minecraft.world.phys.shapes.Shapes;
import net.minecraft.world.phys.shapes.VoxelShape;
public class TriffidGooBlock extends Block {
private static final double BOUNCE_VELOCITY_THRESHOLD = 0.6;
private static final double MAX_SINK_SPEED = 0.05;
private static final double SPRINT_VELOCITY_THRESHOLD = 0.1;
public TriffidGooBlock(BlockBehaviour.Properties properties) {
super(properties);
}
public boolean isStickyBlock(BlockState state) {
return true;
}
public boolean canStickTo(BlockState state, BlockState other) {
return !other.is(Blocks.SLIME_BLOCK) && !other.is(Blocks.HONEY_BLOCK);
}
@Override
public VoxelShape getCollisionShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) {
if (context instanceof EntityCollisionContext ecc) {
Entity entity = ecc.getEntity();
if (entity != null) {
Vec3 delta = entity.getDeltaMovement();
double vy = delta.y;
double hSpeed = Math.sqrt(delta.x * delta.x + delta.z * delta.z);
if (vy <= 0.0 && vy > -BOUNCE_VELOCITY_THRESHOLD && hSpeed < SPRINT_VELOCITY_THRESHOLD) {
return Shapes.empty();
}
}
}
return super.getCollisionShape(state, level, pos, context);
}
@Override
public void entityInside(BlockState state, Level level, BlockPos pos, Entity entity) {
Vec3 delta = entity.getDeltaMovement();
if (delta.y < -MAX_SINK_SPEED) {
entity.setDeltaMovement(delta.x * 0.9, -MAX_SINK_SPEED, delta.z * 0.9);
} else {
entity.setDeltaMovement(delta.x * 0.9, delta.y, delta.z * 0.9);
}
entity.fallDistance = 0.0F;
}
@Override
public void fallOn(Level level, BlockState state, BlockPos pos, Entity entity, float fallDistance) {
entity.causeFallDamage(fallDistance, 0.0F, level.damageSources().fall());
}
@Override
public void updateEntityAfterFallOn(BlockGetter level, Entity entity) {
Vec3 delta = entity.getDeltaMovement();
if (delta.y < 0.0) {
double coefficient = entity instanceof LivingEntity ? 1.0 : 0.8;
entity.setDeltaMovement(delta.x, -delta.y * coefficient, delta.z);
}
}
}
@@ -1,19 +1,24 @@
package com.craisinlord.antarchy.content.block;
import com.craisinlord.antarchy.Antarchy;
import com.craisinlord.antarchy.content.block.entity.UnderVaultBlockEntity;
import com.craisinlord.antarchy.content.block.entity.undervault.UnderVaultEffects;
import com.craisinlord.antarchy.content.block.entity.undervault.UnderVaultLogic;
import com.craisinlord.antarchy.content.block.entity.undervault.UnderVaultState;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.BaseEntityBlock;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.HorizontalDirectionalBlock;
import net.minecraft.world.level.block.Mirror;
import net.minecraft.world.level.block.RenderShape;
import net.minecraft.world.level.block.Rotation;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityTicker;
import net.minecraft.world.level.block.entity.BlockEntityType;
@@ -22,33 +27,58 @@ import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.StateDefinition;
import net.minecraft.world.level.block.state.properties.BooleanProperty;
import net.minecraft.world.level.block.state.properties.DirectionProperty;
import net.minecraft.world.level.block.state.properties.EnumProperty;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.resources.ResourceLocation;
import java.util.function.Supplier;
public final class UnderVaultBlock extends BaseEntityBlock {
public static final DirectionProperty FACING = DirectionProperty.create("facing");
public static final BooleanProperty ACTIVE = BooleanProperty.create("active");
public static final EnumProperty<UnderVaultState> STATE = EnumProperty.create("vault_state", UnderVaultState.class);
public static final DirectionProperty FACING = HorizontalDirectionalBlock.FACING;
public static final BooleanProperty OMINOUS = BooleanProperty.create("ominous");
public static final DirectionProperty UNDER_FACING = DirectionProperty.create("under_facing", Direction.values());
private final Supplier<? extends BlockEntityType<UnderVaultBlockEntity>> blockEntityType;
public UnderVaultBlock(Supplier<? extends BlockEntityType<UnderVaultBlockEntity>> blockEntityType,
BlockBehaviour.Properties properties) {
super(properties);
public UnderVaultBlock(Supplier<? extends BlockEntityType<UnderVaultBlockEntity>> blockEntityType, BlockBehaviour.Properties properties) {
super(properties.lightLevel(UnderVaultBlock::lightLevel));
this.blockEntityType = blockEntityType;
registerDefaultState(stateDefinition.any().setValue(FACING, Direction.DOWN).setValue(ACTIVE, true));
this.registerDefaultState(this.stateDefinition.any()
.setValue(FACING, Direction.NORTH)
.setValue(STATE, UnderVaultState.INACTIVE)
.setValue(OMINOUS, false)
.setValue(UNDER_FACING, Direction.NORTH));
}
public static int lightLevel(BlockState state) {
return state.getValue(STATE).lightLevel();
}
@Override
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
builder.add(FACING, ACTIVE);
builder.add(FACING, STATE, OMINOUS);
builder.add(UNDER_FACING);
}
@Override
public BlockState getStateForPlacement(net.minecraft.world.item.context.BlockPlaceContext context) {
return context.getClickedFace() == Direction.DOWN
? defaultBlockState().setValue(FACING, Direction.DOWN)
: null;
public BlockState getStateForPlacement(BlockPlaceContext context) {
if (context.getClickedFace() != Direction.DOWN) {
return null;
}
return this.defaultBlockState()
.setValue(FACING, context.getHorizontalDirection().getOpposite())
.setValue(UNDER_FACING, Direction.DOWN);
}
@Override
public BlockState rotate(BlockState state, Rotation rotation) {
BlockState rotated = state.setValue(FACING, rotation.rotate(state.getValue(FACING)));
return rotated.setValue(UNDER_FACING, rotation.rotate(state.getValue(UNDER_FACING)));
}
@Override
public BlockState mirror(BlockState state, Mirror mirror) {
BlockState mirrored = state.rotate(mirror.getRotation(state.getValue(FACING)));
return mirrored.setValue(UNDER_FACING, mirror.getRotation(state.getValue(UNDER_FACING)).rotate(state.getValue(UNDER_FACING)));
}
@Override
@@ -62,29 +92,41 @@ public final class UnderVaultBlock extends BaseEntityBlock {
}
@Override
public InteractionResult use(BlockState state, Level level, BlockPos pos, Player player,
InteractionHand hand, BlockHitResult hit) {
public InteractionResult use(BlockState state, Level level, BlockPos pos, Player player, InteractionHand hand,
BlockHitResult hitResult) {
ItemStack stack = player.getItemInHand(hand);
if (!state.getValue(ACTIVE) || !isKey(stack)) {
if (stack.isEmpty() || state.getValue(STATE) != UnderVaultState.ACTIVE) {
return InteractionResult.PASS;
}
if (!level.isClientSide && level instanceof ServerLevel serverLevel
&& level.getBlockEntity(pos) instanceof UnderVaultBlockEntity vault) {
if (vault.unlock(serverLevel, pos, player, stack)) {
level.setBlock(pos, state.setValue(ACTIVE, false), Block.UPDATE_ALL);
return InteractionResult.CONSUME;
}
if (level instanceof ServerLevel serverLevel
&& level.getBlockEntity(pos) instanceof UnderVaultBlockEntity underVault) {
UnderVaultLogic.Server.tryInsertKey(
serverLevel, pos, state, underVault.getVaultData().getConfig(),
underVault.getVaultData().getServerData(), underVault.getVaultData().getSharedData(),
player, stack);
return InteractionResult.SUCCESS;
}
return InteractionResult.sidedSuccess(level.isClientSide);
}
private static boolean isKey(ItemStack stack) {
return stack.is(BuiltInRegistries.ITEM.get(new ResourceLocation(Antarchy.MODID, "undertrial_key")));
return level.isClientSide ? InteractionResult.CONSUME : InteractionResult.PASS;
}
@Override
public <T extends BlockEntity> BlockEntityTicker<T> getTicker(Level level, BlockState state,
BlockEntityType<T> type) {
return createTickerHelper(type, blockEntityType.get(), UnderVaultBlockEntity::tick);
public boolean triggerEvent(BlockState state, Level level, BlockPos pos, int id, int param) {
return UnderVaultEffects.handle(level, pos, id, param);
}
@Override
@SuppressWarnings("unchecked")
public <T extends BlockEntity> BlockEntityTicker<T> getTicker(Level level, BlockState state, BlockEntityType<T> type) {
if (this.blockEntityType == null || type != this.blockEntityType.get()) {
return null;
}
if (level.isClientSide) {
return (BlockEntityTicker<T>) (BlockEntityTicker<UnderVaultBlockEntity>) UnderVaultBlockEntity::clientTick;
}
return (BlockEntityTicker<T>) (BlockEntityTicker<UnderVaultBlockEntity>) ((serverLevel, pos, blockState, blockEntity) -> {
if (serverLevel instanceof ServerLevel levelServer) {
UnderVaultBlockEntity.serverTick(levelServer, pos, blockState, blockEntity);
}
});
}
}
@@ -1,8 +1,16 @@
package com.craisinlord.antarchy.content.block;
import com.craisinlord.antarchy.content.block.entity.UndertrialSpawnerBlockEntity;
import com.craisinlord.antarchy.content.block.entity.undertrial.UndertrialSpawnerEffects;
import com.craisinlord.antarchy.content.block.entity.undertrial.UndertrialSpawnerState;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.SpawnEggItem;
import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.BaseEntityBlock;
@@ -15,29 +23,36 @@ import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.StateDefinition;
import net.minecraft.world.level.block.state.properties.BooleanProperty;
import net.minecraft.world.level.block.state.properties.EnumProperty;
import net.minecraft.world.phys.BlockHitResult;
import java.util.function.Supplier;
public final class UndertrialSpawnerBlock extends BaseEntityBlock {
public static final BooleanProperty ACTIVE = BooleanProperty.create("active");
public static final EnumProperty<UndertrialSpawnerState> STATE = EnumProperty.create("trial_spawner_state", UndertrialSpawnerState.class);
public static final BooleanProperty OMINOUS = BooleanProperty.create("ominous");
private final Supplier<? extends BlockEntityType<UndertrialSpawnerBlockEntity>> blockEntityType;
public UndertrialSpawnerBlock(Supplier<? extends BlockEntityType<UndertrialSpawnerBlockEntity>> blockEntityType,
BlockBehaviour.Properties properties) {
super(properties);
public UndertrialSpawnerBlock(Supplier<? extends BlockEntityType<UndertrialSpawnerBlockEntity>> blockEntityType, BlockBehaviour.Properties properties) {
super(properties.lightLevel(UndertrialSpawnerBlock::lightLevel));
this.blockEntityType = blockEntityType;
registerDefaultState(stateDefinition.any().setValue(ACTIVE, false).setValue(OMINOUS, false));
this.registerDefaultState(this.stateDefinition.any()
.setValue(STATE, UndertrialSpawnerState.INACTIVE)
.setValue(OMINOUS, false));
}
public static int lightLevel(BlockState state) {
return state.getValue(STATE).lightLevel();
}
@Override
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
builder.add(ACTIVE, OMINOUS);
builder.add(STATE, OMINOUS);
}
@Override
public BlockState getStateForPlacement(BlockPlaceContext context) {
return context.getClickedFace() == Direction.DOWN ? defaultBlockState() : null;
return context.getClickedFace() == Direction.DOWN ? this.defaultBlockState() : null;
}
@Override
@@ -51,8 +66,35 @@ public final class UndertrialSpawnerBlock extends BaseEntityBlock {
}
@Override
public <T extends BlockEntity> BlockEntityTicker<T> getTicker(Level level, BlockState state,
BlockEntityType<T> type) {
return createTickerHelper(type, blockEntityType.get(), UndertrialSpawnerBlockEntity::tick);
public InteractionResult use(BlockState state, Level level, BlockPos pos, Player player, InteractionHand hand,
BlockHitResult hitResult) {
ItemStack stack = player.getItemInHand(hand);
if (!(stack.getItem() instanceof SpawnEggItem)) {
return InteractionResult.PASS;
}
if (level.getBlockEntity(pos) instanceof UndertrialSpawnerBlockEntity spawner) {
if (!level.isClientSide) {
spawner.setSpawnEntity(((SpawnEggItem) stack.getItem()).getType(stack.getTag()));
}
return InteractionResult.sidedSuccess(level.isClientSide);
}
return InteractionResult.PASS;
}
@Override
public boolean triggerEvent(BlockState state, Level level, BlockPos pos, int id, int param) {
return UndertrialSpawnerEffects.handle(level, pos, id, param);
}
@Override
public <T extends BlockEntity> BlockEntityTicker<T> getTicker(Level level, BlockState state, BlockEntityType<T> blockEntityType) {
if (level.isClientSide) {
return createTickerHelper(blockEntityType, this.blockEntityType.get(), UndertrialSpawnerBlockEntity::clientTick);
}
return createTickerHelper(blockEntityType, this.blockEntityType.get(), (tickLevel, tickPos, tickState, blockEntity) -> {
if (tickLevel instanceof ServerLevel serverLevel) {
UndertrialSpawnerBlockEntity.serverTick(serverLevel, tickPos, tickState, blockEntity);
}
});
}
}
@@ -41,6 +41,28 @@ public class AntNestBlockEntity extends BlockEntity {
}
public static void serverTick(ServerLevel level, BlockPos pos, BlockState state, AntNestBlockEntity nest) {
if (state.getBlock() instanceof com.craisinlord.antarchy.content.block.AntTrapBlock) {
if (level.getGameTime() % 20L == 0L && nest.storedAnts.size() < nest.maxOccupants()) {
net.minecraft.world.phys.AABB searchArea = new net.minecraft.world.phys.AABB(pos).inflate(15.0D);
for (BaseAntEntity ant : level.getEntitiesOfClass(BaseAntEntity.class, searchArea, BaseAntEntity::isAlive)) {
if (ant.distanceToSqr(net.minecraft.world.phys.Vec3.atCenterOf(pos)) > 225.0D) {
continue;
}
if (!nest.canAccept(ant.getType())) {
continue;
}
if (ant.distanceToSqr(net.minecraft.world.phys.Vec3.atCenterOf(pos)) <= 2.25D) {
nest.tryStoreAnt(ant);
if (nest.storedAnts.size() >= nest.maxOccupants()) {
break;
}
} else {
ant.getNavigation().moveTo(pos.getX() + 0.5D, pos.getY() + 0.5D, pos.getZ() + 0.5D, 1.05D);
}
}
}
return;
}
boolean playerNearby = level.hasNearbyAlivePlayer(
pos.getX() + 0.5D, pos.getY() + 0.5D, pos.getZ() + 0.5D, PLAYER_ACTIVATION_RANGE);
if (!playerNearby) {
@@ -116,12 +138,17 @@ public class AntNestBlockEntity extends BlockEntity {
}
public void releaseAll(ServerLevel level) {
while (!this.storedAnts.isEmpty()) {
this.releaseOne(level);
int remaining = this.storedAnts.size();
while (remaining-- > 0 && !this.storedAnts.isEmpty()) {
this.releaseOne(level, true);
}
}
private void releaseOne(ServerLevel level) {
this.releaseOne(level, false);
}
private void releaseOne(ServerLevel level, boolean forceRelease) {
if (this.storedAnts.isEmpty()) {
return;
}
@@ -133,20 +160,39 @@ public class AntNestBlockEntity extends BlockEntity {
return;
}
BlockPos spawnPos = this.getBlockPos().above();
if (!level.getBlockState(spawnPos).canBeReplaced()) {
BlockPos origin = this.getBlockPos();
java.util.List<BlockPos> candidates = new java.util.ArrayList<>();
for (int y = 1; y <= 3; y++) {
candidates.add(origin.above(y));
for (int radius = 1; radius <= 2; radius++) {
candidates.add(origin.offset(radius, y, 0));
candidates.add(origin.offset(-radius, y, 0));
candidates.add(origin.offset(0, y, radius));
candidates.add(origin.offset(0, y, -radius));
}
}
BlockPos spawnPos = null;
for (BlockPos candidate : candidates) {
antEntity.moveTo(candidate.getX() + SPAWN_OFFSET_XZ, candidate.getY() + SPAWN_OFFSET_Y, candidate.getZ() + SPAWN_OFFSET_XZ,
level.random.nextFloat() * 360.0F, 0.0F);
if (level.noCollision(antEntity)) {
spawnPos = candidate;
break;
}
}
if (spawnPos == null && !forceRelease) {
return;
}
antEntity.moveTo(spawnPos.getX() + SPAWN_OFFSET_XZ, spawnPos.getY() + SPAWN_OFFSET_Y, spawnPos.getZ() + SPAWN_OFFSET_XZ, level.random.nextFloat() * 360.0F, 0.0F);
if (!level.noCollision(antEntity)) {
return;
if (spawnPos == null) {
antEntity.moveTo(origin.getX() + SPAWN_OFFSET_XZ, origin.getY() + SPAWN_OFFSET_Y, origin.getZ() + SPAWN_OFFSET_XZ,
level.random.nextFloat() * 360.0F, 0.0F);
}
this.storedAnts.remove(0);
antEntity.setNestPos(this.getBlockPos());
antEntity.setNestPos(origin);
antEntity.onExitNest();
level.addFreshEntity(antEntity);
if (!level.addFreshEntity(antEntity) && !forceRelease) {
return;
}
this.storedAnts.remove(0);
this.setChanged();
}
@@ -5,6 +5,7 @@ import com.craisinlord.antarchy.content.entity.portal.DimensionalTearEntity;
import java.util.function.Supplier;
import net.minecraft.core.BlockPos;
import net.minecraft.core.HolderLookup;
import net.minecraft.core.SectionPos;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.util.Mth;
@@ -12,6 +13,7 @@ import net.minecraft.util.RandomSource;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.Nullable;
@@ -139,14 +141,21 @@ public class DimensionalTearMarkerBlockEntity extends BlockEntity {
if (pos.getY() < minY || pos.getY() > maxY) {
return false;
}
BlockPos.MutableBlockPos checkPos = new BlockPos.MutableBlockPos();
LevelChunk chunk = null;
for (int dx = -2; dx <= 2; dx++) {
for (int dy = -2; dy <= 3; dy++) {
for (int dz = -4; dz <= 4; dz++) {
BlockPos checkPos = pos.offset(dx, dy, dz);
if (!level.isLoaded(checkPos)) {
for (int dz = -4; dz <= 4; dz++) {
int chunkX = SectionPos.blockToSectionCoord(pos.getX() + dx);
int chunkZ = SectionPos.blockToSectionCoord(pos.getZ() + dz);
if (chunk == null || chunk.getPos().x != chunkX || chunk.getPos().z != chunkZ) {
chunk = level.getChunkSource().getChunkNow(chunkX, chunkZ);
if (chunk == null) {
return false;
}
if (!level.getBlockState(checkPos).getCollisionShape(level, checkPos).isEmpty()) {
}
for (int dy = -2; dy <= 3; dy++) {
checkPos.set(pos.getX() + dx, pos.getY() + dy, pos.getZ() + dz);
if (!chunk.getBlockState(checkPos).getCollisionShape(level, checkPos).isEmpty()) {
return false;
}
}
@@ -1,6 +1,8 @@
package com.craisinlord.antarchy.content.block.entity;
import com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity;
import java.util.EnumMap;
import java.util.Map;
import java.util.UUID;
import java.util.function.Supplier;
import net.minecraft.core.BlockPos;
@@ -14,7 +16,8 @@ import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
public class PortalGunPortalBaseBlockEntity extends BlockEntity {
public class PortalGunPortalBaseBlockEntity extends BlockEntity implements PortalGunPortalCellAccess {
private final Map<net.minecraft.core.Direction, PortalGunPortalFaceRecord> faceRecords = new EnumMap<>(net.minecraft.core.Direction.class);
private UUID ownerId;
private UUID portalId;
private PortalGunPortalEntity.PortalSide side = PortalGunPortalEntity.PortalSide.BLUE;
@@ -26,6 +29,11 @@ public class PortalGunPortalBaseBlockEntity extends BlockEntity {
super(typeSupplier.get(), pos, state);
}
@Override
public Map<net.minecraft.core.Direction, PortalGunPortalFaceRecord> portalGun$getFaceRecords() {
return this.faceRecords;
}
public void configure(UUID ownerId, UUID portalId, PortalGunPortalEntity.PortalSide side, BlockPos masterPos) {
this.ownerId = ownerId;
this.portalId = portalId;
@@ -61,6 +69,11 @@ public class PortalGunPortalBaseBlockEntity extends BlockEntity {
public void updatePair(UUID linkedPortalId, int pairTime) {
this.linkedPortalId = linkedPortalId;
this.pairTime = pairTime;
for (PortalGunPortalFaceRecord record : java.util.List.copyOf(this.faceRecords.values())) {
if (record.portalId().equals(this.portalId)) {
this.portalGun$putFaceRecord(record.withPair(linkedPortalId, pairTime));
}
}
this.setChanged();
}
@@ -69,11 +82,20 @@ public class PortalGunPortalBaseBlockEntity extends BlockEntity {
}
public void onBroken() {
if (!(this.level instanceof ServerLevel serverLevel) || this.portalId == null) {
if (!(this.level instanceof ServerLevel serverLevel)) {
return;
}
if (serverLevel.getEntity(this.portalId) instanceof PortalGunPortalEntity portal && !portal.isRemoved()) {
portal.discard();
java.util.Set<UUID> portalIds = new java.util.HashSet<>();
if (this.portalId != null) {
portalIds.add(this.portalId);
}
for (PortalGunPortalFaceRecord record : this.faceRecords.values()) {
portalIds.add(record.portalId());
}
for (UUID id : portalIds) {
if (serverLevel.getEntity(id) instanceof PortalGunPortalEntity portal && !portal.isRemoved()) {
portal.discard();
}
}
}
@@ -81,15 +103,28 @@ public class PortalGunPortalBaseBlockEntity extends BlockEntity {
if (((level.getGameTime() + pos.asLong()) & 15L) != 0L) {
return;
}
if (level.getBlockEntity(blockEntity.masterPos) instanceof PortalGunPortalMasterBlockEntity master
&& master.matches(blockEntity.ownerId, blockEntity.portalId, blockEntity.side)) {
PortalGunPortalFaceRecord masterRecord = master.portalGun$getFaceRecord(master.getFacing(), blockEntity.portalId);
if (masterRecord != null && blockEntity.portalGun$getFaceRecord(masterRecord.face(), masterRecord.portalId()) == null) {
blockEntity.portalGun$putFaceRecord(masterRecord.withMaster(false));
}
}
blockEntity.portalGun$restoreMasterFaces(level, pos);
if (blockEntity.portalId != null
&& !(level.getEntity(blockEntity.portalId) instanceof PortalGunPortalEntity)
&& blockEntity.faceRecords.values().stream().anyMatch(record -> !record.portalId().equals(blockEntity.portalId))) {
return;
}
if (!(level.getBlockEntity(blockEntity.masterPos) instanceof PortalGunPortalMasterBlockEntity master)
|| !master.matches(blockEntity.ownerId, blockEntity.portalId, blockEntity.side)
|| !pos.equals(master.getBasePos())) {
|| !master.containsPortalSpot(pos)) {
level.removeBlock(pos, false);
return;
}
if (!(level.getEntity(blockEntity.portalId) instanceof PortalGunPortalEntity portal)
|| portal.isRemoved()
|| !pos.equals(portal.getBasePos())
|| !portal.containsPortalSpot(pos)
|| blockEntity.ownerId == null
|| !blockEntity.ownerId.equals(portal.getOwnerId())
|| blockEntity.side != portal.getPortalSide()) {
@@ -123,6 +158,7 @@ public class PortalGunPortalBaseBlockEntity extends BlockEntity {
tag.putInt("MasterY", this.masterPos.getY());
tag.putInt("MasterZ", this.masterPos.getZ());
tag.putInt("PairTime", this.pairTime);
this.portalGun$saveFaceRecords(tag);
}
@Override
@@ -134,6 +170,7 @@ public class PortalGunPortalBaseBlockEntity extends BlockEntity {
this.side = tag.getInt("Side") == PortalGunPortalEntity.PortalSide.ORANGE.ordinal() ? PortalGunPortalEntity.PortalSide.ORANGE : PortalGunPortalEntity.PortalSide.BLUE;
this.masterPos = new BlockPos(tag.getInt("MasterX"), tag.getInt("MasterY"), tag.getInt("MasterZ"));
this.pairTime = tag.getInt("PairTime");
this.portalGun$loadFaceRecords(tag);
}
@Override
@@ -0,0 +1,117 @@
package com.craisinlord.antarchy.content.block.entity;
import com.craisinlord.antarchy.content.portalgun.PortalGunPlacement;
import com.craisinlord.antarchy.content.portalgun.PortalGunSavedData;
import java.util.Map;
import java.util.UUID;
import net.minecraft.core.Direction;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.Tag;
import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ServerLevel;
public interface PortalGunPortalCellAccess {
Map<Direction, PortalGunPortalFaceRecord> portalGun$getFaceRecords();
default PortalGunPortalFaceRecord portalGun$getFaceRecord(Direction face) {
return this.portalGun$getFaceRecords().get(face);
}
default PortalGunPortalFaceRecord portalGun$getFaceRecord(Direction face, UUID portalId) {
PortalGunPortalFaceRecord record = this.portalGun$getFaceRecord(face);
return record != null && record.portalId().equals(portalId) ? record : null;
}
default boolean portalGun$putFaceRecord(PortalGunPortalFaceRecord record) {
PortalGunPortalFaceRecord existing = this.portalGun$getFaceRecord(record.face());
if (existing != null
&& (!java.util.Objects.equals(existing.gunId(), record.gunId())
|| existing.side() != record.side())) {
return false;
}
this.portalGun$getFaceRecords().put(record.face(), record);
if (this instanceof net.minecraft.world.level.block.entity.BlockEntity blockEntity) {
blockEntity.setChanged();
}
return true;
}
default PortalGunPortalFaceRecord portalGun$removeFaceRecord(Direction face, UUID portalId) {
PortalGunPortalFaceRecord existing = this.portalGun$getFaceRecord(face, portalId);
if (existing == null) {
return null;
}
PortalGunPortalFaceRecord removed = this.portalGun$getFaceRecords().remove(face);
if (this instanceof net.minecraft.world.level.block.entity.BlockEntity blockEntity) {
blockEntity.setChanged();
}
return removed;
}
default void portalGun$updateFacePair(UUID portalId, UUID linkedPortalId, int pairTime) {
for (PortalGunPortalFaceRecord record : java.util.List.copyOf(this.portalGun$getFaceRecords().values())) {
if (record.portalId().equals(portalId)) {
this.portalGun$putFaceRecord(record.withPair(linkedPortalId, pairTime));
}
}
}
default void portalGun$restoreMasterFaces(ServerLevel level, BlockPos pos) {
for (PortalGunPortalFaceRecord record : java.util.List.copyOf(this.portalGun$getFaceRecords().values())) {
if (record.ownerId() == null || record.portalId() == null) {
continue;
}
if (!PortalGunSavedData.isRegistered(level, record.gunId(), record.side(), record.portalId())) {
this.portalGun$removeFaceRecord(record.face(), record.portalId());
continue;
}
if (!record.master() || !record.masterPos().equals(pos)) {
continue;
}
boolean complete = !record.portalSpots().isEmpty() && record.portalSpots().get(0).equals(record.masterPos());
for (int i = 0; complete && i < record.portalSpots().size(); i++) {
BlockPos spot = record.portalSpots().get(i);
BlockPos support = spot.relative(record.face().getOpposite());
if (!level.hasChunkAt(spot) || !level.hasChunkAt(support)
|| !level.getBlockState(support).isFaceSturdy(level, support, record.face())
|| !(level.getBlockEntity(spot) instanceof PortalGunPortalCellAccess cell)) {
complete = false;
continue;
}
PortalGunPortalFaceRecord footprintRecord = cell.portalGun$getFaceRecord(record.face(), record.portalId());
complete = footprintRecord != null && footprintRecord.master() == (i == 0);
}
complete = complete && PortalGunPlacement.isRectangularFootprint(
record.portalSpots().toArray(BlockPos[]::new),
record.face(),
record.upAxis(),
record.portalWidth(),
record.portalHeight()
);
if (complete) {
PortalGunPortalMasterBlockEntity.restorePortalEntity(level, record);
}
}
}
default void portalGun$saveFaceRecords(CompoundTag tag) {
ListTag faces = new ListTag();
for (PortalGunPortalFaceRecord record : this.portalGun$getFaceRecords().values()) {
faces.add(record.save());
}
tag.put("PortalFaces", faces);
}
default void portalGun$loadFaceRecords(CompoundTag tag) {
Map<Direction, PortalGunPortalFaceRecord> records = this.portalGun$getFaceRecords();
records.clear();
ListTag faces = tag.getList("PortalFaces", Tag.TAG_COMPOUND);
for (int i = 0; i < faces.size(); i++) {
PortalGunPortalFaceRecord record = PortalGunPortalFaceRecord.load(faces.getCompound(i));
if (record != null) {
records.put(record.face(), record);
}
}
}
}
@@ -0,0 +1,149 @@
package com.craisinlord.antarchy.content.block.entity;
import com.craisinlord.antarchy.content.portalgun.PortalGunPlacement;
import com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity;
import com.craisinlord.antarchy.content.portalgun.PortalGunVariant;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.Tag;
public record PortalGunPortalFaceRecord(
UUID ownerId,
UUID gunId,
PortalGunVariant variant,
UUID portalId,
UUID linkedPortalId,
PortalGunPortalEntity.PortalSide side,
Direction face,
Direction upAxis,
BlockPos masterPos,
BlockPos basePos,
List<BlockPos> portalSpots,
Set<BlockPos> compensatedSpots,
int pairTime,
int portalWidth,
int portalHeight,
boolean master
) {
public PortalGunPortalFaceRecord {
masterPos = masterPos.immutable();
basePos = basePos.immutable();
portalSpots = portalSpots.stream().map(BlockPos::immutable).toList();
compensatedSpots = Set.copyOf(compensatedSpots);
}
public PortalGunPlacement placement() {
return PortalGunPlacement.fromStored(this.face, this.upAxis, this.masterPos, this.basePos, this.portalSpots.toArray(BlockPos[]::new), this.compensatedSpots, this.portalWidth, this.portalHeight);
}
public PortalGunPortalFaceRecord withPair(UUID linkedPortalId, int pairTime) {
return new PortalGunPortalFaceRecord(this.ownerId, this.gunId, this.variant, this.portalId, linkedPortalId, this.side, this.face, this.upAxis, this.masterPos, this.basePos, this.portalSpots, this.compensatedSpots, pairTime, this.portalWidth, this.portalHeight, this.master);
}
public PortalGunPortalFaceRecord withMaster(boolean master) {
return new PortalGunPortalFaceRecord(this.ownerId, this.gunId, this.variant, this.portalId, this.linkedPortalId, this.side, this.face, this.upAxis, this.masterPos, this.basePos, this.portalSpots, this.compensatedSpots, this.pairTime, this.portalWidth, this.portalHeight, master);
}
public CompoundTag save() {
CompoundTag tag = new CompoundTag();
tag.putUUID("OwnerId", this.ownerId);
if (this.gunId != null) {
tag.putUUID("GunId", this.gunId);
}
tag.putInt("Variant", this.variant.id());
tag.putUUID("PortalId", this.portalId);
if (this.linkedPortalId != null) {
tag.putUUID("LinkedPortalId", this.linkedPortalId);
}
tag.putInt("Side", this.side.ordinal());
tag.putInt("Face", this.face.get3DDataValue());
tag.putInt("UpAxis", this.upAxis.get3DDataValue());
putPos(tag, "Master", this.masterPos);
putPos(tag, "Base", this.basePos);
tag.putInt("PairTime", this.pairTime);
tag.putInt("PortalWidth", this.portalWidth);
tag.putInt("PortalHeight", this.portalHeight);
tag.putBoolean("MasterRole", this.master);
ListTag spots = new ListTag();
for (BlockPos pos : this.portalSpots) {
spots.add(savePos(pos));
}
tag.put("PortalSpots", spots);
ListTag compensated = new ListTag();
for (BlockPos pos : this.compensatedSpots) {
compensated.add(savePos(pos));
}
tag.put("CompensatedSpots", compensated);
return tag;
}
public static PortalGunPortalFaceRecord load(CompoundTag tag) {
if (!tag.hasUUID("OwnerId") || !tag.hasUUID("PortalId")) {
return null;
}
ListTag spotsTag = tag.getList("PortalSpots", Tag.TAG_COMPOUND);
List<BlockPos> spots = new java.util.ArrayList<>(spotsTag.size());
for (int i = 0; i < spotsTag.size(); i++) {
spots.add(loadPos(spotsTag.getCompound(i)));
}
ListTag compensatedTag = tag.getList("CompensatedSpots", Tag.TAG_COMPOUND);
Set<BlockPos> compensated = new HashSet<>();
for (int i = 0; i < compensatedTag.size(); i++) {
compensated.add(loadPos(compensatedTag.getCompound(i)));
}
PortalGunPortalEntity.PortalSide side = tag.getInt("Side") == PortalGunPortalEntity.PortalSide.ORANGE.ordinal()
? PortalGunPortalEntity.PortalSide.ORANGE
: PortalGunPortalEntity.PortalSide.BLUE;
int width = net.minecraft.util.Mth.clamp(tag.getInt("PortalWidth"), 1, 16);
int height = net.minecraft.util.Mth.clamp(tag.getInt("PortalHeight"), 2, 16);
return new PortalGunPortalFaceRecord(
tag.getUUID("OwnerId"),
tag.hasUUID("GunId") ? tag.getUUID("GunId") : null,
PortalGunVariant.byId(tag.getInt("Variant")),
tag.getUUID("PortalId"),
tag.hasUUID("LinkedPortalId") ? tag.getUUID("LinkedPortalId") : null,
side,
Direction.from3DDataValue(tag.getInt("Face")),
Direction.from3DDataValue(tag.getInt("UpAxis")),
getPos(tag, "Master"),
getPos(tag, "Base"),
spots,
compensated,
tag.getInt("PairTime"),
width,
height,
tag.getBoolean("MasterRole")
);
}
private static void putPos(CompoundTag tag, String key, BlockPos pos) {
CompoundTag value = new CompoundTag();
value.putInt("X", pos.getX());
value.putInt("Y", pos.getY());
value.putInt("Z", pos.getZ());
tag.put(key, value);
}
private static BlockPos getPos(CompoundTag tag, String key) {
CompoundTag value = tag.getCompound(key);
return new BlockPos(value.getInt("X"), value.getInt("Y"), value.getInt("Z"));
}
private static CompoundTag savePos(BlockPos pos) {
CompoundTag tag = new CompoundTag();
tag.putInt("X", pos.getX());
tag.putInt("Y", pos.getY());
tag.putInt("Z", pos.getZ());
return tag;
}
private static BlockPos loadPos(CompoundTag tag) {
return new BlockPos(tag.getInt("X"), tag.getInt("Y"), tag.getInt("Z"));
}
}
@@ -4,15 +4,17 @@ import com.craisinlord.antarchy.Antarchy;
import com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity;
import com.craisinlord.antarchy.content.portalgun.PortalGunPlacement;
import com.craisinlord.antarchy.content.portalgun.PortalGunSavedData;
import com.craisinlord.antarchy.content.portalgun.PortalGunVariant;
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.function.Supplier;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.HolderLookup;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.Tag;
@@ -30,14 +32,19 @@ import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.Vec3;
public class PortalGunPortalMasterBlockEntity extends BlockEntity {
public class PortalGunPortalMasterBlockEntity extends BlockEntity implements PortalGunPortalCellAccess {
private final Map<Direction, PortalGunPortalFaceRecord> faceRecords = new EnumMap<>(Direction.class);
private UUID ownerId;
private UUID gunId;
private PortalGunVariant variant = PortalGunVariant.DEFAULT;
private UUID portalId;
private PortalGunPortalEntity.PortalSide side = PortalGunPortalEntity.PortalSide.BLUE;
private Direction facing = Direction.NORTH;
private Direction upAxis = Direction.UP;
private BlockPos basePos = BlockPos.ZERO;
private BlockPos[] portalSpots = new BlockPos[] {BlockPos.ZERO, BlockPos.ZERO};
private int portalWidth = 1;
private int portalHeight = 2;
private Set<BlockPos> compensatedSpots = Set.of();
private UUID linkedPortalId;
private int pairTime;
@@ -48,6 +55,8 @@ public class PortalGunPortalMasterBlockEntity extends BlockEntity {
public void configure(
UUID ownerId,
UUID gunId,
PortalGunVariant variant,
UUID portalId,
PortalGunPortalEntity.PortalSide side,
Direction facing,
@@ -55,20 +64,32 @@ public class PortalGunPortalMasterBlockEntity extends BlockEntity {
BlockPos basePos,
BlockPos[] portalSpots,
Set<BlockPos> compensatedSpots,
int pairTime
int pairTime,
int portalWidth,
int portalHeight
) {
this.ownerId = ownerId;
this.gunId = gunId;
this.variant = variant == null ? PortalGunVariant.DEFAULT : variant;
this.portalId = portalId;
this.side = side;
this.facing = facing;
this.upAxis = upAxis;
this.basePos = basePos.immutable();
this.portalSpots = new BlockPos[] {portalSpots[0].immutable(), portalSpots[1].immutable()};
this.portalSpots = java.util.Arrays.stream(portalSpots).map(BlockPos::immutable).toArray(BlockPos[]::new);
this.portalWidth = portalWidth;
this.portalHeight = portalHeight;
this.compensatedSpots = new HashSet<>(compensatedSpots);
this.pairTime = pairTime;
this.portalGun$putFaceRecord(new PortalGunPortalFaceRecord(ownerId, gunId, this.variant, portalId, this.linkedPortalId, side, facing, upAxis, this.worldPosition, this.basePos, List.of(this.portalSpots), this.compensatedSpots, pairTime, portalWidth, portalHeight, true));
this.setChanged();
}
@Override
public Map<Direction, PortalGunPortalFaceRecord> portalGun$getFaceRecords() {
return this.faceRecords;
}
public UUID getPortalId() {
return this.portalId;
}
@@ -77,6 +98,14 @@ public class PortalGunPortalMasterBlockEntity extends BlockEntity {
return this.ownerId;
}
public UUID getGunId() {
return this.gunId;
}
public PortalGunVariant getVariant() {
return this.variant;
}
public PortalGunPortalEntity.PortalSide getSide() {
return this.side;
}
@@ -94,7 +123,16 @@ public class PortalGunPortalMasterBlockEntity extends BlockEntity {
}
public BlockPos[] getPortalSpots() {
return new BlockPos[] {this.portalSpots[0], this.portalSpots[1]};
return this.portalSpots.clone();
}
public boolean containsPortalSpot(BlockPos pos) {
for (BlockPos portalSpot : this.portalSpots) {
if (portalSpot.equals(pos)) {
return true;
}
}
return false;
}
public Set<BlockPos> getCompensatedSpots() {
@@ -112,6 +150,10 @@ public class PortalGunPortalMasterBlockEntity extends BlockEntity {
public void updatePair(UUID linkedPortalId, int pairTime) {
this.linkedPortalId = linkedPortalId;
this.pairTime = pairTime;
PortalGunPortalFaceRecord record = this.portalGun$getFaceRecord(this.facing, this.portalId);
if (record != null) {
this.portalGun$putFaceRecord(record.withPair(linkedPortalId, pairTime));
}
this.setChanged();
}
@@ -120,11 +162,20 @@ public class PortalGunPortalMasterBlockEntity extends BlockEntity {
}
public void onBroken() {
if (!(this.level instanceof ServerLevel serverLevel) || this.portalId == null) {
if (!(this.level instanceof ServerLevel serverLevel)) {
return;
}
if (serverLevel.getEntity(this.portalId) instanceof PortalGunPortalEntity portal && !portal.isRemoved()) {
portal.discard();
Set<UUID> portalIds = new HashSet<>();
if (this.portalId != null) {
portalIds.add(this.portalId);
}
for (PortalGunPortalFaceRecord record : this.faceRecords.values()) {
portalIds.add(record.portalId());
}
for (UUID id : portalIds) {
if (serverLevel.getEntity(id) instanceof PortalGunPortalEntity portal && !portal.isRemoved()) {
portal.discard();
}
}
}
@@ -132,6 +183,12 @@ public class PortalGunPortalMasterBlockEntity extends BlockEntity {
if (((level.getGameTime() + pos.asLong()) & 15L) != 0L) {
return;
}
blockEntity.portalGun$restoreMasterFaces(level, pos);
if (blockEntity.portalId != null
&& !(level.getEntity(blockEntity.portalId) instanceof PortalGunPortalEntity)
&& blockEntity.faceRecords.values().stream().anyMatch(record -> !record.portalId().equals(blockEntity.portalId))) {
return;
}
if (!blockEntity.hasValidFootprint(level, pos)) {
level.removeBlock(pos, false);
return;
@@ -161,16 +218,24 @@ public class PortalGunPortalMasterBlockEntity extends BlockEntity {
if (this.ownerId == null || this.portalId == null || !pos.equals(this.worldPosition)) {
return false;
}
if (!(level.getBlockEntity(this.basePos) instanceof PortalGunPortalBaseBlockEntity base)
|| !base.matches(this.ownerId, this.portalId, this.side)
|| !pos.equals(base.getMasterPos())) {
if (!PortalGunPlacement.isRectangularFootprint(this.portalSpots, this.facing, this.upAxis, this.portalWidth, this.portalHeight)) {
return false;
}
List<BlockPos> supportPositions = new ArrayList<>(2);
for (BlockPos portalSpot : this.portalSpots) {
List<BlockPos> supportPositions = new ArrayList<>(this.portalSpots.length);
for (int i = 0; i < this.portalSpots.length; i++) {
BlockPos portalSpot = this.portalSpots[i];
if (portalSpot == null || BlockPos.ZERO.equals(portalSpot)) {
return false;
}
if (i == 0) {
if (!portalSpot.equals(pos)) {
return false;
}
} else if (!(level.getBlockEntity(portalSpot) instanceof PortalGunPortalBaseBlockEntity base)
|| !base.matches(this.ownerId, this.portalId, this.side)
|| !pos.equals(base.getMasterPos())) {
return false;
}
supportPositions.add(portalSpot.relative(this.facing.getOpposite()));
}
for (BlockPos supportPos : supportPositions) {
@@ -186,23 +251,60 @@ public class PortalGunPortalMasterBlockEntity extends BlockEntity {
if (this.portalId == null || this.ownerId == null) {
return null;
}
if (!PortalGunSavedData.isRegistered(level, this.gunId, this.side, this.portalId)) {
return null;
}
if (level.getEntity(this.portalId) instanceof PortalGunPortalEntity portal && !portal.isRemoved()) {
return portal;
}
PortalGunPortalFaceRecord record = this.portalGun$getFaceRecord(this.facing, this.portalId);
if (record != null) {
return restorePortalEntity(level, record);
}
EntityType<?> rawType = BuiltInRegistries.ENTITY_TYPE.get(new ResourceLocation(Antarchy.MODID, "portal_gun_portal"));
if (!(rawType instanceof EntityType<?>)) {
return null;
}
PortalGunPlacement placement = PortalGunPlacement.fromStored(this.facing, this.upAxis, this.worldPosition, this.basePos, this.compensatedSpots);
PortalGunPlacement placement = PortalGunPlacement.fromStored(this.facing, this.upAxis, this.worldPosition, this.basePos, this.portalSpots, this.compensatedSpots, this.portalWidth, this.portalHeight);
@SuppressWarnings("unchecked")
PortalGunPortalEntity restored = new PortalGunPortalEntity((EntityType<? extends PortalGunPortalEntity>) rawType, level);
restored.setUUID(this.portalId);
restored.configure(this.ownerId, this.side, placement);
restored.configure(this.ownerId, this.gunId, this.variant, this.side, placement);
restored.restorePair(this.linkedPortalId, this.pairTime);
Vec3 center = placement.center();
restored.moveTo(center.x, center.y, center.z, placement.yaw(), 0.0F);
level.addFreshEntity(restored);
PortalGunSavedData.setPortal(level.getServer(), this.ownerId, this.side, restored.getUUID());
PortalGunSavedData.setPortal(level, this.gunId, this.ownerId, this.side, restored.getUUID());
return restored;
}
public static PortalGunPortalEntity restorePortalEntity(ServerLevel level, PortalGunPortalFaceRecord record) {
if (record.ownerId() == null || record.portalId() == null
|| !PortalGunSavedData.isRegistered(level, record.gunId(), record.side(), record.portalId())) {
return null;
}
if (level.getEntity(record.portalId()) instanceof PortalGunPortalEntity portal && !portal.isRemoved()) {
return portal;
}
EntityType<?> rawType = BuiltInRegistries.ENTITY_TYPE.get(new ResourceLocation(Antarchy.MODID, "portal_gun_portal"));
if (rawType == null) {
return null;
}
PortalGunPlacement placement = record.placement();
@SuppressWarnings("unchecked")
PortalGunPortalEntity restored = new PortalGunPortalEntity((EntityType<? extends PortalGunPortalEntity>) rawType, level);
restored.setUUID(record.portalId());
restored.configure(record.ownerId(), record.gunId(), record.variant(), record.side(), placement);
restored.restorePair(record.linkedPortalId(), record.pairTime());
Vec3 center = placement.center();
restored.moveTo(center.x, center.y, center.z, placement.yaw(), 0.0F);
level.addFreshEntity(restored);
PortalGunSavedData.setPortal(level, record.gunId(), record.ownerId(), record.side(), restored.getUUID());
PortalGunPortalEntity linked = PortalGunSavedData.findLoadedPortal(level, record.gunId(), record.side() == PortalGunPortalEntity.PortalSide.BLUE ? PortalGunPortalEntity.PortalSide.ORANGE : PortalGunPortalEntity.PortalSide.BLUE);
if (linked != null && linked != restored && !linked.isRemoved()) {
restored.linkTo(linked);
linked.linkTo(restored);
}
return restored;
}
@@ -211,8 +313,8 @@ public class PortalGunPortalMasterBlockEntity extends BlockEntity {
return;
}
PortalGunPortalEntity counterpart = PortalGunSavedData.findLoadedPortal(
level.getServer(),
this.ownerId,
level,
this.gunId,
this.side == PortalGunPortalEntity.PortalSide.BLUE ? PortalGunPortalEntity.PortalSide.ORANGE : PortalGunPortalEntity.PortalSide.BLUE
);
if (counterpart == null || counterpart == portal || counterpart.isRemoved()) {
@@ -230,6 +332,10 @@ public class PortalGunPortalMasterBlockEntity extends BlockEntity {
if (this.ownerId != null) {
tag.putUUID("OwnerId", this.ownerId);
}
if (this.gunId != null) {
tag.putUUID("GunId", this.gunId);
}
tag.putInt("Variant", this.variant.id());
if (this.portalId != null) {
tag.putUUID("PortalId", this.portalId);
}
@@ -243,6 +349,8 @@ public class PortalGunPortalMasterBlockEntity extends BlockEntity {
tag.putInt("BaseY", this.basePos.getY());
tag.putInt("BaseZ", this.basePos.getZ());
tag.putInt("PairTime", this.pairTime);
tag.putInt("PortalWidth", this.portalWidth);
tag.putInt("PortalHeight", this.portalHeight);
ListTag spots = new ListTag();
for (BlockPos portalSpot : this.portalSpots) {
CompoundTag spot = new CompoundTag();
@@ -261,12 +369,15 @@ public class PortalGunPortalMasterBlockEntity extends BlockEntity {
compensated.add(spot);
}
tag.put("CompensatedSpots", compensated);
this.portalGun$saveFaceRecords(tag);
}
@Override
public void load(CompoundTag tag) {
super.load(tag);
this.ownerId = tag.hasUUID("OwnerId") ? tag.getUUID("OwnerId") : null;
this.gunId = tag.hasUUID("GunId") ? tag.getUUID("GunId") : null;
this.variant = PortalGunVariant.byId(tag.getInt("Variant"));
this.portalId = tag.hasUUID("PortalId") ? tag.getUUID("PortalId") : null;
this.linkedPortalId = tag.hasUUID("LinkedPortalId") ? tag.getUUID("LinkedPortalId") : null;
this.side = tag.getInt("Side") == PortalGunPortalEntity.PortalSide.ORANGE.ordinal() ? PortalGunPortalEntity.PortalSide.ORANGE : PortalGunPortalEntity.PortalSide.BLUE;
@@ -275,10 +386,15 @@ public class PortalGunPortalMasterBlockEntity extends BlockEntity {
this.basePos = new BlockPos(tag.getInt("BaseX"), tag.getInt("BaseY"), tag.getInt("BaseZ"));
this.pairTime = tag.getInt("PairTime");
ListTag spots = tag.getList("PortalSpots", Tag.TAG_COMPOUND);
for (int i = 0; i < Math.min(2, spots.size()); i++) {
if (!spots.isEmpty()) {
this.portalSpots = new BlockPos[spots.size()];
}
for (int i = 0; i < spots.size(); i++) {
CompoundTag spot = spots.getCompound(i);
this.portalSpots[i] = new BlockPos(spot.getInt("X"), spot.getInt("Y"), spot.getInt("Z"));
}
this.portalWidth = net.minecraft.util.Mth.clamp(tag.getInt("PortalWidth"), 1, 16);
this.portalHeight = net.minecraft.util.Mth.clamp(tag.getInt("PortalHeight"), 2, 16);
ListTag compensated = tag.getList("CompensatedSpots", Tag.TAG_COMPOUND);
Set<BlockPos> compensatedSpots = new HashSet<>();
for (int i = 0; i < compensated.size(); i++) {
@@ -286,6 +402,10 @@ public class PortalGunPortalMasterBlockEntity extends BlockEntity {
compensatedSpots.add(new BlockPos(spot.getInt("X"), spot.getInt("Y"), spot.getInt("Z")));
}
this.compensatedSpots = compensatedSpots;
this.portalGun$loadFaceRecords(tag);
if (this.faceRecords.isEmpty() && this.ownerId != null && this.portalId != null && this.portalSpots.length > 0) {
this.portalGun$putFaceRecord(new PortalGunPortalFaceRecord(this.ownerId, this.gunId, this.variant, this.portalId, this.linkedPortalId, this.side, this.facing, this.upAxis, this.worldPosition, this.basePos, List.of(this.portalSpots), this.compensatedSpots, this.pairTime, this.portalWidth, this.portalHeight, true));
}
}
@Override
@@ -6,9 +6,9 @@ import com.craisinlord.antarchy.content.AntarchyObjects;
import com.craisinlord.antarchy.content.block.PotentNyxiteBlock;
import com.craisinlord.antarchy.content.block.state.PotentNyxiteState;
import com.craisinlord.antarchy.content.fluid.AntarchyFluidChecks;
import com.craisinlord.antarchy.content.gravity.AntarchyGravityApi;
import com.craisinlord.antarchy.content.gravity.AntarchyGravityDirection;
import com.craisinlord.antarchy.content.gravity.AntarchyGravityRotationUtil;
import com.craisinlord.anthology.gravity.GravityApi;
import com.craisinlord.anthology.gravity.GravityDirection;
import com.craisinlord.anthology.gravity.GravityRotationUtil;
import com.craisinlord.antarchy.content.client.particle.InvertedGeyserParticleOptions;
import java.util.List;
import java.util.function.Predicate;
@@ -333,9 +333,9 @@ public class PotentNyxiteBlockEntity extends BlockEntity {
continue;
}
AntarchyGravityDirection gravityDirection = AntarchyGravityApi.getGravityDirection(entity);
GravityDirection gravityDirection = GravityApi.getGravityDirection(entity);
Vec3 currentWorldVelocity = gravityDirection.isInverted()
? AntarchyGravityRotationUtil.vecPlayerToWorld(entity.getDeltaMovement(), gravityDirection)
? GravityRotationUtil.vecPlayerToWorld(entity.getDeltaMovement(), gravityDirection)
: entity.getDeltaMovement();
double axisVelocity = currentWorldVelocity.y * eruptionDirection.getStepY();
@@ -343,7 +343,7 @@ public class PotentNyxiteBlockEntity extends BlockEntity {
Vec3 nextWorldVelocity = currentWorldVelocity.add(worldPush);
entity.setDeltaMovement(
gravityDirection.isInverted()
? AntarchyGravityRotationUtil.vecWorldToPlayer(nextWorldVelocity, gravityDirection)
? GravityRotationUtil.vecWorldToPlayer(nextWorldVelocity, gravityDirection)
: nextWorldVelocity
);
entity.hurtMarked = true;
@@ -10,6 +10,7 @@ import net.minecraft.world.Difficulty;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
import org.jetbrains.annotations.Nullable;
public final class QueenTrailSpawnMarkerBlockEntity extends BlockEntity {
private static final double ACTIVATION_DISTANCE_SQUARED = 96.0D * 96.0D;
@@ -19,11 +20,11 @@ public final class QueenTrailSpawnMarkerBlockEntity extends BlockEntity {
private float yaw;
private boolean configured;
private boolean consumed;
@Nullable
private UUID queenUuid;
public QueenTrailSpawnMarkerBlockEntity(BlockPos pos, BlockState state,
Supplier<? extends BlockEntityType<QueenTrailSpawnMarkerBlockEntity>> type) {
super(type.get(), pos, state);
public QueenTrailSpawnMarkerBlockEntity(BlockPos pos, BlockState state, Supplier<? extends BlockEntityType<QueenTrailSpawnMarkerBlockEntity>> typeSupplier) {
super(typeSupplier.get(), pos, state);
}
public void configure(long siteId, BlockPos spawnPos, BlockPos homePos, float yaw) {
@@ -32,52 +33,109 @@ public final class QueenTrailSpawnMarkerBlockEntity extends BlockEntity {
this.homePos = homePos.immutable();
this.yaw = yaw;
this.configured = true;
setChanged();
this.setChanged();
}
public long getSiteId() { return siteId; }
public BlockPos getSpawnPos() { return spawnPos; }
public boolean isConfigured() { return configured; }
public long getSiteId() {
return this.siteId;
}
public BlockPos getSpawnPos() {
return this.spawnPos;
}
public boolean isConfigured() {
return this.configured;
}
public boolean isConsumed() {
return this.consumed;
}
public static void serverTick(ServerLevel level, QueenTrailSpawnMarkerBlockEntity marker) {
if (!marker.configured || marker.consumed || level.getDifficulty() == Difficulty.PEACEFUL) return;
if (Math.floorMod(level.getGameTime(), 20L) != Math.floorMod(marker.siteId, 20L)) return;
boolean nearby = level.players().stream().anyMatch(player -> !player.isSpectator()
&& player.distanceToSqr(marker.spawnPos.getX() + .5D, marker.spawnPos.getY() + .5D,
marker.spawnPos.getZ() + .5D) <= ACTIVATION_DISTANCE_SQUARED);
if (!nearby) return;
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++)
if (!marker.configured || marker.consumed || level.getDifficulty() == Difficulty.PEACEFUL) {
return;
}
if (Math.floorMod(level.getGameTime(), 20L) != Math.floorMod(marker.siteId, 20L)) {
return;
}
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) {
return;
}
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);
}
}
marker.spawnNow(level);
}
@Nullable
public QueenEntity spawnNow(ServerLevel level) {
if (!this.configured) {
return null;
}
if (this.consumed) {
if (this.queenUuid != null && level.getEntity(this.queenUuid) instanceof QueenEntity queen) {
return queen;
}
return null;
}
for (int chunkX = (this.spawnPos.getX() - 16) >> 4; chunkX <= (this.spawnPos.getX() + 16) >> 4; chunkX++) {
for (int chunkZ = (this.spawnPos.getZ() - 16) >> 4; chunkZ <= (this.spawnPos.getZ() + 16) >> 4; chunkZ++) {
level.getChunk(chunkX, chunkZ);
}
}
QueenEntity queen = QueenEntity.spawnFromUndersideTrail(level, this.spawnPos, this.homePos, this.siteId, this.yaw);
if (queen == null) {
for (int radius = 2; radius <= 10 && queen == null; radius += 2) {
for (int dx = -radius; dx <= radius && queen == null; dx += radius) {
for (int dz = -radius; dz <= radius && queen == null; dz += radius) {
BlockPos candidate = this.spawnPos.offset(dx, 0, dz);
if (candidate.equals(this.spawnPos)) {
continue;
}
queen = QueenEntity.spawnFromUndersideTrail(level, candidate, this.homePos, this.siteId, this.yaw);
if (queen != null) {
this.spawnPos = candidate.immutable();
}
}
}
}
}
if (queen != null) {
marker.consumed = true;
marker.queenUuid = queen.getUUID();
marker.setChanged();
this.consumed = true;
this.queenUuid = queen.getUUID();
this.setChanged();
}
return queen;
}
@Override
protected void saveAdditional(CompoundTag tag) {
super.saveAdditional(tag);
tag.putLong("site_id", this.siteId);
tag.putLong("spawn_pos", this.spawnPos.asLong());
tag.putLong("home_pos", this.homePos.asLong());
tag.putFloat("yaw", this.yaw);
tag.putBoolean("configured", this.configured);
tag.putBoolean("consumed", this.consumed);
if (this.queenUuid != null) {
tag.putUUID("queen_uuid", this.queenUuid);
}
}
@Override
public void load(CompoundTag tag) {
super.load(tag);
siteId = tag.getLong("site_id");
spawnPos = BlockPos.of(tag.getLong("spawn_pos"));
homePos = BlockPos.of(tag.getLong("home_pos"));
yaw = tag.getFloat("yaw");
configured = tag.getBoolean("configured");
consumed = tag.getBoolean("consumed");
queenUuid = tag.hasUUID("queen_uuid") ? tag.getUUID("queen_uuid") : null;
}
@Override
protected void saveAdditional(CompoundTag tag) {
super.saveAdditional(tag);
tag.putLong("site_id", siteId);
tag.putLong("spawn_pos", spawnPos.asLong());
tag.putLong("home_pos", homePos.asLong());
tag.putFloat("yaw", yaw);
tag.putBoolean("configured", configured);
tag.putBoolean("consumed", consumed);
if (queenUuid != null) tag.putUUID("queen_uuid", queenUuid);
this.siteId = tag.getLong("site_id");
this.spawnPos = BlockPos.of(tag.getLong("spawn_pos"));
this.homePos = BlockPos.of(tag.getLong("home_pos"));
this.yaw = tag.getFloat("yaw");
this.configured = tag.getBoolean("configured");
this.consumed = tag.getBoolean("consumed");
this.queenUuid = tag.hasUUID("queen_uuid") ? tag.getUUID("queen_uuid") : null;
}
}
@@ -1,10 +1,18 @@
package com.craisinlord.antarchy.content.block.entity;
import com.craisinlord.antarchy.Antarchy;
import com.craisinlord.antarchy.content.block.UnderVaultBlock;
import com.craisinlord.antarchy.content.block.entity.undervault.UnderVaultConfig;
import com.craisinlord.antarchy.content.block.entity.undervault.UnderVaultData;
import com.craisinlord.antarchy.content.block.entity.undervault.UnderVaultLogic;
import com.craisinlord.antarchy.content.block.entity.undervault.UnderVaultState;
import net.minecraft.core.BlockPos;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.core.particles.SimpleParticleType;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.game.ClientGamePacketListener;
import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.player.Player;
@@ -12,72 +20,87 @@ 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.BlockEntityType;
import net.minecraft.world.level.storage.loot.parameters.LootContextParamSets;
import net.minecraft.world.level.storage.loot.LootParams;
import net.minecraft.world.level.storage.loot.LootTable;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
import net.minecraft.core.Direction;
import net.minecraft.core.dispenser.DefaultDispenseItemBehavior;
import java.util.Optional;
import java.util.function.Supplier;
public final class UnderVaultBlockEntity extends BlockEntity {
private boolean unlocked;
private final UnderVaultData vaultData;
private boolean playerWasNearby;
public UnderVaultBlockEntity(BlockPos pos, net.minecraft.world.level.block.state.BlockState state,
Supplier<? extends BlockEntityType<UnderVaultBlockEntity>> type) {
public UnderVaultBlockEntity(BlockPos pos, BlockState state, Supplier<? extends BlockEntityType<UnderVaultBlockEntity>> type) {
super(type.get(), pos, state);
this.vaultData = new UnderVaultData();
this.vaultData.setConfig(createConfig());
}
public static void tick(Level level, BlockPos pos, net.minecraft.world.level.block.state.BlockState state,
UnderVaultBlockEntity blockEntity) {
if (level.isClientSide && state.getValue(com.craisinlord.antarchy.content.block.UnderVaultBlock.ACTIVE)
&& level.random.nextInt(4) == 0) {
private static UnderVaultConfig createConfig() {
return new UnderVaultConfig(new ResourceLocation(Antarchy.MODID, "chests/undertrial_vault"), 4.0D, 4.0D,
new ItemStack(BuiltInRegistries.ITEM.get(new ResourceLocation(Antarchy.MODID, "undertrial_key"))),
Optional.empty());
}
public static void serverTick(ServerLevel level, BlockPos pos, BlockState state, UnderVaultBlockEntity blockEntity) {
blockEntity.vaultData.setLevel(level);
boolean playerNearby = !level.getEntitiesOfClass(Player.class, new AABB(pos).inflate(4.0D), player -> !player.isSpectator()).isEmpty();
if (state.getValue(UnderVaultBlock.STATE) == UnderVaultState.INACTIVE && playerNearby && !blockEntity.playerWasNearby) {
state = state.setValue(UnderVaultBlock.STATE, UnderVaultState.ACTIVE);
level.setBlockAndUpdate(pos, state);
}
blockEntity.playerWasNearby = playerNearby;
UnderVaultLogic.Server.tick(level, pos, state, blockEntity.vaultData.getConfig(), blockEntity.vaultData.getServerData(), blockEntity.vaultData.getSharedData());
}
public static void clientTick(Level level, BlockPos pos, BlockState state, UnderVaultBlockEntity blockEntity) {
blockEntity.vaultData.setLevel(level);
UnderVaultLogic.Client.tick(level, pos, state, blockEntity.vaultData.getClientData(), blockEntity.vaultData.getSharedData());
UnderVaultState vaultState = state.getValue(UnderVaultBlock.STATE);
if (level.isClientSide && level.random.nextInt(4) == 0
&& (vaultState == UnderVaultState.ACTIVE || vaultState == UnderVaultState.UNLOCKING)) {
Player player = level.getNearestPlayer(pos.getX() + 0.5D, pos.getY() + 0.5D, pos.getZ() + 0.5D, 8.0D, false);
if (player != null) {
Vec3 start = Vec3.atCenterOf(pos);
Vec3 end = player.position().add(0.0D, player.getBbHeight() * 0.5D, 0.0D);
Vec3 point = start.lerp(end, 0.15D + level.random.nextDouble() * 0.7D);
level.addParticle(particle("undervault_connection"), point.x, point.y, point.z, 0.0D, 0.0D, 0.0D);
double progress = 0.15D + level.random.nextDouble() * 0.7D;
Vec3 point = start.lerp(end, progress);
level.addParticle((SimpleParticleType) BuiltInRegistries.PARTICLE_TYPE.get(new ResourceLocation(Antarchy.MODID, "undervault_connection")), point.x, point.y, point.z, 0.0D, 0.0D, 0.0D);
}
}
}
private static SimpleParticleType particle(String id) {
return (SimpleParticleType) BuiltInRegistries.PARTICLE_TYPE.get(new ResourceLocation(Antarchy.MODID, id));
}
public boolean unlock(ServerLevel level, BlockPos pos, Player player, ItemStack key) {
if (unlocked) {
return false;
}
if (!player.getAbilities().instabuild) {
key.shrink(1);
}
LootTable table = level.getServer().getLootData().getLootTable(
new ResourceLocation(Antarchy.MODID, "chests/undertrial_vault"));
LootParams params = new LootParams.Builder(level).withParameter(
net.minecraft.world.level.storage.loot.parameters.LootContextParams.ORIGIN,
Vec3.atCenterOf(pos)).create(LootContextParamSets.CHEST);
for (ItemStack stack : table.getRandomItems(params)) {
DefaultDispenseItemBehavior.spawnItem(level, stack, 2, Direction.DOWN,
Vec3.atBottomCenterOf(pos).relative(Direction.DOWN, 1.2D));
}
unlocked = true;
setChanged();
level.levelEvent(3011, pos, 0);
return true;
@Override
public void setLevel(Level level) {
super.setLevel(level);
this.vaultData.setLevel(level);
}
@Override
public void load(CompoundTag tag) {
super.load(tag);
unlocked = tag.getBoolean("Unlocked");
vaultData.load(tag);
vaultData.setConfig(createConfig());
}
@Override
protected void saveAdditional(CompoundTag tag) {
super.saveAdditional(tag);
tag.putBoolean("Unlocked", unlocked);
tag.merge(vaultData.save());
}
@Override
public CompoundTag getUpdateTag() {
return vaultData.save();
}
@Override
public Packet<ClientGamePacketListener> getUpdatePacket() {
return ClientboundBlockEntityDataPacket.create(this);
}
public UnderVaultData getVaultData() {
return vaultData;
}
}
@@ -0,0 +1,38 @@
package com.craisinlord.antarchy.content.block.entity;
import net.minecraft.world.entity.MobSpawnType;
public final class UndertrialSpawnContext implements AutoCloseable {
private static final ThreadLocal<Integer> DEPTH = ThreadLocal.withInitial(() -> 0);
private boolean closed;
private UndertrialSpawnContext() {
DEPTH.set(DEPTH.get() + 1);
}
public static UndertrialSpawnContext enter() {
return new UndertrialSpawnContext();
}
public static boolean isActive() {
return DEPTH.get() > 0;
}
public static boolean isTrialSpawn(MobSpawnType spawnReason) {
return spawnReason == MobSpawnType.SPAWNER && isActive();
}
@Override
public void close() {
if (!closed) {
closed = true;
int depth = DEPTH.get() - 1;
if (depth <= 0) {
DEPTH.remove();
} else {
DEPTH.set(depth);
}
}
}
}
@@ -1,113 +1,242 @@
package com.craisinlord.antarchy.content.block.entity;
import com.craisinlord.antarchy.Antarchy;
import com.craisinlord.antarchy.content.AntarchyObjects;
import com.craisinlord.antarchy.content.block.UndertrialSpawnerBlock;
import com.craisinlord.antarchy.content.block.entity.undertrial.UndertrialPlayerDetector;
import com.craisinlord.antarchy.content.block.entity.undertrial.UndertrialSpawner;
import com.craisinlord.antarchy.content.block.entity.undertrial.UndertrialSpawnerConfig;
import com.craisinlord.antarchy.content.block.entity.undertrial.UndertrialSpawnerEffects;
import com.craisinlord.antarchy.content.block.entity.undertrial.UndertrialSpawnerState;
import com.craisinlord.anthology.effect.AnthologyEffects;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.core.dispenser.DefaultDispenseItemBehavior;
import net.minecraft.core.particles.SimpleParticleType;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.Tag;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.util.random.SimpleWeightedRandomList;
import net.minecraft.world.effect.MobEffect;
import net.minecraft.world.effect.MobEffectInstance;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.Mob;
import net.minecraft.world.entity.monster.Zombie;
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.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.storage.loot.parameters.LootContextParamSets;
import net.minecraft.world.level.storage.loot.LootParams;
import net.minecraft.world.level.storage.loot.LootTable;
import net.minecraft.world.level.storage.loot.parameters.LootContextParam;
import net.minecraft.world.level.storage.loot.parameters.LootContextParamSets;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
import net.minecraft.core.dispenser.DefaultDispenseItemBehavior;
import java.util.List;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import java.util.function.Supplier;
public final class UndertrialSpawnerBlockEntity extends BlockEntity {
private static final String COOLDOWN_TAG = "Cooldown";
private static final String WAVES_TAG = "Waves";
private static final int MAX_WAVES = 6;
private int cooldown;
private int waves;
public final class UndertrialSpawnerBlockEntity extends BlockEntity implements UndertrialSpawner.StateAccessor {
private static final String SPAWNER_TAG = "trial_spawner";
private static final int DEFAULT_TARGET_COOLDOWN_LENGTH = 36000;
private static final int EFFECT_DURATION = 3600;
private UndertrialSpawner trialSpawner;
private UndertrialSpawnerState previousClientState;
private double clientSpin;
private double clientOldSpin;
private SimpleWeightedRandomList<ResourceLocation> normalLootTables = SimpleWeightedRandomList.empty();
private SimpleWeightedRandomList<ResourceLocation> ominousLootTables = SimpleWeightedRandomList.empty();
private Tag normalLootTablesTag;
private Tag ominousLootTablesTag;
public UndertrialSpawnerBlockEntity(BlockPos pos, BlockState state,
Supplier<? extends BlockEntityType<UndertrialSpawnerBlockEntity>> type) {
public UndertrialSpawnerBlockEntity(BlockPos pos, BlockState state, Supplier<? extends BlockEntityType<UndertrialSpawnerBlockEntity>> type) {
super(type.get(), pos, state);
this.trialSpawner = new UndertrialSpawner(this, UndertrialPlayerDetector.INCLUDING_CREATIVE_PLAYERS,
UndertrialPlayerDetector.EntitySelector.SELECT_FROM_LEVEL);
this.trialSpawner.overridePeacefulAndMobSpawnRule();
this.previousClientState = state.getValue(UndertrialSpawnerBlock.STATE);
}
public static void tick(Level level, BlockPos pos, BlockState state, UndertrialSpawnerBlockEntity blockEntity) {
if (!(level instanceof ServerLevel serverLevel)) {
if (level.isClientSide && state.getValue(UndertrialSpawnerBlock.ACTIVE)
&& level.random.nextInt(2) == 0) {
level.addParticle(particle("undertrial_omen"), pos.getX() + 0.5D + (level.random.nextDouble() - 0.5D) * 0.7D,
public static void serverTick(ServerLevel level, BlockPos pos, BlockState state, UndertrialSpawnerBlockEntity blockEntity) {
Set<UUID> before = new HashSet<>();
for (Mob mob : level.getEntitiesOfClass(Mob.class, new AABB(pos).inflate(16.0D))) {
before.add(mob.getUUID());
}
UndertrialSpawnerState previousState = blockEntity.getState();
boolean ominous = state.getValue(UndertrialSpawnerBlock.OMINOUS);
boolean rewardReady = previousState == UndertrialSpawnerState.EJECTING_REWARD
&& blockEntity.trialSpawner.getData().isReadyToEjectItems(level, 30.0F, blockEntity.trialSpawner.getTargetCooldownLength());
try (UndertrialSpawnContext ignored = UndertrialSpawnContext.enter()) {
blockEntity.trialSpawner.tickServer(level, pos, ominous);
}
if (rewardReady && blockEntity.getState() == UndertrialSpawnerState.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);
}
}
}
public static void clientTick(Level level, BlockPos pos, BlockState state, UndertrialSpawnerBlockEntity blockEntity) {
UndertrialSpawnerState currentState = state.getValue(UndertrialSpawnerBlock.STATE);
blockEntity.clientOldSpin = blockEntity.clientSpin;
if (currentState == UndertrialSpawnerState.ACTIVE || currentState == UndertrialSpawnerState.WAITING_FOR_REWARD_EJECTION) {
blockEntity.clientSpin = (blockEntity.clientSpin + 4.0D) % 360.0D;
}
if (currentState == UndertrialSpawnerState.ACTIVE || currentState == UndertrialSpawnerState.WAITING_FOR_REWARD_EJECTION) {
if (level.random.nextInt(2) == 0) {
level.addParticle(blockEntity.particle("undertrial_omen"), pos.getX() + 0.5D + (level.random.nextDouble() - 0.5D) * 0.7D,
pos.getY() + 0.45D + level.random.nextDouble() * 0.35D,
pos.getZ() + 0.5D + (level.random.nextDouble() - 0.5D) * 0.7D, 0.0D, 0.01D, 0.0D);
}
return;
}
if (blockEntity.cooldown > 0) {
blockEntity.cooldown--;
return;
}
if (!state.getValue(UndertrialSpawnerBlock.ACTIVE)) {
return;
}
if (serverLevel.getNearestPlayer(pos.getX() + 0.5D, pos.getY() - 0.5D, pos.getZ() + 0.5D, 16.0D, false) == null) {
return;
}
long livingTrialMobs = serverLevel.getEntitiesOfClass(Mob.class, new AABB(pos).inflate(16.0D),
mob -> mob.getTags().contains("antarchy_undertrial_mob")).size();
if (livingTrialMobs > 0) {
return;
}
if (blockEntity.waves >= MAX_WAVES) {
blockEntity.ejectReward(serverLevel, pos);
serverLevel.setBlock(pos, state.setValue(UndertrialSpawnerBlock.ACTIVE, false), 3);
return;
}
Zombie zombie = EntityType.ZOMBIE.create(serverLevel);
if (zombie != null) {
zombie.moveTo(pos.getX() + 0.5D, pos.getY() - 1.0D, pos.getZ() + 0.5D,
serverLevel.random.nextFloat() * 360.0F, 0.0F);
zombie.addTag("antarchy_undertrial_mob");
serverLevel.addFreshEntity(zombie);
blockEntity.waves++;
blockEntity.cooldown = 80;
blockEntity.setChanged();
} else if (currentState == UndertrialSpawnerState.WAITING_FOR_PLAYERS && blockEntity.previousClientState != currentState) {
ResourceLocation detectionSound = new ResourceLocation(Antarchy.MODID,
level.random.nextBoolean() ? "undertrial_detect_player1" : "undertrial_detect_player2");
level.playSound(null, pos, BuiltInRegistries.SOUND_EVENT.get(detectionSound),
net.minecraft.sounds.SoundSource.BLOCKS, 1.0F, 1.0F);
for (int i = 0; i < 12; i++) {
level.addParticle(blockEntity.particle("undertrial_spawner_detection"), pos.getX() + 0.5D + (level.random.nextDouble() - 0.5D) * 1.4D,
pos.getY() + 0.15D + level.random.nextDouble() * 0.7D,
pos.getZ() + 0.5D + (level.random.nextDouble() - 0.5D) * 1.4D, 0.0D, 0.0D, 0.0D);
}
}
blockEntity.previousClientState = currentState;
}
private static SimpleParticleType particle(String id) {
private SimpleParticleType particle(String id) {
return (SimpleParticleType) BuiltInRegistries.PARTICLE_TYPE.get(new ResourceLocation(Antarchy.MODID, id));
}
private void ejectReward(ServerLevel level, BlockPos pos) {
ResourceLocation id = new ResourceLocation(Antarchy.MODID, "spawners/undertrial");
LootTable table = level.getServer().getLootData().getLootTable(id);
LootParams params = new LootParams.Builder(level).create(LootContextParamSets.EMPTY);
for (ItemStack stack : table.getRandomItems(params)) {
DefaultDispenseItemBehavior.spawnItem(level, stack, 2, Direction.DOWN,
Vec3.atBottomCenterOf(pos).relative(Direction.DOWN, 1.2D));
private void applyUndertrialEffects(Mob mob) {
mob.addEffect(new MobEffectInstance(AntarchyObjects.INVERTED_EFFECT.get(), EFFECT_DURATION, 0, false, true, true));
int roll = mob.getRandom().nextInt(100);
if (roll < 30) {
mob.addEffect(new MobEffectInstance(AnthologyEffects.contracted(), EFFECT_DURATION, 0, false, true, true));
} else if (roll < 50) {
mob.addEffect(new MobEffectInstance(effect("shrinking"), EFFECT_DURATION, 0, false, true, true));
} else if (roll < 65) {
mob.addEffect(new MobEffectInstance(effect("growth"), EFFECT_DURATION, 0, false, true, true));
}
level.levelEvent(3014, pos, 0);
}
private static MobEffect effect(String path) {
MobEffect effect = BuiltInRegistries.MOB_EFFECT.get(new ResourceLocation(Antarchy.MODID, path));
if (effect == null) {
throw new IllegalStateException("Missing mob effect " + Antarchy.MODID + ":" + path);
}
return effect;
}
@Override
public void load(CompoundTag tag) {
super.load(tag);
cooldown = tag.getInt(COOLDOWN_TAG);
waves = tag.getInt(WAVES_TAG);
Tag spawnerTag = tag.contains(SPAWNER_TAG) ? tag.get(SPAWNER_TAG) : tag;
if (spawnerTag instanceof CompoundTag compound) {
UndertrialSpawner decoded = trialSpawner.load(compound);
captureLootTables(decoded, spawnerTag);
trialSpawner = createRewardlessSpawner(decoded);
}
}
@Override
public CompoundTag getUpdateTag() {
CompoundTag tag = new CompoundTag();
saveAdditional(tag);
return tag;
}
@Override
protected void saveAdditional(CompoundTag tag) {
super.saveAdditional(tag);
tag.putInt(COOLDOWN_TAG, cooldown);
tag.putInt(WAVES_TAG, waves);
CompoundTag spawnerTag = trialSpawner.save();
if (normalLootTablesTag != null) {
spawnerTag.getCompound(UndertrialSpawner.NORMAL_CONFIG_TAG_NAME).put("loot_tables_to_eject", normalLootTablesTag.copy());
}
if (ominousLootTablesTag != null) {
spawnerTag.getCompound(UndertrialSpawner.OMINOUS_CONFIG_TAG_NAME).put("loot_tables_to_eject", ominousLootTablesTag.copy());
}
tag.put(SPAWNER_TAG, spawnerTag);
}
@Override
public void setState(Level level, UndertrialSpawnerState state) {
level.setBlockAndUpdate(worldPosition, getBlockState().setValue(UndertrialSpawnerBlock.STATE, state));
}
@Override
public UndertrialSpawnerState getState() {
return getBlockState().getValue(UndertrialSpawnerBlock.STATE);
}
@Override
public void markUpdated() {
setChanged();
if (level != null) {
level.sendBlockUpdated(worldPosition, getBlockState(), getBlockState(), 3);
}
}
public UndertrialSpawner getTrialSpawner() {
return trialSpawner;
}
public void setSpawnEntity(EntityType<?> entityType) {
if (this.level == null) {
return;
}
this.trialSpawner.getData().setEntityId(this.trialSpawner, this.level.random, entityType);
this.markUpdated();
}
public double getClientSpin(float partialTick) {
return this.clientOldSpin + (this.clientSpin - this.clientOldSpin) * partialTick;
}
private void captureLootTables(UndertrialSpawner source, Tag spawnerTag) {
normalLootTables = source.getNormalConfig().lootTablesToEject();
ominousLootTables = source.getOminousConfig().lootTablesToEject();
if (spawnerTag instanceof CompoundTag compound) {
CompoundTag normalConfig = compound.getCompound(UndertrialSpawner.NORMAL_CONFIG_TAG_NAME);
CompoundTag ominousConfig = compound.getCompound(UndertrialSpawner.OMINOUS_CONFIG_TAG_NAME);
normalLootTablesTag = normalConfig.get("loot_tables_to_eject");
ominousLootTablesTag = ominousConfig.get("loot_tables_to_eject");
}
}
private UndertrialSpawner createRewardlessSpawner(UndertrialSpawner source) {
UndertrialSpawner rewardless = new UndertrialSpawner(withoutLootTables(source.getNormalConfig()), withoutLootTables(source.getOminousConfig()),
source.getData(), targetCooldownLength(source), source.getRequiredPlayerRange(), this,
source.getPlayerDetector(), source.getEntitySelector());
rewardless.overridePeacefulAndMobSpawnRule();
return rewardless;
}
private static int targetCooldownLength(UndertrialSpawner source) {
int targetCooldownLength = source.getTargetCooldownLength();
return targetCooldownLength > 0 ? targetCooldownLength : DEFAULT_TARGET_COOLDOWN_LENGTH;
}
private static UndertrialSpawnerConfig withoutLootTables(UndertrialSpawnerConfig config) {
return new UndertrialSpawnerConfig(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<ResourceLocation> lootTables = ominous ? ominousLootTables : normalLootTables;
lootTables.getRandomValue(level.getRandom()).ifPresent(key -> {
LootTable lootTable = level.getServer().getLootData().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));
}
UndertrialSpawnerEffects.ejectItem(level, pos);
});
}
}
@@ -0,0 +1,130 @@
package com.craisinlord.antarchy.content.block.entity.undertrial;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.FloatTag;
import net.minecraft.nbt.NumericTag;
import net.minecraft.nbt.Tag;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.EquipmentSlot;
import net.minecraft.world.entity.Mob;
import net.minecraft.world.item.Equipable;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.storage.loot.BuiltInLootTables;
import net.minecraft.world.level.storage.loot.LootParams;
import net.minecraft.world.level.storage.loot.LootTable;
import net.minecraft.world.level.storage.loot.parameters.LootContextParamSet;
import net.minecraft.world.level.storage.loot.parameters.LootContextParams;
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
public record UndertrialEquipmentTable(ResourceLocation lootTable, Map<EquipmentSlot, Float> slotDropChances) {
private static final LootContextParamSet EQUIPMENT = LootContextParamSet.builder()
.required(LootContextParams.ORIGIN)
.required(LootContextParams.THIS_ENTITY)
.build();
public static Optional<UndertrialEquipmentTable> load(Tag tag) {
if (!(tag instanceof CompoundTag compound) || !compound.contains("loot_table", Tag.TAG_STRING)) {
return Optional.empty();
}
ResourceLocation lootTable = ResourceLocation.tryParse(compound.getString("loot_table"));
if (lootTable == null) {
return Optional.empty();
}
Map<EquipmentSlot, Float> chances = new EnumMap<>(EquipmentSlot.class);
Tag chancesTag = compound.get("slot_drop_chances");
if (chancesTag instanceof NumericTag numeric) {
for (EquipmentSlot slot : EquipmentSlot.values()) {
chances.put(slot, numeric.getAsFloat());
}
} else if (chancesTag instanceof CompoundTag chanceMap) {
for (String key : chanceMap.getAllKeys()) {
EquipmentSlot slot = slotByName(key);
if (slot == null || !(chanceMap.get(key) instanceof NumericTag value)) {
return Optional.empty();
}
chances.put(slot, value.getAsFloat());
}
} else if (chancesTag != null) {
return Optional.empty();
}
return Optional.of(new UndertrialEquipmentTable(lootTable, chances));
}
private static EquipmentSlot slotByName(String name) {
for (EquipmentSlot slot : EquipmentSlot.values()) {
if (slot.getName().equals(name)) {
return slot;
}
}
return null;
}
public CompoundTag save() {
CompoundTag tag = new CompoundTag();
tag.putString("loot_table", this.lootTable.toString());
if (!this.slotDropChances.isEmpty()) {
Set<Float> distinct = new HashSet<>(this.slotDropChances.values());
if (distinct.size() == 1 && this.slotDropChances.keySet().containsAll(List.of(EquipmentSlot.values()))) {
tag.put("slot_drop_chances", FloatTag.valueOf(distinct.iterator().next()));
} else {
CompoundTag map = new CompoundTag();
for (Map.Entry<EquipmentSlot, Float> entry : this.slotDropChances.entrySet()) {
map.putFloat(entry.getKey().getName(), entry.getValue());
}
tag.put("slot_drop_chances", map);
}
}
return tag;
}
public void equip(Mob mob) {
if (!(mob.level() instanceof ServerLevel level) || this.lootTable.equals(BuiltInLootTables.EMPTY)) {
return;
}
LootTable table = level.getServer().getLootData().getLootTable(this.lootTable);
if (table == LootTable.EMPTY) {
return;
}
LootParams params = new LootParams.Builder(level)
.withParameter(LootContextParams.ORIGIN, mob.position())
.withParameter(LootContextParams.THIS_ENTITY, mob)
.create(EQUIPMENT);
List<EquipmentSlot> used = new ArrayList<>();
for (ItemStack stack : table.getRandomItems(params, 0L)) {
EquipmentSlot slot = resolveSlot(stack, used);
if (slot != null) {
ItemStack equipped = slot.isArmor() ? stack.split(1) : stack;
mob.setItemSlot(slot, equipped);
Float chance = this.slotDropChances.get(slot);
if (chance != null) {
mob.setDropChance(slot, chance);
}
used.add(slot);
}
}
}
private static EquipmentSlot resolveSlot(ItemStack stack, List<EquipmentSlot> used) {
if (stack.isEmpty()) {
return null;
}
Equipable equipable = Equipable.get(stack);
if (equipable != null) {
EquipmentSlot slot = equipable.getEquipmentSlot();
if (!used.contains(slot)) {
return slot;
}
} else if (!used.contains(EquipmentSlot.MAINHAND)) {
return EquipmentSlot.MAINHAND;
}
return null;
}
}
@@ -0,0 +1,70 @@
package com.craisinlord.antarchy.content.block.entity.undertrial;
import net.minecraft.world.effect.MobEffectInstance;
import net.minecraft.world.effect.MobEffects;
import net.minecraft.world.entity.player.Player;
import java.util.ArrayList;
import java.util.List;
public final class UndertrialOmen {
public static final int TRIAL_OMEN_PER_BAD_OMEN_LEVEL = 18000;
private static final String TAG_PREFIX = "antarchy.trial_omen.";
private UndertrialOmen() {
}
public static boolean hasTrialOmen(Player player) {
long now = player.level().getGameTime();
boolean active = false;
List<String> expired = new ArrayList<>();
for (String tag : player.getTags()) {
if (tag.startsWith(TAG_PREFIX)) {
long expiresAt = parseExpiry(tag);
if (expiresAt > now) {
active = true;
} else {
expired.add(tag);
}
}
}
for (String tag : expired) {
player.removeTag(tag);
}
return active;
}
public static boolean hasBadOmen(Player player) {
return player.hasEffect(MobEffects.BAD_OMEN);
}
public static void transformBadOmenIntoTrialOmen(Player player) {
MobEffectInstance badOmen = player.getEffect(MobEffects.BAD_OMEN);
if (badOmen != null) {
int duration = TRIAL_OMEN_PER_BAD_OMEN_LEVEL * (badOmen.getAmplifier() + 1);
player.removeEffect(MobEffects.BAD_OMEN);
grantTrialOmen(player, duration);
}
}
public static void grantTrialOmen(Player player, int duration) {
List<String> existing = new ArrayList<>();
for (String tag : player.getTags()) {
if (tag.startsWith(TAG_PREFIX)) {
existing.add(tag);
}
}
for (String tag : existing) {
player.removeTag(tag);
}
player.addTag(TAG_PREFIX + (player.level().getGameTime() + duration));
}
private static long parseExpiry(String tag) {
try {
return Long.parseLong(tag.substring(TAG_PREFIX.length()));
} catch (NumberFormatException exception) {
return Long.MIN_VALUE;
}
}
}
@@ -0,0 +1,48 @@
package com.craisinlord.antarchy.content.block.entity.undertrial;
import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.ClipContext;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.HitResult;
import net.minecraft.world.phys.Vec3;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.function.Predicate;
public interface UndertrialPlayerDetector {
UndertrialPlayerDetector NO_CREATIVE_PLAYERS = (level, selector, pos, range, requireLineOfSight) -> collect(level, pos,
selector.getPlayers(level, player -> player.blockPosition().closerThan(pos, range) && !player.isCreative() && !player.isSpectator()),
requireLineOfSight);
UndertrialPlayerDetector INCLUDING_CREATIVE_PLAYERS = (level, selector, pos, range, requireLineOfSight) -> collect(level, pos,
selector.getPlayers(level, player -> player.blockPosition().closerThan(pos, range) && !player.isSpectator()),
requireLineOfSight);
List<UUID> detect(ServerLevel level, EntitySelector selector, BlockPos pos, double range, boolean requireLineOfSight);
private static List<UUID> collect(ServerLevel level, BlockPos pos, List<? extends Player> players, boolean requireLineOfSight) {
List<UUID> result = new ArrayList<>();
for (Player player : players) {
if (!requireLineOfSight || inLineOfSight(level, pos.getCenter(), player.getEyePosition())) {
result.add(player.getUUID());
}
}
return List.copyOf(result);
}
static boolean inLineOfSight(Level level, Vec3 target, Vec3 from) {
BlockHitResult hit = level.clip(new ClipContext(from, target, ClipContext.Block.VISUAL, ClipContext.Fluid.NONE, (Entity) null));
return hit.getBlockPos().equals(BlockPos.containing(target)) || hit.getType() == HitResult.Type.MISS;
}
interface EntitySelector {
EntitySelector SELECT_FROM_LEVEL = (level, predicate) -> level.getPlayers(predicate::test);
List<? extends Player> getPlayers(ServerLevel level, Predicate<? super Player> predicate);
}
}
@@ -0,0 +1,62 @@
package com.craisinlord.antarchy.content.block.entity.undertrial;
import net.minecraft.core.BlockPos;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.sounds.SoundSource;
import net.minecraft.world.level.Level;
import java.util.function.Supplier;
public final class UndertrialSounds {
public static final Entry TRIAL_SPAWNER_SPAWN_MOB = new Entry("block.trial_spawner.spawn_mob", () -> SoundEvents.FIRECHARGE_USE);
public static final Entry TRIAL_SPAWNER_DETECT_PLAYER = new Entry("block.trial_spawner.detect_player", () -> SoundEvents.RESPAWN_ANCHOR_CHARGE);
public static final Entry TRIAL_SPAWNER_EJECT_ITEM = new Entry("block.trial_spawner.eject_item", () -> SoundEvents.DISPENSER_DISPENSE);
public static final Entry TRIAL_SPAWNER_OPEN_SHUTTER = new Entry("block.trial_spawner.open_shutter", () -> SoundEvents.IRON_TRAPDOOR_OPEN);
public static final Entry TRIAL_SPAWNER_CLOSE_SHUTTER = new Entry("block.trial_spawner.close_shutter", () -> SoundEvents.IRON_TRAPDOOR_CLOSE);
public static final Entry TRIAL_SPAWNER_OMINOUS_ACTIVATE = new Entry("block.trial_spawner.ominous_activate", () -> SoundEvents.RESPAWN_ANCHOR_SET_SPAWN);
public static final Entry TRIAL_SPAWNER_SPAWN_ITEM_BEGIN = new Entry("block.trial_spawner.spawn_item_begin", () -> SoundEvents.AMETHYST_BLOCK_CHIME);
public static final Entry VAULT_AMBIENT = new Entry("block.vault.ambient", () -> null);
public static final Entry VAULT_DEACTIVATE = new Entry("block.vault.deactivate", () -> SoundEvents.IRON_DOOR_CLOSE);
public static final Entry VAULT_INSERT_ITEM = new Entry("block.vault.insert_item", () -> SoundEvents.CHAIN_PLACE);
public static final Entry VAULT_INSERT_ITEM_FAIL = new Entry("block.vault.insert_item_fail", () -> SoundEvents.DISPENSER_FAIL);
public static final Entry VAULT_REJECT_REWARDED_PLAYER = new Entry("block.vault.reject_rewarded_player", () -> SoundEvents.DISPENSER_FAIL);
public static final Entry VAULT_OPEN_SHUTTER = new Entry("block.vault.open_shutter", () -> SoundEvents.IRON_TRAPDOOR_OPEN);
public static final Entry VAULT_CLOSE_SHUTTER = new Entry("block.vault.close_shutter", () -> SoundEvents.IRON_TRAPDOOR_CLOSE);
public static final Entry VAULT_EJECT_ITEM = new Entry("block.vault.eject_item", () -> SoundEvents.DISPENSER_DISPENSE);
private UndertrialSounds() {
}
public static void playServer(ServerLevel level, BlockPos pos, Entry entry, SoundSource source, float volume, float pitch) {
SoundEvent sound = entry.get();
if (sound != null) {
level.playSound(null, pos, sound, source, volume, pitch);
}
}
public static void playLocal(Level level, BlockPos pos, Entry entry, SoundSource source, float volume, float pitch, boolean distanceDelay) {
SoundEvent sound = entry.get();
if (sound != null) {
level.playLocalSound(pos, sound, source, volume, pitch, distanceDelay);
}
}
public static final class Entry {
private final ResourceLocation id;
private final Supplier<SoundEvent> fallback;
private Entry(String path, Supplier<SoundEvent> fallback) {
this.id = new ResourceLocation("minecraft", path);
this.fallback = fallback;
}
public SoundEvent get() {
SoundEvent registered = BuiltInRegistries.SOUND_EVENT.getOptional(this.id).orElse(null);
return registered != null ? registered : this.fallback.get();
}
}
}
@@ -0,0 +1,102 @@
package com.craisinlord.antarchy.content.block.entity.undertrial;
import net.minecraft.core.BlockPos;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.NbtOps;
import net.minecraft.nbt.Tag;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.util.random.SimpleWeightedRandomList;
import net.minecraft.util.random.WeightedEntry;
import net.minecraft.world.level.LightLayer;
import net.minecraft.world.level.SpawnData;
import java.util.Optional;
public record UndertrialSpawnData(CompoundTag entityToSpawn, Optional<SpawnData.CustomSpawnRules> customSpawnRules,
Optional<UndertrialEquipmentTable> equipment) {
public static final String ENTITY_TAG = "entity";
public UndertrialSpawnData() {
this(new CompoundTag(), Optional.empty(), Optional.empty());
}
public CompoundTag getEntityToSpawn() {
return this.entityToSpawn;
}
public Optional<SpawnData.CustomSpawnRules> getCustomSpawnRules() {
return this.customSpawnRules;
}
public Optional<UndertrialEquipmentTable> getEquipment() {
return this.equipment;
}
public static boolean isValidPosition(SpawnData.CustomSpawnRules rules, BlockPos pos, ServerLevel level) {
return rules.blockLightLimit().isValueInRange(level.getBrightness(LightLayer.BLOCK, pos))
&& rules.skyLightLimit().isValueInRange(level.getBrightness(LightLayer.SKY, pos));
}
public static Optional<UndertrialSpawnData> load(Tag tag) {
if (!(tag instanceof CompoundTag compound) || !compound.contains(ENTITY_TAG, Tag.TAG_COMPOUND)) {
return Optional.empty();
}
Optional<SpawnData.CustomSpawnRules> rules = Optional.empty();
if (compound.contains("custom_spawn_rules")) {
Optional<SpawnData.CustomSpawnRules> parsed = SpawnData.CustomSpawnRules.CODEC
.parse(NbtOps.INSTANCE, compound.get("custom_spawn_rules")).result();
if (parsed.isEmpty()) {
return Optional.empty();
}
rules = parsed;
}
Optional<UndertrialEquipmentTable> equipment = Optional.empty();
if (compound.contains("equipment")) {
equipment = UndertrialEquipmentTable.load(compound.get("equipment"));
if (equipment.isEmpty()) {
return Optional.empty();
}
}
return Optional.of(new UndertrialSpawnData(compound.getCompound(ENTITY_TAG), rules, equipment));
}
public CompoundTag save() {
CompoundTag tag = new CompoundTag();
tag.put(ENTITY_TAG, this.entityToSpawn);
this.customSpawnRules.flatMap(rules -> SpawnData.CustomSpawnRules.CODEC.encodeStart(NbtOps.INSTANCE, rules).result())
.ifPresent(encoded -> tag.put("custom_spawn_rules", encoded));
this.equipment.ifPresent(table -> tag.put("equipment", table.save()));
return tag;
}
public static Optional<SimpleWeightedRandomList<UndertrialSpawnData>> loadList(Tag tag) {
if (!(tag instanceof ListTag list)) {
return Optional.empty();
}
SimpleWeightedRandomList.Builder<UndertrialSpawnData> builder = SimpleWeightedRandomList.builder();
for (Tag element : list) {
if (!(element instanceof CompoundTag entry) || !entry.contains("weight", Tag.TAG_ANY_NUMERIC)) {
return Optional.empty();
}
Optional<UndertrialSpawnData> data = load(entry.get("data"));
int weight = entry.getInt("weight");
if (data.isEmpty() || weight < 0) {
return Optional.empty();
}
builder.add(data.get(), weight);
}
return Optional.of(builder.build());
}
public static ListTag saveList(SimpleWeightedRandomList<UndertrialSpawnData> list) {
ListTag tag = new ListTag();
for (WeightedEntry.Wrapper<UndertrialSpawnData> wrapper : list.unwrap()) {
CompoundTag entry = new CompoundTag();
entry.put("data", wrapper.getData().save());
entry.putInt("weight", wrapper.getWeight().asInt());
tag.add(entry);
}
return tag;
}
}
@@ -0,0 +1,282 @@
package com.craisinlord.antarchy.content.block.entity.undertrial;
import com.craisinlord.antarchy.content.block.UndertrialSpawnerBlock;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.dispenser.DefaultDispenseItemBehavior;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.Tag;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.util.Mth;
import net.minecraft.util.RandomSource;
import net.minecraft.world.Difficulty;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.Mob;
import net.minecraft.world.entity.MobSpawnType;
import net.minecraft.world.entity.SpawnPlacements;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.GameRules;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.gameevent.GameEvent;
import net.minecraft.world.level.storage.loot.LootParams;
import net.minecraft.world.level.storage.loot.LootTable;
import net.minecraft.world.level.storage.loot.parameters.LootContextParamSets;
import net.minecraft.world.phys.Vec3;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
public final class UndertrialSpawner {
public static final String NORMAL_CONFIG_TAG_NAME = "normal_config";
public static final String OMINOUS_CONFIG_TAG_NAME = "ominous_config";
public static final int DETECT_PLAYER_SPAWN_BUFFER = 40;
public static final int DEFAULT_TARGET_COOLDOWN_LENGTH = 36000;
public static final int DEFAULT_PLAYER_SCAN_RANGE = 14;
private static final int MAX_MOB_TRACKING_DISTANCE = 47;
private static final int MAX_MOB_TRACKING_DISTANCE_SQR = Mth.square(MAX_MOB_TRACKING_DISTANCE);
private final UndertrialSpawnerConfig normalConfig;
private final UndertrialSpawnerConfig ominousConfig;
private final UndertrialSpawnerData data;
private final int requiredPlayerRange;
private final int targetCooldownLength;
private final StateAccessor stateAccessor;
private final UndertrialPlayerDetector playerDetector;
private final UndertrialPlayerDetector.EntitySelector entitySelector;
private boolean overridePeacefulAndMobSpawnRule;
private boolean isOminous;
public UndertrialSpawner(StateAccessor stateAccessor, UndertrialPlayerDetector playerDetector,
UndertrialPlayerDetector.EntitySelector entitySelector) {
this(UndertrialSpawnerConfig.DEFAULT, UndertrialSpawnerConfig.DEFAULT, new UndertrialSpawnerData(),
DEFAULT_TARGET_COOLDOWN_LENGTH, DEFAULT_PLAYER_SCAN_RANGE, stateAccessor, playerDetector, entitySelector);
}
public UndertrialSpawner(UndertrialSpawnerConfig normalConfig, UndertrialSpawnerConfig ominousConfig, UndertrialSpawnerData data,
int targetCooldownLength, int requiredPlayerRange, StateAccessor stateAccessor,
UndertrialPlayerDetector playerDetector, UndertrialPlayerDetector.EntitySelector entitySelector) {
this.normalConfig = normalConfig;
this.ominousConfig = ominousConfig;
this.data = data;
this.targetCooldownLength = targetCooldownLength;
this.requiredPlayerRange = requiredPlayerRange;
this.stateAccessor = stateAccessor;
this.playerDetector = playerDetector;
this.entitySelector = entitySelector;
}
public UndertrialSpawner load(CompoundTag tag) {
UndertrialSpawnerConfig normal = tag.contains(NORMAL_CONFIG_TAG_NAME, Tag.TAG_COMPOUND)
? UndertrialSpawnerConfig.load(tag.getCompound(NORMAL_CONFIG_TAG_NAME)) : UndertrialSpawnerConfig.DEFAULT;
UndertrialSpawnerConfig ominous = tag.contains(OMINOUS_CONFIG_TAG_NAME, Tag.TAG_COMPOUND)
? UndertrialSpawnerConfig.load(tag.getCompound(OMINOUS_CONFIG_TAG_NAME)) : UndertrialSpawnerConfig.DEFAULT;
UndertrialSpawnerData loadedData = UndertrialSpawnerData.load(tag);
int cooldown = DEFAULT_TARGET_COOLDOWN_LENGTH;
if (tag.contains("target_cooldown_length", Tag.TAG_ANY_NUMERIC) && tag.getInt("target_cooldown_length") >= 0) {
cooldown = tag.getInt("target_cooldown_length");
}
int range = DEFAULT_PLAYER_SCAN_RANGE;
if (tag.contains("required_player_range", Tag.TAG_ANY_NUMERIC)) {
int value = tag.getInt("required_player_range");
if (value >= 1 && value <= 128) {
range = value;
}
}
return new UndertrialSpawner(normal, ominous, loadedData, cooldown, range, this.stateAccessor, this.playerDetector, this.entitySelector);
}
public CompoundTag save() {
CompoundTag tag = new CompoundTag();
if (!this.normalConfig.equals(UndertrialSpawnerConfig.DEFAULT)) {
tag.put(NORMAL_CONFIG_TAG_NAME, this.normalConfig.save());
}
UndertrialSpawnerConfig ominous = this.getOminousConfigForSerialization();
if (!ominous.equals(UndertrialSpawnerConfig.DEFAULT)) {
tag.put(OMINOUS_CONFIG_TAG_NAME, ominous.save());
}
this.data.save(tag);
if (this.targetCooldownLength != DEFAULT_TARGET_COOLDOWN_LENGTH) {
tag.putInt("target_cooldown_length", this.targetCooldownLength);
}
if (this.requiredPlayerRange != DEFAULT_PLAYER_SCAN_RANGE) {
tag.putInt("required_player_range", this.requiredPlayerRange);
}
return tag;
}
public UndertrialSpawnerConfig getConfig() {
return this.isOminous ? this.ominousConfig : this.normalConfig;
}
public UndertrialSpawnerConfig getNormalConfig() {
return this.normalConfig;
}
public UndertrialSpawnerConfig getOminousConfig() {
return this.ominousConfig;
}
private UndertrialSpawnerConfig getOminousConfigForSerialization() {
return !this.ominousConfig.equals(this.normalConfig) ? this.ominousConfig : UndertrialSpawnerConfig.DEFAULT;
}
public void applyOminous(ServerLevel level, BlockPos pos) {
level.setBlock(pos, level.getBlockState(pos).setValue(UndertrialSpawnerBlock.OMINOUS, true), 3);
UndertrialSpawnerEffects.becomeOminous(level, pos, pos, 1);
this.isOminous = true;
this.data.resetAfterBecomingOminous(this, level, pos);
}
public void removeOminous(ServerLevel level, BlockPos pos) {
level.setBlock(pos, level.getBlockState(pos).setValue(UndertrialSpawnerBlock.OMINOUS, false), 3);
this.isOminous = false;
}
public boolean isOminous() {
return this.isOminous;
}
public UndertrialSpawnerData getData() {
return this.data;
}
public int getTargetCooldownLength() {
return this.targetCooldownLength;
}
public int getRequiredPlayerRange() {
return this.requiredPlayerRange;
}
public UndertrialSpawnerState getState() {
return this.stateAccessor.getState();
}
public void setState(Level level, UndertrialSpawnerState state) {
this.stateAccessor.setState(level, state);
}
public void markUpdated() {
this.stateAccessor.markUpdated();
}
public UndertrialPlayerDetector getPlayerDetector() {
return this.playerDetector;
}
public UndertrialPlayerDetector.EntitySelector getEntitySelector() {
return this.entitySelector;
}
public boolean canSpawnInLevel(Level level) {
if (this.overridePeacefulAndMobSpawnRule) {
return true;
}
return level.getDifficulty() != Difficulty.PEACEFUL && level.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING);
}
public Optional<UUID> spawnMob(ServerLevel level, BlockPos pos) {
RandomSource random = level.getRandom();
UndertrialSpawnData spawnData = this.data.getOrCreateNextSpawnData(this, level.getRandom());
CompoundTag entityTag = spawnData.entityToSpawn();
ListTag posList = entityTag.getList("Pos", Tag.TAG_DOUBLE);
Optional<EntityType<?>> type = EntityType.by(entityTag);
if (type.isEmpty()) {
return Optional.empty();
}
int size = posList.size();
double x = size >= 1 ? posList.getDouble(0)
: pos.getX() + (random.nextDouble() - random.nextDouble()) * this.getConfig().spawnRange() + 0.5D;
double y = size >= 2 ? posList.getDouble(1) : pos.getY() + random.nextInt(3) - 1;
double z = size >= 3 ? posList.getDouble(2)
: pos.getZ() + (random.nextDouble() - random.nextDouble()) * this.getConfig().spawnRange() + 0.5D;
if (!level.noCollision(type.get().getAABB(x, y, z))) {
return Optional.empty();
}
Vec3 spawnPos = new Vec3(x, y, z);
if (!UndertrialPlayerDetector.inLineOfSight(level, pos.getCenter(), spawnPos)) {
return Optional.empty();
}
BlockPos spawnBlockPos = BlockPos.containing(spawnPos);
if (!SpawnPlacements.checkSpawnRules(type.get(), level, MobSpawnType.SPAWNER, spawnBlockPos, level.getRandom())) {
return Optional.empty();
}
if (spawnData.getCustomSpawnRules().isPresent()
&& !UndertrialSpawnData.isValidPosition(spawnData.getCustomSpawnRules().get(), spawnBlockPos, level)) {
return Optional.empty();
}
Entity entity = EntityType.loadEntityRecursive(entityTag, level, loaded -> {
loaded.moveTo(x, y, z, random.nextFloat() * 360.0F, 0.0F);
return loaded;
});
if (entity == null) {
return Optional.empty();
}
if (entity instanceof Mob mob) {
if (!mob.checkSpawnObstruction(level)) {
return Optional.empty();
}
boolean onlyId = spawnData.getEntityToSpawn().size() == 1 && spawnData.getEntityToSpawn().contains("id", Tag.TAG_STRING);
if (onlyId) {
mob.finalizeSpawn(level, level.getCurrentDifficultyAt(mob.blockPosition()), MobSpawnType.SPAWNER, null, null);
}
mob.setPersistenceRequired();
spawnData.getEquipment().ifPresent(table -> table.equip(mob));
}
if (!level.tryAddFreshEntityWithPassengers(entity)) {
return Optional.empty();
}
int flame = this.isOminous ? UndertrialSpawnerEffects.FLAME_OMINOUS : UndertrialSpawnerEffects.FLAME_NORMAL;
UndertrialSpawnerEffects.spawnerSpawn(level, pos, flame);
UndertrialSpawnerEffects.mobSpawn(level, pos, spawnBlockPos, flame);
level.gameEvent(entity, GameEvent.ENTITY_PLACE, spawnBlockPos);
return Optional.of(entity.getUUID());
}
public void ejectReward(ServerLevel level, BlockPos pos, ResourceLocation lootTableId) {
LootTable lootTable = level.getServer().getLootData().getLootTable(lootTableId);
LootParams params = new LootParams.Builder(level).create(LootContextParamSets.EMPTY);
List<ItemStack> items = lootTable.getRandomItems(params);
if (!items.isEmpty()) {
for (ItemStack stack : items) {
DefaultDispenseItemBehavior.spawnItem(level, stack, 2, Direction.UP, Vec3.atBottomCenterOf(pos).relative(Direction.UP, 1.2D));
}
UndertrialSpawnerEffects.ejectItem(level, pos);
}
}
public void tickServer(ServerLevel level, BlockPos pos, boolean ominous) {
this.isOminous = ominous;
UndertrialSpawnerState state = this.getState();
if (this.data.currentMobs.removeIf(uuid -> shouldMobBeUntracked(level, pos, uuid))) {
this.data.nextMobSpawnsAt = level.getGameTime() + this.getConfig().ticksBetweenSpawn();
}
UndertrialSpawnerState next = state.tickAndGetNext(pos, this, level);
if (next != state) {
this.setState(level, next);
}
}
private static boolean shouldMobBeUntracked(ServerLevel level, BlockPos pos, UUID uuid) {
Entity entity = level.getEntity(uuid);
return entity == null
|| !entity.isAlive()
|| !entity.level().dimension().equals(level.dimension())
|| entity.blockPosition().distSqr(pos) > MAX_MOB_TRACKING_DISTANCE_SQR;
}
public void overridePeacefulAndMobSpawnRule() {
this.overridePeacefulAndMobSpawnRule = true;
}
public interface StateAccessor {
void setState(Level level, UndertrialSpawnerState state);
UndertrialSpawnerState getState();
void markUpdated();
}
}
@@ -0,0 +1,157 @@
package com.craisinlord.antarchy.content.block.entity.undertrial;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.Tag;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.random.SimpleWeightedRandomList;
import net.minecraft.util.random.WeightedEntry;
import java.util.Optional;
public record UndertrialSpawnerConfig(
int spawnRange,
float totalMobs,
float simultaneousMobs,
float totalMobsAddedPerPlayer,
float simultaneousMobsAddedPerPlayer,
int ticksBetweenSpawn,
SimpleWeightedRandomList<UndertrialSpawnData> spawnPotentialsDefinition,
SimpleWeightedRandomList<ResourceLocation> lootTablesToEject,
ResourceLocation itemsToDropWhenOminous
) {
public static final ResourceLocation SPAWNER_TRIAL_CHAMBER_CONSUMABLES = new ResourceLocation("minecraft", "spawners/trial_chamber/consumables");
public static final ResourceLocation SPAWNER_TRIAL_CHAMBER_KEY = new ResourceLocation("minecraft", "spawners/trial_chamber/key");
public static final ResourceLocation SPAWNER_TRIAL_ITEMS_TO_DROP_WHEN_OMINOUS = new ResourceLocation("minecraft", "spawners/trial_chamber/items_to_drop_when_ominous");
public static final UndertrialSpawnerConfig DEFAULT = new UndertrialSpawnerConfig(
4,
6.0F,
2.0F,
2.0F,
1.0F,
40,
SimpleWeightedRandomList.empty(),
SimpleWeightedRandomList.<ResourceLocation>builder().add(SPAWNER_TRIAL_CHAMBER_CONSUMABLES, 1).add(SPAWNER_TRIAL_CHAMBER_KEY, 1).build(),
SPAWNER_TRIAL_ITEMS_TO_DROP_WHEN_OMINOUS
);
public int calculateTargetTotalMobs(int additionalPlayers) {
return (int) Math.floor(this.totalMobs + this.totalMobsAddedPerPlayer * additionalPlayers);
}
public int calculateTargetSimultaneousMobs(int additionalPlayers) {
return (int) Math.floor(this.simultaneousMobs + this.simultaneousMobsAddedPerPlayer * additionalPlayers);
}
public long ticksBetweenItemSpawners() {
return 160L;
}
public static UndertrialSpawnerConfig load(CompoundTag tag) {
int spawnRange = DEFAULT.spawnRange;
if (tag.contains("spawn_range", Tag.TAG_ANY_NUMERIC)) {
int value = tag.getInt("spawn_range");
if (value >= 1 && value <= 128) {
spawnRange = value;
}
}
float totalMobs = nonNegativeFloat(tag, "total_mobs", DEFAULT.totalMobs);
float simultaneousMobs = nonNegativeFloat(tag, "simultaneous_mobs", DEFAULT.simultaneousMobs);
float totalMobsAddedPerPlayer = nonNegativeFloat(tag, "total_mobs_added_per_player", DEFAULT.totalMobsAddedPerPlayer);
float simultaneousMobsAddedPerPlayer = nonNegativeFloat(tag, "simultaneous_mobs_added_per_player", DEFAULT.simultaneousMobsAddedPerPlayer);
int ticksBetweenSpawn = DEFAULT.ticksBetweenSpawn;
if (tag.contains("ticks_between_spawn", Tag.TAG_ANY_NUMERIC) && tag.getInt("ticks_between_spawn") >= 0) {
ticksBetweenSpawn = tag.getInt("ticks_between_spawn");
}
SimpleWeightedRandomList<UndertrialSpawnData> potentials = DEFAULT.spawnPotentialsDefinition;
if (tag.contains("spawn_potentials")) {
potentials = UndertrialSpawnData.loadList(tag.get("spawn_potentials")).orElse(DEFAULT.spawnPotentialsDefinition);
}
SimpleWeightedRandomList<ResourceLocation> lootTables = DEFAULT.lootTablesToEject;
if (tag.contains("loot_tables_to_eject")) {
lootTables = loadLootTables(tag.get("loot_tables_to_eject")).orElse(DEFAULT.lootTablesToEject);
}
ResourceLocation itemsToDrop = DEFAULT.itemsToDropWhenOminous;
if (tag.contains("items_to_drop_when_ominous", Tag.TAG_STRING)) {
ResourceLocation parsed = ResourceLocation.tryParse(tag.getString("items_to_drop_when_ominous"));
if (parsed != null) {
itemsToDrop = parsed;
}
}
return new UndertrialSpawnerConfig(spawnRange, totalMobs, simultaneousMobs, totalMobsAddedPerPlayer,
simultaneousMobsAddedPerPlayer, ticksBetweenSpawn, potentials, lootTables, itemsToDrop);
}
private static float nonNegativeFloat(CompoundTag tag, String key, float fallback) {
if (tag.contains(key, Tag.TAG_ANY_NUMERIC)) {
float value = tag.getFloat(key);
if (value >= 0.0F) {
return value;
}
}
return fallback;
}
public CompoundTag save() {
CompoundTag tag = new CompoundTag();
if (this.spawnRange != DEFAULT.spawnRange) {
tag.putInt("spawn_range", this.spawnRange);
}
if (this.totalMobs != DEFAULT.totalMobs) {
tag.putFloat("total_mobs", this.totalMobs);
}
if (this.simultaneousMobs != DEFAULT.simultaneousMobs) {
tag.putFloat("simultaneous_mobs", this.simultaneousMobs);
}
if (this.totalMobsAddedPerPlayer != DEFAULT.totalMobsAddedPerPlayer) {
tag.putFloat("total_mobs_added_per_player", this.totalMobsAddedPerPlayer);
}
if (this.simultaneousMobsAddedPerPlayer != DEFAULT.simultaneousMobsAddedPerPlayer) {
tag.putFloat("simultaneous_mobs_added_per_player", this.simultaneousMobsAddedPerPlayer);
}
if (this.ticksBetweenSpawn != DEFAULT.ticksBetweenSpawn) {
tag.putInt("ticks_between_spawn", this.ticksBetweenSpawn);
}
if (this.spawnPotentialsDefinition != DEFAULT.spawnPotentialsDefinition) {
tag.put("spawn_potentials", UndertrialSpawnData.saveList(this.spawnPotentialsDefinition));
}
if (this.lootTablesToEject != DEFAULT.lootTablesToEject) {
tag.put("loot_tables_to_eject", saveLootTables(this.lootTablesToEject));
}
if (!this.itemsToDropWhenOminous.equals(DEFAULT.itemsToDropWhenOminous)) {
tag.putString("items_to_drop_when_ominous", this.itemsToDropWhenOminous.toString());
}
return tag;
}
public static Optional<SimpleWeightedRandomList<ResourceLocation>> loadLootTables(Tag tag) {
if (!(tag instanceof ListTag list)) {
return Optional.empty();
}
SimpleWeightedRandomList.Builder<ResourceLocation> builder = SimpleWeightedRandomList.builder();
for (Tag element : list) {
if (!(element instanceof CompoundTag entry) || !entry.contains("data", Tag.TAG_STRING)
|| !entry.contains("weight", Tag.TAG_ANY_NUMERIC)) {
return Optional.empty();
}
ResourceLocation id = ResourceLocation.tryParse(entry.getString("data"));
int weight = entry.getInt("weight");
if (id == null || weight < 0) {
return Optional.empty();
}
builder.add(id, weight);
}
return Optional.of(builder.build());
}
public static ListTag saveLootTables(SimpleWeightedRandomList<ResourceLocation> list) {
ListTag tag = new ListTag();
for (WeightedEntry.Wrapper<ResourceLocation> wrapper : list.unwrap()) {
CompoundTag entry = new CompoundTag();
entry.putString("data", wrapper.getData().toString());
entry.putInt("weight", wrapper.getWeight().asInt());
tag.add(entry);
}
return tag;
}
}
@@ -0,0 +1,320 @@
package com.craisinlord.antarchy.content.block.entity.undertrial;
import com.mojang.datafixers.util.Pair;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import net.minecraft.core.BlockPos;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.IntArrayTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.NbtUtils;
import net.minecraft.nbt.Tag;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.util.Mth;
import net.minecraft.util.RandomSource;
import net.minecraft.util.random.SimpleWeightedRandomList;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.EquipmentSlot;
import net.minecraft.world.entity.Mob;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.storage.loot.LootParams;
import net.minecraft.world.level.storage.loot.LootTable;
import net.minecraft.world.level.storage.loot.parameters.LootContextParamSets;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.function.Function;
public class UndertrialSpawnerData {
public static final String TAG_SPAWN_DATA = "spawn_data";
private static final String TAG_NEXT_MOB_SPAWNS_AT = "next_mob_spawns_at";
private static final int DELAY_BETWEEN_PLAYER_SCANS = 20;
protected final Set<UUID> detectedPlayers = new HashSet<>();
protected final Set<UUID> currentMobs = new HashSet<>();
protected long cooldownEndsAt;
protected long nextMobSpawnsAt;
protected int totalMobsSpawned;
protected Optional<UndertrialSpawnData> nextSpawnData;
protected Optional<ResourceLocation> ejectingLootTable;
protected Entity displayEntity;
private SimpleWeightedRandomList<ItemStack> dispensing;
public UndertrialSpawnerData() {
this(Set.of(), Set.of(), 0L, 0L, 0, Optional.empty(), Optional.empty());
}
public UndertrialSpawnerData(Set<UUID> detectedPlayers, Set<UUID> currentMobs, long cooldownEndsAt, long nextMobSpawnsAt,
int totalMobsSpawned, Optional<UndertrialSpawnData> nextSpawnData,
Optional<ResourceLocation> ejectingLootTable) {
this.detectedPlayers.addAll(detectedPlayers);
this.currentMobs.addAll(currentMobs);
this.cooldownEndsAt = cooldownEndsAt;
this.nextMobSpawnsAt = nextMobSpawnsAt;
this.totalMobsSpawned = totalMobsSpawned;
this.nextSpawnData = nextSpawnData;
this.ejectingLootTable = ejectingLootTable;
}
public static UndertrialSpawnerData load(CompoundTag tag) {
Set<UUID> detected = loadUuidSet(tag.get("registered_players"));
Set<UUID> mobs = loadUuidSet(tag.get("current_mobs"));
long cooldownEndsAt = tag.contains("cooldown_ends_at", Tag.TAG_ANY_NUMERIC) ? tag.getLong("cooldown_ends_at") : 0L;
long nextMobSpawnsAt = tag.contains(TAG_NEXT_MOB_SPAWNS_AT, Tag.TAG_ANY_NUMERIC) ? tag.getLong(TAG_NEXT_MOB_SPAWNS_AT) : 0L;
int totalMobsSpawned = tag.contains("total_mobs_spawned", Tag.TAG_ANY_NUMERIC) && tag.getInt("total_mobs_spawned") >= 0
? tag.getInt("total_mobs_spawned") : 0;
Optional<UndertrialSpawnData> spawnData = tag.contains(TAG_SPAWN_DATA)
? UndertrialSpawnData.load(tag.get(TAG_SPAWN_DATA)) : Optional.empty();
Optional<ResourceLocation> ejecting = tag.contains("ejecting_loot_table", Tag.TAG_STRING)
? Optional.ofNullable(ResourceLocation.tryParse(tag.getString("ejecting_loot_table"))) : Optional.empty();
return new UndertrialSpawnerData(detected, mobs, cooldownEndsAt, nextMobSpawnsAt, totalMobsSpawned, spawnData, ejecting);
}
public void save(CompoundTag tag) {
if (!this.detectedPlayers.isEmpty()) {
tag.put("registered_players", saveUuidSet(this.detectedPlayers));
}
if (!this.currentMobs.isEmpty()) {
tag.put("current_mobs", saveUuidSet(this.currentMobs));
}
if (this.cooldownEndsAt != 0L) {
tag.putLong("cooldown_ends_at", this.cooldownEndsAt);
}
if (this.nextMobSpawnsAt != 0L) {
tag.putLong(TAG_NEXT_MOB_SPAWNS_AT, this.nextMobSpawnsAt);
}
if (this.totalMobsSpawned != 0) {
tag.putInt("total_mobs_spawned", this.totalMobsSpawned);
}
this.nextSpawnData.ifPresent(data -> tag.put(TAG_SPAWN_DATA, data.save()));
this.ejectingLootTable.ifPresent(id -> tag.putString("ejecting_loot_table", id.toString()));
}
private static Set<UUID> loadUuidSet(Tag tag) {
Set<UUID> result = new HashSet<>();
if (!(tag instanceof ListTag list)) {
return result;
}
for (Tag element : list) {
if (!(element instanceof IntArrayTag array) || array.size() != 4) {
return new HashSet<>();
}
result.add(NbtUtils.loadUUID(array));
}
return result;
}
private static ListTag saveUuidSet(Set<UUID> uuids) {
ListTag list = new ListTag();
for (UUID uuid : uuids) {
list.add(NbtUtils.createUUID(uuid));
}
return list;
}
public void reset() {
this.detectedPlayers.clear();
this.totalMobsSpawned = 0;
this.nextMobSpawnsAt = 0L;
this.cooldownEndsAt = 0L;
this.currentMobs.clear();
this.nextSpawnData = Optional.empty();
}
public boolean hasMobToSpawn(UndertrialSpawner spawner, RandomSource random) {
boolean hasId = this.getOrCreateNextSpawnData(spawner, random).getEntityToSpawn().contains("id", Tag.TAG_STRING);
return hasId || !spawner.getConfig().spawnPotentialsDefinition().isEmpty();
}
public boolean hasFinishedSpawningAllMobs(UndertrialSpawnerConfig config, int additionalPlayers) {
return this.totalMobsSpawned >= config.calculateTargetTotalMobs(additionalPlayers);
}
public boolean haveAllCurrentMobsDied() {
return this.currentMobs.isEmpty();
}
public boolean isReadyToSpawnNextMob(ServerLevel level, UndertrialSpawnerConfig config, int additionalPlayers) {
return level.getGameTime() >= this.nextMobSpawnsAt
&& this.currentMobs.size() < config.calculateTargetSimultaneousMobs(additionalPlayers);
}
public int countAdditionalPlayers(BlockPos pos) {
return Math.max(0, this.detectedPlayers.size() - 1);
}
public void tryDetectPlayers(ServerLevel level, BlockPos pos, UndertrialSpawner spawner) {
if ((pos.asLong() + level.getGameTime()) % DELAY_BETWEEN_PLAYER_SCANS != 0L) {
return;
}
if (spawner.getState().equals(UndertrialSpawnerState.COOLDOWN) && spawner.isOminous()) {
return;
}
List<UUID> players = spawner.getPlayerDetector()
.detect(level, spawner.getEntitySelector(), pos, spawner.getRequiredPlayerRange(), true);
boolean becameOminous;
if (!spawner.isOminous() && !players.isEmpty()) {
Optional<Pair<Player, Boolean>> omenPlayer = findPlayerWithOminousEffect(level, players);
omenPlayer.ifPresent(pair -> {
Player player = pair.getFirst();
if (pair.getSecond()) {
UndertrialOmen.transformBadOmenIntoTrialOmen(player);
}
UndertrialSpawnerEffects.becomeOminous(level, pos, BlockPos.containing(player.getEyePosition()), 0);
spawner.applyOminous(level, pos);
});
becameOminous = omenPlayer.isPresent();
} else {
becameOminous = false;
}
if (!spawner.getState().equals(UndertrialSpawnerState.COOLDOWN) || becameOminous) {
boolean firstDetection = spawner.getData().detectedPlayers.isEmpty();
List<UUID> newPlayers = firstDetection ? players
: spawner.getPlayerDetector().detect(level, spawner.getEntitySelector(), pos, spawner.getRequiredPlayerRange(), false);
if (this.detectedPlayers.addAll(newPlayers)) {
this.nextMobSpawnsAt = Math.max(level.getGameTime() + UndertrialSpawner.DETECT_PLAYER_SPAWN_BUFFER, this.nextMobSpawnsAt);
if (!becameOminous) {
UndertrialSpawnerEffects.detectPlayer(level, pos, spawner.isOminous(), this.detectedPlayers.size());
}
}
}
}
private static Optional<Pair<Player, Boolean>> findPlayerWithOminousEffect(ServerLevel level, List<UUID> players) {
Player badOmenPlayer = null;
for (UUID uuid : players) {
Player player = level.getPlayerByUUID(uuid);
if (player != null) {
if (UndertrialOmen.hasTrialOmen(player)) {
return Optional.of(Pair.of(player, false));
}
if (UndertrialOmen.hasBadOmen(player)) {
badOmenPlayer = player;
}
}
}
return Optional.ofNullable(badOmenPlayer).map(player -> Pair.of(player, true));
}
public void resetAfterBecomingOminous(UndertrialSpawner spawner, ServerLevel level, BlockPos spawnerPos) {
for (UUID uuid : this.currentMobs) {
Entity entity = level.getEntity(uuid);
if (entity != null) {
UndertrialSpawnerEffects.mobSpawn(level, spawnerPos, entity.blockPosition(), UndertrialSpawnerEffects.FLAME_NORMAL);
if (entity instanceof Mob mob) {
dropPreservedEquipment(mob);
}
entity.remove(Entity.RemovalReason.DISCARDED);
}
}
if (!spawner.getOminousConfig().spawnPotentialsDefinition().isEmpty()) {
this.nextSpawnData = Optional.empty();
}
this.totalMobsSpawned = 0;
this.currentMobs.clear();
this.nextMobSpawnsAt = level.getGameTime() + spawner.getOminousConfig().ticksBetweenSpawn();
spawner.markUpdated();
this.cooldownEndsAt = level.getGameTime() + spawner.getOminousConfig().ticksBetweenItemSpawners();
}
private static void dropPreservedEquipment(Mob mob) {
CompoundTag saved = mob.saveWithoutId(new CompoundTag());
ListTag hand = saved.getList("HandDropChances", Tag.TAG_FLOAT);
ListTag armor = saved.getList("ArmorDropChances", Tag.TAG_FLOAT);
for (EquipmentSlot slot : EquipmentSlot.values()) {
ItemStack stack = mob.getItemBySlot(slot);
if (stack.isEmpty()) {
continue;
}
ListTag chances = slot.getType() == EquipmentSlot.Type.HAND ? hand : armor;
double chance = slot.getIndex() < chances.size() ? chances.getFloat(slot.getIndex()) : 0.0D;
if (chance > 1.0D) {
mob.setItemSlot(slot, ItemStack.EMPTY);
mob.spawnAtLocation(stack);
}
}
}
public boolean isReadyToOpenShutter(ServerLevel level, float delay, int targetCooldownLength) {
long cooldownStartedAt = this.cooldownEndsAt - targetCooldownLength;
return (float) level.getGameTime() >= (float) cooldownStartedAt + delay;
}
public boolean isReadyToEjectItems(ServerLevel level, float interval, int targetCooldownLength) {
long cooldownStartedAt = this.cooldownEndsAt - targetCooldownLength;
return (float) (level.getGameTime() - cooldownStartedAt) % interval == 0.0F;
}
public boolean isCooldownFinished(ServerLevel level) {
return level.getGameTime() >= this.cooldownEndsAt;
}
public void setEntityId(UndertrialSpawner spawner, RandomSource random, EntityType<?> entityType) {
this.getOrCreateNextSpawnData(spawner, random).getEntityToSpawn()
.putString("id", BuiltInRegistries.ENTITY_TYPE.getKey(entityType).toString());
}
protected UndertrialSpawnData getOrCreateNextSpawnData(UndertrialSpawner spawner, RandomSource random) {
if (this.nextSpawnData.isPresent()) {
return this.nextSpawnData.get();
}
SimpleWeightedRandomList<UndertrialSpawnData> potentials = spawner.getConfig().spawnPotentialsDefinition();
Optional<UndertrialSpawnData> picked = potentials.isEmpty()
? this.nextSpawnData
: potentials.getRandom(random).map(wrapper -> wrapper.getData());
this.nextSpawnData = Optional.of(picked.orElseGet(UndertrialSpawnData::new));
spawner.markUpdated();
return this.nextSpawnData.get();
}
public Entity getOrCreateDisplayEntity(UndertrialSpawner spawner, Level level, UndertrialSpawnerState state) {
if (!state.hasSpinningMob()) {
return null;
}
if (this.displayEntity == null) {
CompoundTag entityTag = this.getOrCreateNextSpawnData(spawner, level.getRandom()).getEntityToSpawn();
if (entityTag.contains("id", Tag.TAG_STRING)) {
this.displayEntity = EntityType.loadEntityRecursive(entityTag, level, Function.identity());
}
}
return this.displayEntity;
}
public CompoundTag getUpdateTag(UndertrialSpawnerState state) {
CompoundTag tag = new CompoundTag();
if (state == UndertrialSpawnerState.ACTIVE) {
tag.putLong(TAG_NEXT_MOB_SPAWNS_AT, this.nextMobSpawnsAt);
}
this.nextSpawnData.ifPresent(data -> tag.put(TAG_SPAWN_DATA, data.save()));
return tag;
}
SimpleWeightedRandomList<ItemStack> getDispensingItems(ServerLevel level, UndertrialSpawnerConfig config, BlockPos pos) {
if (this.dispensing != null) {
return this.dispensing;
}
LootTable lootTable = level.getServer().getLootData().getLootTable(config.itemsToDropWhenOminous());
LootParams params = new LootParams.Builder(level).create(LootContextParamSets.EMPTY);
ObjectArrayList<ItemStack> items = lootTable.getRandomItems(params, lowResolutionPosition(level, pos));
if (items.isEmpty()) {
return SimpleWeightedRandomList.empty();
}
SimpleWeightedRandomList.Builder<ItemStack> builder = SimpleWeightedRandomList.builder();
for (ItemStack stack : items) {
builder.add(stack.copyWithCount(1), stack.getCount());
}
this.dispensing = builder.build();
return this.dispensing;
}
private static long lowResolutionPosition(ServerLevel level, BlockPos pos) {
BlockPos lowRes = new BlockPos(Mth.floor(pos.getX() / 30.0F), Mth.floor(pos.getY() / 20.0F), Mth.floor(pos.getZ() / 30.0F));
return level.getSeed() + lowRes.asLong();
}
}
@@ -0,0 +1,186 @@
package com.craisinlord.antarchy.content.block.entity.undertrial;
import com.craisinlord.antarchy.Antarchy;
import net.minecraft.core.BlockPos;
import net.minecraft.core.particles.ParticleOptions;
import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.core.particles.SimpleParticleType;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.sounds.SoundSource;
import net.minecraft.util.RandomSource;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.state.BlockState;
public final class UndertrialSpawnerEffects {
public static final int FLAME_NORMAL = 0;
public static final int FLAME_OMINOUS = 1;
private static final int SPAWNER_SPAWN = 1;
private static final int MOB_SPAWN = 2;
private static final int DETECT_PLAYER = 3;
private static final int DETECT_PLAYER_OMINOUS = 4;
private static final int EJECT_ITEM = 5;
private static final int BECOME_OMINOUS = 6;
private static final int OFFSET_BITS = 9;
private static final int OFFSET_MASK = (1 << OFFSET_BITS) - 1;
private static final int OFFSET_BIAS = 1 << (OFFSET_BITS - 1);
private static final int FLAG_SHIFT = OFFSET_BITS * 3;
private UndertrialSpawnerEffects() {
}
public static void spawnerSpawn(ServerLevel level, BlockPos spawnerPos, int flame) {
send(level, spawnerPos, SPAWNER_SPAWN, flame);
}
public static void mobSpawn(ServerLevel level, BlockPos spawnerPos, BlockPos mobPos, int flame) {
send(level, spawnerPos, MOB_SPAWN, packOffset(spawnerPos, mobPos) | (flame << FLAG_SHIFT));
}
public static void detectPlayer(ServerLevel level, BlockPos spawnerPos, boolean ominous, int playerCount) {
send(level, spawnerPos, ominous ? DETECT_PLAYER_OMINOUS : DETECT_PLAYER, playerCount);
}
public static void ejectItem(ServerLevel level, BlockPos spawnerPos) {
send(level, spawnerPos, EJECT_ITEM, 0);
}
public static void becomeOminous(ServerLevel level, BlockPos spawnerPos, BlockPos effectPos, int data) {
send(level, spawnerPos, BECOME_OMINOUS, packOffset(spawnerPos, effectPos) | ((data == 0 ? 0 : 1) << FLAG_SHIFT));
}
private static void send(ServerLevel level, BlockPos spawnerPos, int id, int param) {
BlockState state = level.getBlockState(spawnerPos);
level.blockEvent(spawnerPos, state.getBlock(), id, param);
}
private static int packOffset(BlockPos origin, BlockPos target) {
int dx = clampOffset(target.getX() - origin.getX());
int dy = clampOffset(target.getY() - origin.getY());
int dz = clampOffset(target.getZ() - origin.getZ());
return (dx + OFFSET_BIAS) | ((dy + OFFSET_BIAS) << OFFSET_BITS) | ((dz + OFFSET_BIAS) << (OFFSET_BITS * 2));
}
private static int clampOffset(int value) {
return Math.max(-OFFSET_BIAS, Math.min(OFFSET_BIAS - 1, value));
}
private static BlockPos unpackOffset(BlockPos origin, int param) {
int dx = (param & OFFSET_MASK) - OFFSET_BIAS;
int dy = ((param >> OFFSET_BITS) & OFFSET_MASK) - OFFSET_BIAS;
int dz = ((param >> (OFFSET_BITS * 2)) & OFFSET_MASK) - OFFSET_BIAS;
return origin.offset(dx, dy, dz);
}
public static boolean handle(Level level, BlockPos spawnerPos, int id, int param) {
if (!level.isClientSide) {
return id >= SPAWNER_SPAWN && id <= BECOME_OMINOUS;
}
RandomSource random = level.random;
switch (id) {
case SPAWNER_SPAWN:
addSpawnParticles(level, spawnerPos, random, flameParticle(param));
return true;
case MOB_SPAWN: {
BlockPos mobPos = unpackOffset(spawnerPos, param);
UndertrialSounds.playLocal(level, mobPos, UndertrialSounds.TRIAL_SPAWNER_SPAWN_MOB, SoundSource.BLOCKS, 1.0F,
(random.nextFloat() - random.nextFloat()) * 0.2F + 1.0F, true);
addSpawnParticles(level, mobPos, random, flameParticle((param >> FLAG_SHIFT) & 1));
return true;
}
case DETECT_PLAYER:
case DETECT_PLAYER_OMINOUS:
UndertrialSounds.playLocal(level, spawnerPos, UndertrialSounds.TRIAL_SPAWNER_DETECT_PLAYER, SoundSource.BLOCKS, 1.0F,
(random.nextFloat() - random.nextFloat()) * 0.2F + 1.0F, true);
addDetectPlayerParticles(level, spawnerPos, random, param, detectedPlayerParticle(id == DETECT_PLAYER_OMINOUS));
return true;
case EJECT_ITEM:
UndertrialSounds.playLocal(level, spawnerPos, UndertrialSounds.TRIAL_SPAWNER_EJECT_ITEM, SoundSource.BLOCKS, 1.0F,
(random.nextFloat() - random.nextFloat()) * 0.2F + 1.0F, true);
addEjectItemParticles(level, spawnerPos, random);
return true;
case BECOME_OMINOUS: {
BlockPos effectPos = unpackOffset(spawnerPos, param);
boolean loud = ((param >> FLAG_SHIFT) & 1) != 0;
UndertrialSounds.playLocal(level, effectPos, UndertrialSounds.TRIAL_SPAWNER_OMINOUS_ACTIVATE, SoundSource.BLOCKS,
loud ? 1.0F : 0.3F, (random.nextFloat() - random.nextFloat()) * 0.2F + 1.0F, true);
addDetectPlayerParticles(level, effectPos, random, 0, detectedPlayerParticle(true));
addBecomeOminousParticles(level, effectPos, random);
return true;
}
default:
return false;
}
}
private static SimpleParticleType flameParticle(int flame) {
return flame == FLAME_OMINOUS ? ParticleTypes.SOUL_FIRE_FLAME : ParticleTypes.FLAME;
}
private static ParticleOptions detectedPlayerParticle(boolean ominous) {
SimpleParticleType detection = antarchyParticle("undertrial_spawner_detection");
if (detection != null) {
return detection;
}
return ominous ? ParticleTypes.SOUL_FIRE_FLAME : ParticleTypes.FLAME;
}
private static ParticleOptions omenParticle() {
SimpleParticleType omen = antarchyParticle("undertrial_omen");
return omen != null ? omen : ParticleTypes.SOUL;
}
private static SimpleParticleType antarchyParticle(String path) {
return BuiltInRegistries.PARTICLE_TYPE.get(new ResourceLocation(Antarchy.MODID, path)) instanceof SimpleParticleType simple
? simple : null;
}
public static void addSpawnParticles(Level level, BlockPos pos, RandomSource random, SimpleParticleType flame) {
for (int i = 0; i < 20; i++) {
double x = pos.getX() + 0.5D + (random.nextDouble() - 0.5D) * 2.0D;
double y = pos.getY() + 0.5D + (random.nextDouble() - 0.5D) * 2.0D;
double z = pos.getZ() + 0.5D + (random.nextDouble() - 0.5D) * 2.0D;
level.addParticle(ParticleTypes.SMOKE, x, y, z, 0.0D, 0.0D, 0.0D);
level.addParticle(flame, x, y, z, 0.0D, 0.0D, 0.0D);
}
}
public static void addBecomeOminousParticles(Level level, BlockPos pos, RandomSource random) {
ParticleOptions omen = omenParticle();
for (int i = 0; i < 20; i++) {
double x = pos.getX() + 0.5D + (random.nextDouble() - 0.5D) * 2.0D;
double y = pos.getY() + 0.5D + (random.nextDouble() - 0.5D) * 2.0D;
double z = pos.getZ() + 0.5D + (random.nextDouble() - 0.5D) * 2.0D;
double xd = random.nextGaussian() * 0.02D;
double yd = random.nextGaussian() * 0.02D;
double zd = random.nextGaussian() * 0.02D;
level.addParticle(omen, x, y, z, xd, yd, zd);
level.addParticle(ParticleTypes.SOUL_FIRE_FLAME, x, y, z, xd, yd, zd);
}
}
public static void addDetectPlayerParticles(Level level, BlockPos pos, RandomSource random, int playerCount, ParticleOptions particle) {
for (int i = 0; i < 30 + Math.min(playerCount, 10) * 5; i++) {
double dx = (2.0F * random.nextFloat() - 1.0F) * 0.65D;
double dz = (2.0F * random.nextFloat() - 1.0F) * 0.65D;
double x = pos.getX() + 0.5D + dx;
double y = pos.getY() + 0.1D + random.nextFloat() * 0.8D;
double z = pos.getZ() + 0.5D + dz;
level.addParticle(particle, x, y, z, 0.0D, 0.0D, 0.0D);
}
}
public static void addEjectItemParticles(Level level, BlockPos pos, RandomSource random) {
for (int i = 0; i < 20; i++) {
double x = pos.getX() + 0.4D + random.nextDouble() * 0.2D;
double y = pos.getY() + 0.4D + random.nextDouble() * 0.2D;
double z = pos.getZ() + 0.4D + random.nextDouble() * 0.2D;
double xd = random.nextGaussian() * 0.02D;
double yd = random.nextGaussian() * 0.02D;
double zd = random.nextGaussian() * 0.02D;
level.addParticle(ParticleTypes.SMALL_FLAME, x, y, z, xd, yd, zd * 0.25D);
level.addParticle(ParticleTypes.SMOKE, x, y, z, xd, yd, zd);
}
}
}
@@ -0,0 +1,211 @@
package com.craisinlord.antarchy.content.block.entity.undertrial;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.sounds.SoundSource;
import net.minecraft.util.Mth;
import net.minecraft.util.StringRepresentable;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.item.ItemEntity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.ClipContext;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.Vec3;
import net.minecraft.Util;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
public enum UndertrialSpawnerState implements StringRepresentable {
INACTIVE("inactive", 0, -1.0D, false),
WAITING_FOR_PLAYERS("waiting_for_players", 4, 200.0D, true),
ACTIVE("active", 8, 1000.0D, true),
WAITING_FOR_REWARD_EJECTION("waiting_for_reward_ejection", 8, -1.0D, false),
EJECTING_REWARD("ejecting_reward", 8, -1.0D, false),
COOLDOWN("cooldown", 0, -1.0D, false);
private static final float DELAY_BEFORE_EJECT_AFTER_KILLING_LAST_MOB = 40.0F;
private static final int TIME_BETWEEN_EACH_EJECTION = Mth.floor(30.0F);
private final String name;
private final int lightLevel;
private final double spinningMobSpeed;
private final boolean isCapableOfSpawning;
UndertrialSpawnerState(String name, int lightLevel, double spinningMobSpeed, boolean isCapableOfSpawning) {
this.name = name;
this.lightLevel = lightLevel;
this.spinningMobSpeed = spinningMobSpeed;
this.isCapableOfSpawning = isCapableOfSpawning;
}
UndertrialSpawnerState tickAndGetNext(BlockPos pos, UndertrialSpawner spawner, ServerLevel level) {
UndertrialSpawnerData data = spawner.getData();
UndertrialSpawnerConfig config = spawner.getConfig();
switch (this) {
case INACTIVE:
return data.getOrCreateDisplayEntity(spawner, level, WAITING_FOR_PLAYERS) == null ? this : WAITING_FOR_PLAYERS;
case WAITING_FOR_PLAYERS:
if (!spawner.canSpawnInLevel(level)) {
data.reset();
return this;
}
if (!data.hasMobToSpawn(spawner, level.random)) {
return INACTIVE;
}
data.tryDetectPlayers(level, pos, spawner);
return data.detectedPlayers.isEmpty() ? this : ACTIVE;
case ACTIVE: {
if (!spawner.canSpawnInLevel(level)) {
data.reset();
return WAITING_FOR_PLAYERS;
}
if (!data.hasMobToSpawn(spawner, level.random)) {
return INACTIVE;
}
int additionalPlayers = data.countAdditionalPlayers(pos);
data.tryDetectPlayers(level, pos, spawner);
if (spawner.isOminous()) {
spawnOminousItem(level, pos, spawner);
}
if (data.hasFinishedSpawningAllMobs(config, additionalPlayers)) {
if (data.haveAllCurrentMobsDied()) {
data.cooldownEndsAt = level.getGameTime() + spawner.getTargetCooldownLength();
data.totalMobsSpawned = 0;
data.nextMobSpawnsAt = 0L;
return WAITING_FOR_REWARD_EJECTION;
}
} else if (data.isReadyToSpawnNextMob(level, config, additionalPlayers)) {
spawner.spawnMob(level, pos).ifPresent(uuid -> {
data.currentMobs.add(uuid);
data.totalMobsSpawned++;
data.nextMobSpawnsAt = level.getGameTime() + config.ticksBetweenSpawn();
config.spawnPotentialsDefinition().getRandom(level.getRandom()).ifPresent(wrapper -> {
data.nextSpawnData = Optional.of(wrapper.getData());
spawner.markUpdated();
});
});
}
return this;
}
case WAITING_FOR_REWARD_EJECTION:
if (data.isReadyToOpenShutter(level, DELAY_BEFORE_EJECT_AFTER_KILLING_LAST_MOB, spawner.getTargetCooldownLength())) {
UndertrialSounds.playServer(level, pos, UndertrialSounds.TRIAL_SPAWNER_OPEN_SHUTTER, SoundSource.BLOCKS, 1.0F, 1.0F);
return EJECTING_REWARD;
}
return this;
case EJECTING_REWARD:
if (!data.isReadyToEjectItems(level, TIME_BETWEEN_EACH_EJECTION, spawner.getTargetCooldownLength())) {
return this;
}
if (data.detectedPlayers.isEmpty()) {
UndertrialSounds.playServer(level, pos, UndertrialSounds.TRIAL_SPAWNER_CLOSE_SHUTTER, SoundSource.BLOCKS, 1.0F, 1.0F);
data.ejectingLootTable = Optional.empty();
return COOLDOWN;
}
if (data.ejectingLootTable.isEmpty()) {
data.ejectingLootTable = config.lootTablesToEject().getRandomValue(level.getRandom());
}
data.ejectingLootTable.ifPresent(lootTable -> spawner.ejectReward(level, pos, lootTable));
data.detectedPlayers.remove(data.detectedPlayers.iterator().next());
return this;
case COOLDOWN:
default:
data.tryDetectPlayers(level, pos, spawner);
if (!data.detectedPlayers.isEmpty()) {
data.totalMobsSpawned = 0;
data.nextMobSpawnsAt = 0L;
return ACTIVE;
}
if (data.isCooldownFinished(level)) {
spawner.removeOminous(level, pos);
data.reset();
return WAITING_FOR_PLAYERS;
}
return this;
}
}
private void spawnOminousItem(ServerLevel level, BlockPos pos, UndertrialSpawner spawner) {
UndertrialSpawnerData data = spawner.getData();
UndertrialSpawnerConfig config = spawner.getConfig();
ItemStack stack = data.getDispensingItems(level, config, pos).getRandomValue(level.random).orElse(ItemStack.EMPTY);
if (stack.isEmpty() || level.getGameTime() < data.cooldownEndsAt) {
return;
}
calculatePositionToSpawnItem(level, pos, spawner, data).ifPresent(position -> {
ItemEntity item = new ItemEntity(level, position.x, position.y, position.z, stack.copy());
item.setDeltaMovement(Vec3.ZERO);
level.addFreshEntity(item);
float pitch = (level.getRandom().nextFloat() - level.getRandom().nextFloat()) * 0.2F + 1.0F;
UndertrialSounds.playServer(level, BlockPos.containing(position), UndertrialSounds.TRIAL_SPAWNER_SPAWN_ITEM_BEGIN, SoundSource.BLOCKS, 1.0F, pitch);
data.cooldownEndsAt = level.getGameTime() + spawner.getOminousConfig().ticksBetweenItemSpawners();
});
}
private static Optional<Vec3> calculatePositionToSpawnItem(ServerLevel level, BlockPos pos, UndertrialSpawner spawner, UndertrialSpawnerData data) {
List<Player> players = new ArrayList<>();
for (UUID uuid : data.detectedPlayers) {
Player player = level.getPlayerByUUID(uuid);
if (player != null && !player.isCreative() && !player.isSpectator() && player.isAlive()
&& player.distanceToSqr(pos.getCenter()) <= Mth.square(spawner.getRequiredPlayerRange())) {
players.add(player);
}
}
if (players.isEmpty()) {
return Optional.empty();
}
Entity entity = selectEntityToSpawnItemAbove(players, data.currentMobs, spawner, pos, level);
return entity == null ? Optional.empty() : calculatePositionAbove(entity, level);
}
private static Optional<Vec3> calculatePositionAbove(Entity entity, ServerLevel level) {
Vec3 start = entity.position();
Vec3 end = start.relative(Direction.UP, entity.getBbHeight() + 2.0F + level.random.nextInt(4));
BlockHitResult hit = level.clip(new ClipContext(start, end, ClipContext.Block.VISUAL, ClipContext.Fluid.NONE, null));
Vec3 position = hit.getBlockPos().getCenter().relative(Direction.DOWN, 1.0D);
BlockPos blockPos = BlockPos.containing(position);
return !level.getBlockState(blockPos).getCollisionShape(level, blockPos).isEmpty() ? Optional.empty() : Optional.of(position);
}
private static Entity selectEntityToSpawnItemAbove(List<Player> players, Set<UUID> mobs, UndertrialSpawner spawner, BlockPos pos, ServerLevel level) {
List<Entity> livingMobs = new ArrayList<>();
for (UUID uuid : mobs) {
Entity mob = level.getEntity(uuid);
if (mob != null && mob.isAlive() && mob.distanceToSqr(pos.getCenter()) <= Mth.square(spawner.getRequiredPlayerRange())) {
livingMobs.add(mob);
}
}
List<? extends Entity> candidates = level.random.nextBoolean() ? livingMobs : players;
if (candidates.isEmpty()) {
return null;
}
return candidates.size() == 1 ? candidates.get(0) : Objects.requireNonNull(Util.getRandom(candidates, level.random));
}
public int lightLevel() {
return this.lightLevel;
}
public double spinningMobSpeed() {
return this.spinningMobSpeed;
}
public boolean hasSpinningMob() {
return this.spinningMobSpeed >= 0.0D;
}
public boolean isCapableOfSpawning() {
return this.isCapableOfSpawning;
}
@Override
public String getSerializedName() {
return this.name;
}
}
@@ -0,0 +1,22 @@
package com.craisinlord.antarchy.content.block.entity.undervault;
import net.minecraft.util.Mth;
public class UnderVaultClientData {
public static final float ROTATION_SPEED = 10.0F;
private float currentSpin;
private float previousSpin;
public float currentSpin() {
return this.currentSpin;
}
public float previousSpin() {
return this.previousSpin;
}
void updateDisplayItemSpin() {
this.previousSpin = this.currentSpin;
this.currentSpin = Mth.wrapDegrees(this.currentSpin + ROTATION_SPEED);
}
}
@@ -0,0 +1,67 @@
package com.craisinlord.antarchy.content.block.entity.undervault;
import com.craisinlord.antarchy.content.block.entity.undertrial.UndertrialPlayerDetector;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.Tag;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.ItemStack;
import java.util.Optional;
public record UnderVaultConfig(
ResourceLocation lootTable,
double activationRange,
double deactivationRange,
ItemStack keyItem,
Optional<ResourceLocation> overrideLootTableToDisplay,
UndertrialPlayerDetector playerDetector,
UndertrialPlayerDetector.EntitySelector entitySelector
) {
public static final String TAG_NAME = "config";
public static final ResourceLocation TRIAL_CHAMBERS_REWARD = new ResourceLocation("minecraft", "chests/trial_chambers/reward");
public static final UnderVaultConfig DEFAULT = new UnderVaultConfig(TRIAL_CHAMBERS_REWARD, 4.0D, 4.5D, ItemStack.EMPTY,
Optional.empty(), UndertrialPlayerDetector.INCLUDING_CREATIVE_PLAYERS, UndertrialPlayerDetector.EntitySelector.SELECT_FROM_LEVEL);
public UnderVaultConfig(ResourceLocation lootTable, double activationRange, double deactivationRange, ItemStack keyItem,
Optional<ResourceLocation> overrideLootTableToDisplay) {
this(lootTable, activationRange, deactivationRange, keyItem, overrideLootTableToDisplay,
DEFAULT.playerDetector(), DEFAULT.entitySelector());
}
public static Optional<UnderVaultConfig> load(CompoundTag tag) {
ResourceLocation lootTable = DEFAULT.lootTable;
if (tag.contains("loot_table", Tag.TAG_STRING)) {
ResourceLocation parsed = ResourceLocation.tryParse(tag.getString("loot_table"));
if (parsed != null) {
lootTable = parsed;
}
}
double activation = tag.contains("activation_range", Tag.TAG_ANY_NUMERIC) ? tag.getDouble("activation_range") : DEFAULT.activationRange;
double deactivation = tag.contains("deactivation_range", Tag.TAG_ANY_NUMERIC) ? tag.getDouble("deactivation_range") : DEFAULT.deactivationRange;
ItemStack key = tag.contains("key_item", Tag.TAG_COMPOUND) ? ItemStack.of(tag.getCompound("key_item")) : ItemStack.EMPTY;
Optional<ResourceLocation> override = tag.contains("override_loot_table_to_display", Tag.TAG_STRING)
? Optional.ofNullable(ResourceLocation.tryParse(tag.getString("override_loot_table_to_display"))) : Optional.empty();
if (activation > deactivation) {
return Optional.empty();
}
return Optional.of(new UnderVaultConfig(lootTable, activation, deactivation, key, override));
}
public CompoundTag save() {
CompoundTag tag = new CompoundTag();
if (!this.lootTable.equals(DEFAULT.lootTable)) {
tag.putString("loot_table", this.lootTable.toString());
}
if (this.activationRange != DEFAULT.activationRange) {
tag.putDouble("activation_range", this.activationRange);
}
if (this.deactivationRange != DEFAULT.deactivationRange) {
tag.putDouble("deactivation_range", this.deactivationRange);
}
if (!this.keyItem.isEmpty()) {
tag.put("key_item", this.keyItem.save(new CompoundTag()));
}
this.overrideLootTableToDisplay.ifPresent(id -> tag.putString("override_loot_table_to_display", id.toString()));
return tag;
}
}
@@ -0,0 +1,61 @@
package com.craisinlord.antarchy.content.block.entity.undervault;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.Tag;
import net.minecraft.world.level.Level;
public final class UnderVaultData {
private final UnderVaultServerData serverData = new UnderVaultServerData();
private final UnderVaultSharedData sharedData = new UnderVaultSharedData();
private final UnderVaultClientData clientData = new UnderVaultClientData();
private UnderVaultConfig config = UnderVaultConfig.DEFAULT;
private Level level;
public void setLevel(Level level) {
this.level = level;
}
public Level getLevel() {
return this.level;
}
public CompoundTag save() {
CompoundTag tag = new CompoundTag();
tag.put(UnderVaultConfig.TAG_NAME, this.config.save());
tag.put(UnderVaultSharedData.TAG_NAME, this.sharedData.save());
tag.put(UnderVaultServerData.TAG_NAME, this.serverData.save());
return tag;
}
public void load(CompoundTag tag) {
if (tag.contains(UnderVaultServerData.TAG_NAME, Tag.TAG_COMPOUND)) {
this.serverData.set(UnderVaultServerData.load(tag.getCompound(UnderVaultServerData.TAG_NAME)));
}
if (tag.contains(UnderVaultConfig.TAG_NAME, Tag.TAG_COMPOUND)) {
UnderVaultConfig.load(tag.getCompound(UnderVaultConfig.TAG_NAME)).ifPresent(loaded -> this.config = loaded);
}
if (tag.contains(UnderVaultSharedData.TAG_NAME, Tag.TAG_COMPOUND)) {
this.sharedData.set(UnderVaultSharedData.load(tag.getCompound(UnderVaultSharedData.TAG_NAME)));
}
}
public UnderVaultServerData getServerData() {
return this.level != null && !this.level.isClientSide ? this.serverData : null;
}
public UnderVaultSharedData getSharedData() {
return this.sharedData;
}
public UnderVaultClientData getClientData() {
return this.clientData;
}
public UnderVaultConfig getConfig() {
return this.config;
}
public void setConfig(UnderVaultConfig config) {
this.config = config;
}
}
@@ -0,0 +1,44 @@
package com.craisinlord.antarchy.content.block.entity.undervault;
import com.craisinlord.antarchy.content.block.entity.undertrial.UndertrialSounds;
import com.craisinlord.antarchy.content.block.entity.undertrial.UndertrialSpawnerEffects;
import net.minecraft.core.BlockPos;
import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.sounds.SoundSource;
import net.minecraft.util.RandomSource;
import net.minecraft.world.level.Level;
public final class UnderVaultEffects {
private static final int DEACTIVATE = 1;
private static final int EJECT_ITEM = 2;
private UnderVaultEffects() {
}
public static void deactivate(ServerLevel level, BlockPos pos, boolean ominous) {
level.blockEvent(pos, level.getBlockState(pos).getBlock(), DEACTIVATE, ominous ? 1 : 0);
}
public static void ejectItem(ServerLevel level, BlockPos pos) {
level.blockEvent(pos, level.getBlockState(pos).getBlock(), EJECT_ITEM, 0);
}
public static boolean handle(Level level, BlockPos pos, int id, int param) {
if (!level.isClientSide) {
return id == DEACTIVATE || id == EJECT_ITEM;
}
RandomSource random = level.random;
if (id == DEACTIVATE) {
UnderVaultLogic.Client.emitDeactivationParticles(level, pos, param == 0 ? ParticleTypes.SMALL_FLAME : ParticleTypes.SOUL_FIRE_FLAME);
UndertrialSounds.playLocal(level, pos, UndertrialSounds.VAULT_DEACTIVATE, SoundSource.BLOCKS, 1.0F,
(random.nextFloat() - random.nextFloat()) * 0.2F + 1.0F, true);
return true;
}
if (id == EJECT_ITEM) {
UndertrialSpawnerEffects.addEjectItemParticles(level, pos, random);
return true;
}
return false;
}
}
@@ -0,0 +1,261 @@
package com.craisinlord.antarchy.content.block.entity.undervault;
import com.craisinlord.antarchy.content.block.UnderVaultBlock;
import com.craisinlord.antarchy.content.block.entity.undertrial.UndertrialSounds;
import net.minecraft.Util;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.particles.ParticleOptions;
import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.sounds.SoundSource;
import net.minecraft.stats.Stats;
import net.minecraft.util.Mth;
import net.minecraft.util.RandomSource;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.storage.loot.BuiltInLootTables;
import net.minecraft.world.level.storage.loot.LootParams;
import net.minecraft.world.level.storage.loot.LootTable;
import net.minecraft.world.level.storage.loot.parameters.LootContextParamSets;
import net.minecraft.world.level.storage.loot.parameters.LootContextParams;
import net.minecraft.world.phys.Vec3;
import java.util.List;
import java.util.Set;
import java.util.UUID;
public final class UnderVaultLogic {
private UnderVaultLogic() {
}
public static final class Client {
private static final int PARTICLE_TICK_RATE = 20;
private static final float IDLE_PARTICLE_CHANCE = 0.5F;
private static final float AMBIENT_SOUND_CHANCE = 0.02F;
private static final int ACTIVATION_PARTICLE_COUNT = 20;
private static final int DEACTIVATION_PARTICLE_COUNT = 20;
private Client() {
}
public static void tick(Level level, BlockPos pos, BlockState state, UnderVaultClientData clientData, UnderVaultSharedData sharedData) {
clientData.updateDisplayItemSpin();
if (level.getGameTime() % PARTICLE_TICK_RATE == 0L) {
emitConnectionParticlesForNearbyPlayers(level, pos, state, sharedData);
}
emitIdleParticles(level, pos, sharedData, state.getValue(UnderVaultBlock.OMINOUS) ? ParticleTypes.SOUL_FIRE_FLAME : ParticleTypes.SMALL_FLAME);
playIdleSounds(level, pos, sharedData);
}
public static void emitActivationParticles(Level level, BlockPos pos, BlockState state, UnderVaultSharedData sharedData, ParticleOptions particle) {
emitConnectionParticlesForNearbyPlayers(level, pos, state, sharedData);
RandomSource random = level.random;
for (int i = 0; i < ACTIVATION_PARTICLE_COUNT; i++) {
Vec3 position = randomPosInsideCage(pos, random);
level.addParticle(ParticleTypes.SMOKE, position.x(), position.y(), position.z(), 0.0D, 0.0D, 0.0D);
level.addParticle(particle, position.x(), position.y(), position.z(), 0.0D, 0.0D, 0.0D);
}
}
public static void emitDeactivationParticles(Level level, BlockPos pos, ParticleOptions particle) {
RandomSource random = level.random;
for (int i = 0; i < DEACTIVATION_PARTICLE_COUNT; i++) {
Vec3 position = randomPosCenterOfCage(pos, random);
Vec3 velocity = new Vec3(random.nextGaussian() * 0.02D, random.nextGaussian() * 0.02D, random.nextGaussian() * 0.02D);
level.addParticle(particle, position.x(), position.y(), position.z(), velocity.x(), velocity.y(), velocity.z());
}
}
private static void emitIdleParticles(Level level, BlockPos pos, UnderVaultSharedData sharedData, ParticleOptions particle) {
RandomSource random = level.getRandom();
if (random.nextFloat() <= IDLE_PARTICLE_CHANCE) {
Vec3 position = randomPosInsideCage(pos, random);
level.addParticle(ParticleTypes.SMOKE, position.x(), position.y(), position.z(), 0.0D, 0.0D, 0.0D);
if (shouldDisplayActiveEffects(sharedData)) {
level.addParticle(particle, position.x(), position.y(), position.z(), 0.0D, 0.0D, 0.0D);
}
}
}
private static void emitConnectionParticlesForPlayer(Level level, Vec3 keyhole, Player player) {
RandomSource random = level.random;
Vec3 toPlayer = keyhole.vectorTo(player.position().add(0.0D, player.getBbHeight() / 2.0F, 0.0D));
int count = Mth.nextInt(random, 2, 5);
for (int i = 0; i < count; i++) {
Vec3 offset = toPlayer.offsetRandom(random, 1.0F);
level.addParticle(ParticleTypes.ENCHANT, keyhole.x(), keyhole.y(), keyhole.z(), offset.x(), offset.y(), offset.z());
}
}
private static void emitConnectionParticlesForNearbyPlayers(Level level, BlockPos pos, BlockState state, UnderVaultSharedData sharedData) {
Set<UUID> players = sharedData.getConnectedPlayers();
if (players.isEmpty()) {
return;
}
Vec3 keyhole = keyholePos(pos, state.getValue(UnderVaultBlock.FACING));
for (UUID uuid : players) {
Player player = level.getPlayerByUUID(uuid);
if (player != null && isWithinConnectionRange(pos, sharedData, player)) {
emitConnectionParticlesForPlayer(level, keyhole, player);
}
}
}
private static boolean isWithinConnectionRange(BlockPos pos, UnderVaultSharedData sharedData, Player player) {
return player.blockPosition().distSqr(pos) <= Mth.square(sharedData.connectedParticlesRange());
}
private static void playIdleSounds(Level level, BlockPos pos, UnderVaultSharedData sharedData) {
if (shouldDisplayActiveEffects(sharedData)) {
RandomSource random = level.getRandom();
if (random.nextFloat() <= AMBIENT_SOUND_CHANCE) {
UndertrialSounds.playLocal(level, pos, UndertrialSounds.VAULT_AMBIENT, SoundSource.BLOCKS,
random.nextFloat() * 0.25F + 0.75F, random.nextFloat() + 0.5F, false);
}
}
}
public static boolean shouldDisplayActiveEffects(UnderVaultSharedData sharedData) {
return sharedData.hasDisplayItem();
}
private static Vec3 randomPosCenterOfCage(BlockPos pos, RandomSource random) {
return Vec3.atLowerCornerOf(pos).add(Mth.nextDouble(random, 0.4D, 0.6D), Mth.nextDouble(random, 0.4D, 0.6D), Mth.nextDouble(random, 0.4D, 0.6D));
}
private static Vec3 randomPosInsideCage(BlockPos pos, RandomSource random) {
return Vec3.atLowerCornerOf(pos).add(Mth.nextDouble(random, 0.1D, 0.9D), Mth.nextDouble(random, 0.25D, 0.75D), Mth.nextDouble(random, 0.1D, 0.9D));
}
private static Vec3 keyholePos(BlockPos pos, Direction direction) {
return Vec3.atBottomCenterOf(pos).add(direction.getStepX() * 0.5D, 1.75D, direction.getStepZ() * 0.5D);
}
}
public static final class Server {
private static final int UNLOCKING_DELAY_TICKS = 14;
private static final int DISPLAY_CYCLE_TICK_RATE = 20;
private static final int INSERT_FAIL_SOUND_BUFFER_TICKS = 15;
private Server() {
}
public static void tick(ServerLevel level, BlockPos pos, BlockState state, UnderVaultConfig config,
UnderVaultServerData serverData, UnderVaultSharedData sharedData) {
UnderVaultState vaultState = state.getValue(UnderVaultBlock.STATE);
if (shouldCycleDisplayItem(level.getGameTime(), vaultState)) {
cycleDisplayItemFromLootTable(level, vaultState, config, sharedData, pos);
}
BlockState nextState = state;
if (level.getGameTime() >= serverData.stateUpdatingResumesAt()) {
nextState = state.setValue(UnderVaultBlock.STATE, vaultState.tickAndGetNext(level, pos, config, serverData, sharedData));
if (!state.equals(nextState)) {
setVaultState(level, pos, state, nextState, config, sharedData);
}
}
if (serverData.isDirty || sharedData.isDirty) {
level.blockEntityChanged(pos);
if (!state.isAir()) {
level.updateNeighbourForOutputSignal(pos, state.getBlock());
}
if (sharedData.isDirty) {
level.sendBlockUpdated(pos, state, nextState, 2);
}
serverData.isDirty = false;
sharedData.isDirty = false;
}
}
public static void tryInsertKey(ServerLevel level, BlockPos pos, BlockState state, UnderVaultConfig config,
UnderVaultServerData serverData, UnderVaultSharedData sharedData, Player player, ItemStack stack) {
UnderVaultState vaultState = state.getValue(UnderVaultBlock.STATE);
if (!canEjectReward(config, vaultState)) {
return;
}
if (!isValidToInsert(config, stack)) {
playInsertFailSound(level, serverData, pos, UndertrialSounds.VAULT_INSERT_ITEM_FAIL);
} else if (serverData.hasRewardedPlayer(player)) {
playInsertFailSound(level, serverData, pos, UndertrialSounds.VAULT_REJECT_REWARDED_PLAYER);
} else {
List<ItemStack> items = resolveItemsToEject(level, config, pos, player);
if (!items.isEmpty()) {
player.awardStat(Stats.ITEM_USED.get(stack.getItem()));
if (!player.getAbilities().instabuild) {
stack.shrink(config.keyItem().getCount());
}
unlock(level, state, pos, config, serverData, sharedData, items);
serverData.addToRewardedPlayers(player);
sharedData.updateConnectedPlayersWithinRange(level, pos, serverData, config, config.deactivationRange());
}
}
}
static void setVaultState(ServerLevel level, BlockPos pos, BlockState oldState, BlockState newState,
UnderVaultConfig config, UnderVaultSharedData sharedData) {
UnderVaultState from = oldState.getValue(UnderVaultBlock.STATE);
UnderVaultState to = newState.getValue(UnderVaultBlock.STATE);
level.setBlock(pos, newState, 3);
from.onTransition(level, pos, to, config, sharedData, newState.getValue(UnderVaultBlock.OMINOUS));
}
static void cycleDisplayItemFromLootTable(ServerLevel level, UnderVaultState state, UnderVaultConfig config,
UnderVaultSharedData sharedData, BlockPos pos) {
if (!canEjectReward(config, state)) {
sharedData.setDisplayItem(ItemStack.EMPTY);
} else {
ItemStack stack = getRandomDisplayItemFromLootTable(level, pos, config.overrideLootTableToDisplay().orElse(config.lootTable()));
sharedData.setDisplayItem(stack);
}
}
private static ItemStack getRandomDisplayItemFromLootTable(ServerLevel level, BlockPos pos, ResourceLocation lootTableId) {
LootTable lootTable = level.getServer().getLootData().getLootTable(lootTableId);
LootParams params = new LootParams.Builder(level)
.withParameter(LootContextParams.ORIGIN, Vec3.atCenterOf(pos))
.create(LootContextParamSets.CHEST);
List<ItemStack> items = lootTable.getRandomItems(params);
return items.isEmpty() ? ItemStack.EMPTY : Util.getRandom(items, level.getRandom());
}
private static void unlock(ServerLevel level, BlockState state, BlockPos pos, UnderVaultConfig config,
UnderVaultServerData serverData, UnderVaultSharedData sharedData, List<ItemStack> items) {
serverData.setItemsToEject(items);
sharedData.setDisplayItem(serverData.getNextItemToEject());
serverData.pauseStateUpdatingUntil(level.getGameTime() + UNLOCKING_DELAY_TICKS);
setVaultState(level, pos, state, state.setValue(UnderVaultBlock.STATE, UnderVaultState.UNLOCKING), config, sharedData);
}
private static List<ItemStack> resolveItemsToEject(ServerLevel level, UnderVaultConfig config, BlockPos pos, Player player) {
LootTable lootTable = level.getServer().getLootData().getLootTable(config.lootTable());
LootParams params = new LootParams.Builder(level)
.withParameter(LootContextParams.ORIGIN, Vec3.atCenterOf(pos))
.withLuck(player.getLuck())
.withParameter(LootContextParams.THIS_ENTITY, player)
.create(LootContextParamSets.CHEST);
return lootTable.getRandomItems(params);
}
private static boolean canEjectReward(UnderVaultConfig config, UnderVaultState state) {
return !config.lootTable().equals(BuiltInLootTables.EMPTY) && !config.keyItem().isEmpty() && state != UnderVaultState.INACTIVE;
}
private static boolean isValidToInsert(UnderVaultConfig config, ItemStack stack) {
return ItemStack.isSameItemSameTags(stack, config.keyItem()) && stack.getCount() >= config.keyItem().getCount();
}
private static boolean shouldCycleDisplayItem(long gameTime, UnderVaultState state) {
return gameTime % DISPLAY_CYCLE_TICK_RATE == 0L && state == UnderVaultState.ACTIVE;
}
private static void playInsertFailSound(ServerLevel level, UnderVaultServerData serverData, BlockPos pos, UndertrialSounds.Entry sound) {
if (level.getGameTime() >= serverData.getLastInsertFailTimestamp() + INSERT_FAIL_SOUND_BUFFER_TICKS) {
UndertrialSounds.playServer(level, pos, sound, SoundSource.BLOCKS, 1.0F, 1.0F);
serverData.setLastInsertFailTimestamp(level.getGameTime());
}
}
}
}
@@ -0,0 +1,158 @@
package com.craisinlord.antarchy.content.block.entity.undervault;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import it.unimi.dsi.fastutil.objects.ObjectLinkedOpenHashSet;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.Tag;
import net.minecraft.util.Mth;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.UUID;
public class UnderVaultServerData {
public static final String TAG_NAME = "server_data";
private static final int MAX_REWARD_PLAYERS = 128;
private final Set<UUID> rewardedPlayers = new ObjectLinkedOpenHashSet<>();
private long stateUpdatingResumesAt;
private final List<ItemStack> itemsToEject = new ObjectArrayList<>();
private long lastInsertFailTimestamp;
private int totalEjectionsNeeded;
boolean isDirty;
public static UnderVaultServerData load(CompoundTag tag) {
UnderVaultServerData data = new UnderVaultServerData();
data.rewardedPlayers.addAll(UnderVaultSharedData.loadUuidSet(tag.get("rewarded_players")));
data.stateUpdatingResumesAt = tag.contains("state_updating_resumes_at", Tag.TAG_ANY_NUMERIC) ? tag.getLong("state_updating_resumes_at") : 0L;
if (tag.contains("items_to_eject", Tag.TAG_LIST)) {
ListTag list = tag.getList("items_to_eject", Tag.TAG_COMPOUND);
List<ItemStack> items = new ObjectArrayList<>();
boolean valid = true;
for (int i = 0; i < list.size(); i++) {
ItemStack stack = ItemStack.of(list.getCompound(i));
if (stack.isEmpty()) {
valid = false;
break;
}
items.add(stack);
}
if (valid) {
data.itemsToEject.addAll(items);
}
}
data.totalEjectionsNeeded = tag.contains("total_ejections_needed", Tag.TAG_ANY_NUMERIC) ? tag.getInt("total_ejections_needed") : 0;
return data;
}
public CompoundTag save() {
CompoundTag tag = new CompoundTag();
if (!this.rewardedPlayers.isEmpty()) {
tag.put("rewarded_players", UnderVaultSharedData.saveUuidSet(this.rewardedPlayers));
}
if (this.stateUpdatingResumesAt != 0L) {
tag.putLong("state_updating_resumes_at", this.stateUpdatingResumesAt);
}
if (!this.itemsToEject.isEmpty()) {
ListTag list = new ListTag();
for (ItemStack stack : this.itemsToEject) {
list.add(stack.save(new CompoundTag()));
}
tag.put("items_to_eject", list);
}
if (this.totalEjectionsNeeded != 0) {
tag.putInt("total_ejections_needed", this.totalEjectionsNeeded);
}
return tag;
}
void setLastInsertFailTimestamp(long timestamp) {
this.lastInsertFailTimestamp = timestamp;
}
long getLastInsertFailTimestamp() {
return this.lastInsertFailTimestamp;
}
Set<UUID> getRewardedPlayers() {
return this.rewardedPlayers;
}
boolean hasRewardedPlayer(Player player) {
return this.rewardedPlayers.contains(player.getUUID());
}
public void addToRewardedPlayers(Player player) {
this.rewardedPlayers.add(player.getUUID());
if (this.rewardedPlayers.size() > MAX_REWARD_PLAYERS) {
Iterator<UUID> iterator = this.rewardedPlayers.iterator();
if (iterator.hasNext()) {
iterator.next();
iterator.remove();
}
}
this.markChanged();
}
long stateUpdatingResumesAt() {
return this.stateUpdatingResumesAt;
}
void pauseStateUpdatingUntil(long time) {
this.stateUpdatingResumesAt = time;
this.markChanged();
}
List<ItemStack> getItemsToEject() {
return this.itemsToEject;
}
void markEjectionFinished() {
this.totalEjectionsNeeded = 0;
this.markChanged();
}
void setItemsToEject(List<ItemStack> items) {
this.itemsToEject.clear();
this.itemsToEject.addAll(items);
this.totalEjectionsNeeded = this.itemsToEject.size();
this.markChanged();
}
ItemStack getNextItemToEject() {
if (this.itemsToEject.isEmpty()) {
return ItemStack.EMPTY;
}
ItemStack stack = this.itemsToEject.get(this.itemsToEject.size() - 1);
return stack != null ? stack : ItemStack.EMPTY;
}
ItemStack popNextItemToEject() {
if (this.itemsToEject.isEmpty()) {
return ItemStack.EMPTY;
}
this.markChanged();
ItemStack stack = this.itemsToEject.remove(this.itemsToEject.size() - 1);
return stack != null ? stack : ItemStack.EMPTY;
}
void set(UnderVaultServerData other) {
this.stateUpdatingResumesAt = other.stateUpdatingResumesAt();
this.itemsToEject.clear();
this.itemsToEject.addAll(other.itemsToEject);
this.rewardedPlayers.clear();
this.rewardedPlayers.addAll(other.rewardedPlayers);
}
private void markChanged() {
this.isDirty = true;
}
public float ejectionProgress() {
return this.totalEjectionsNeeded == 1 ? 1.0F
: 1.0F - Mth.inverseLerp((float) this.getItemsToEject().size(), 1.0F, (float) this.totalEjectionsNeeded);
}
}
@@ -0,0 +1,118 @@
package com.craisinlord.antarchy.content.block.entity.undervault;
import it.unimi.dsi.fastutil.objects.ObjectLinkedOpenHashSet;
import net.minecraft.core.BlockPos;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.IntArrayTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.NbtUtils;
import net.minecraft.nbt.Tag;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.item.ItemStack;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
public class UnderVaultSharedData {
public static final String TAG_NAME = "shared_data";
private ItemStack displayItem = ItemStack.EMPTY;
private Set<UUID> connectedPlayers = new ObjectLinkedOpenHashSet<>();
private double connectedParticlesRange = UnderVaultConfig.DEFAULT.deactivationRange();
boolean isDirty;
public static UnderVaultSharedData load(CompoundTag tag) {
UnderVaultSharedData data = new UnderVaultSharedData();
data.displayItem = tag.contains("display_item", Tag.TAG_COMPOUND) ? ItemStack.of(tag.getCompound("display_item")) : ItemStack.EMPTY;
data.connectedPlayers.addAll(loadUuidSet(tag.get("connected_players")));
data.connectedParticlesRange = tag.contains("connected_particles_range", Tag.TAG_ANY_NUMERIC)
? tag.getDouble("connected_particles_range") : UnderVaultConfig.DEFAULT.deactivationRange();
return data;
}
public CompoundTag save() {
CompoundTag tag = new CompoundTag();
if (!this.displayItem.isEmpty()) {
tag.put("display_item", this.displayItem.save(new CompoundTag()));
}
if (!this.connectedPlayers.isEmpty()) {
tag.put("connected_players", saveUuidSet(this.connectedPlayers));
}
if (this.connectedParticlesRange != UnderVaultConfig.DEFAULT.deactivationRange()) {
tag.putDouble("connected_particles_range", this.connectedParticlesRange);
}
return tag;
}
static Set<UUID> loadUuidSet(Tag tag) {
Set<UUID> result = new ObjectLinkedOpenHashSet<>();
if (!(tag instanceof ListTag list)) {
return result;
}
for (Tag element : list) {
if (!(element instanceof IntArrayTag array) || array.size() != 4) {
return new ObjectLinkedOpenHashSet<>();
}
result.add(NbtUtils.loadUUID(array));
}
return result;
}
static ListTag saveUuidSet(Set<UUID> uuids) {
ListTag list = new ListTag();
for (UUID uuid : uuids) {
list.add(NbtUtils.createUUID(uuid));
}
return list;
}
public ItemStack getDisplayItem() {
return this.displayItem;
}
public boolean hasDisplayItem() {
return !this.displayItem.isEmpty();
}
public void setDisplayItem(ItemStack stack) {
if (!ItemStack.matches(this.displayItem, stack)) {
this.displayItem = stack.copy();
this.markDirty();
}
}
boolean hasConnectedPlayers() {
return !this.connectedPlayers.isEmpty();
}
Set<UUID> getConnectedPlayers() {
return this.connectedPlayers;
}
double connectedParticlesRange() {
return this.connectedParticlesRange;
}
void updateConnectedPlayersWithinRange(ServerLevel level, BlockPos pos, UnderVaultServerData serverData, UnderVaultConfig config, double range) {
Set<UUID> players = new HashSet<>();
for (UUID uuid : config.playerDetector().detect(level, config.entitySelector(), pos, range, false)) {
if (!serverData.getRewardedPlayers().contains(uuid)) {
players.add(uuid);
}
}
if (!this.connectedPlayers.equals(players)) {
this.connectedPlayers = players;
this.markDirty();
}
}
private void markDirty() {
this.isDirty = true;
}
void set(UnderVaultSharedData other) {
this.displayItem = other.displayItem;
this.connectedPlayers = other.connectedPlayers;
this.connectedParticlesRange = other.connectedParticlesRange;
}
}
@@ -0,0 +1,119 @@
package com.craisinlord.antarchy.content.block.entity.undervault;
import com.craisinlord.antarchy.content.block.entity.undertrial.UndertrialSounds;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.dispenser.DefaultDispenseItemBehavior;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.sounds.SoundSource;
import net.minecraft.util.StringRepresentable;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.phys.Vec3;
public enum UnderVaultState implements StringRepresentable {
INACTIVE("inactive", 6) {
@Override
protected void onEnter(ServerLevel level, BlockPos pos, UnderVaultConfig config, UnderVaultSharedData sharedData, boolean ominous) {
sharedData.setDisplayItem(ItemStack.EMPTY);
UnderVaultEffects.deactivate(level, pos, ominous);
}
},
ACTIVE("active", 12) {
@Override
protected void onEnter(ServerLevel level, BlockPos pos, UnderVaultConfig config, UnderVaultSharedData sharedData, boolean ominous) {
if (!sharedData.hasDisplayItem()) {
UnderVaultLogic.Server.cycleDisplayItemFromLootTable(level, this, config, sharedData, pos);
}
}
},
UNLOCKING("unlocking", 12) {
@Override
protected void onEnter(ServerLevel level, BlockPos pos, UnderVaultConfig config, UnderVaultSharedData sharedData, boolean ominous) {
UndertrialSounds.playServer(level, pos, UndertrialSounds.VAULT_INSERT_ITEM, SoundSource.BLOCKS, 1.0F, 1.0F);
}
},
EJECTING("ejecting", 12) {
@Override
protected void onEnter(ServerLevel level, BlockPos pos, UnderVaultConfig config, UnderVaultSharedData sharedData, boolean ominous) {
UndertrialSounds.playServer(level, pos, UndertrialSounds.VAULT_OPEN_SHUTTER, SoundSource.BLOCKS, 1.0F, 1.0F);
}
@Override
protected void onExit(ServerLevel level, BlockPos pos, UnderVaultConfig config, UnderVaultSharedData sharedData) {
UndertrialSounds.playServer(level, pos, UndertrialSounds.VAULT_CLOSE_SHUTTER, SoundSource.BLOCKS, 1.0F, 1.0F);
}
};
private static final int UPDATE_CONNECTED_PLAYERS_TICK_RATE = 20;
private static final int DELAY_BETWEEN_EJECTIONS_TICKS = 20;
private static final int DELAY_AFTER_LAST_EJECTION_TICKS = 20;
private static final int DELAY_BEFORE_FIRST_EJECTION_TICKS = 20;
private final String stateName;
private final int lightLevel;
UnderVaultState(String stateName, int lightLevel) {
this.stateName = stateName;
this.lightLevel = lightLevel;
}
@Override
public String getSerializedName() {
return this.stateName;
}
public int lightLevel() {
return this.lightLevel;
}
public UnderVaultState tickAndGetNext(ServerLevel level, BlockPos pos, UnderVaultConfig config, UnderVaultServerData serverData,
UnderVaultSharedData sharedData) {
switch (this) {
case INACTIVE:
return updateStateForConnectedPlayers(level, pos, config, serverData, sharedData, config.activationRange());
case ACTIVE:
return updateStateForConnectedPlayers(level, pos, config, serverData, sharedData, config.deactivationRange());
case UNLOCKING:
serverData.pauseStateUpdatingUntil(level.getGameTime() + DELAY_BEFORE_FIRST_EJECTION_TICKS);
return EJECTING;
case EJECTING:
default:
if (serverData.getItemsToEject().isEmpty()) {
serverData.markEjectionFinished();
return updateStateForConnectedPlayers(level, pos, config, serverData, sharedData, config.deactivationRange());
}
float progress = serverData.ejectionProgress();
this.ejectResultItem(level, pos, serverData.popNextItemToEject(), progress);
sharedData.setDisplayItem(serverData.getNextItemToEject());
boolean finished = serverData.getItemsToEject().isEmpty();
int delay = finished ? DELAY_AFTER_LAST_EJECTION_TICKS : DELAY_BETWEEN_EJECTIONS_TICKS;
serverData.pauseStateUpdatingUntil(level.getGameTime() + delay);
return EJECTING;
}
}
private static UnderVaultState updateStateForConnectedPlayers(ServerLevel level, BlockPos pos, UnderVaultConfig config,
UnderVaultServerData serverData, UnderVaultSharedData sharedData,
double range) {
sharedData.updateConnectedPlayersWithinRange(level, pos, serverData, config, range);
serverData.pauseStateUpdatingUntil(level.getGameTime() + UPDATE_CONNECTED_PLAYERS_TICK_RATE);
return sharedData.hasConnectedPlayers() ? ACTIVE : INACTIVE;
}
public void onTransition(ServerLevel level, BlockPos pos, UnderVaultState next, UnderVaultConfig config,
UnderVaultSharedData sharedData, boolean ominous) {
this.onExit(level, pos, config, sharedData);
next.onEnter(level, pos, config, sharedData, ominous);
}
protected void onEnter(ServerLevel level, BlockPos pos, UnderVaultConfig config, UnderVaultSharedData sharedData, boolean ominous) {
}
protected void onExit(ServerLevel level, BlockPos pos, UnderVaultConfig config, UnderVaultSharedData sharedData) {
}
private void ejectResultItem(ServerLevel level, BlockPos pos, ItemStack stack, float progress) {
DefaultDispenseItemBehavior.spawnItem(level, stack, 2, Direction.UP, Vec3.atBottomCenterOf(pos).relative(Direction.UP, 1.2D));
UnderVaultEffects.ejectItem(level, pos);
UndertrialSounds.playServer(level, pos, UndertrialSounds.VAULT_EJECT_ITEM, SoundSource.BLOCKS, 1.0F, 0.8F + 0.4F * progress);
}
}
@@ -1,11 +1,29 @@
package com.craisinlord.antarchy.content.boss;
import com.craisinlord.antarchy.config.AntarchySettings;
import com.craisinlord.antarchy.content.AntarchyTags;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.Mob;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
public final class BossCombatUtil {
private BossCombatUtil() {}
private static final Map<UUID, MagicWindowState> MAGIC_WINDOWS = new HashMap<>();
private static final Map<UUID, Long> MAGIC_WARD_EXPIRY_TICK = new HashMap<>();
private static final Map<UUID, Integer> MAGIC_WARD_BREACH_COUNTS = new HashMap<>();
private static final class MagicWindowState {
long windowStartTick;
float accumulated;
}
public static boolean isOutOfDamageRange(Mob boss, double range) {
if (range <= 0.0D) {
return false;
@@ -13,17 +31,63 @@ public final class BossCombatUtil {
return boss.level().getNearestPlayer(boss, range) == null;
}
/**
* Caps a single hit so it cannot bring the boss from above half of its max health
* down past that threshold in one shot. Once the boss is at or below half health,
* hits are no longer capped.
*/
public static float capSingleHitAtHalfHealth(LivingEntity boss, float amount) {
public static void clampHalfHealthCrossing(LivingEntity boss, float preHitHealth) {
float halfHealth = boss.getMaxHealth() * 0.5F;
float currentHealth = boss.getHealth();
if (currentHealth > halfHealth && currentHealth - amount < halfHealth) {
return currentHealth - halfHealth;
if (preHitHealth > halfHealth && boss.getHealth() < halfHealth) {
boss.setHealth(halfHealth);
}
return amount;
}
public static boolean isMagicBurstSource(DamageSource source) {
if (source.is(AntarchyTags.DamageType.ANTARCHY_MAGIC_BURST)) {
return true;
}
ResourceLocation typeId = source.typeHolder().unwrapKey().map(ResourceKey::location).orElse(null);
if (typeId == null) {
return false;
}
for (String namespace : AntarchySettings.magicBurstDamageNamespaces()) {
if (typeId.getNamespace().equals(namespace)) {
return true;
}
}
return false;
}
public static float trackMagicBurstWindow(LivingEntity boss, float amount, int windowTicks) {
long now = boss.level().getGameTime();
MagicWindowState state = MAGIC_WINDOWS.computeIfAbsent(boss.getUUID(), id -> new MagicWindowState());
if (now - state.windowStartTick >= windowTicks) {
state.windowStartTick = now;
state.accumulated = 0.0F;
}
state.accumulated += amount;
return state.accumulated;
}
public static float remainingWindowBudget(LivingEntity boss, float windowCap) {
MagicWindowState state = MAGIC_WINDOWS.get(boss.getUUID());
if (state == null) {
return windowCap;
}
long now = boss.level().getGameTime();
if (now - state.windowStartTick >= AntarchySettings.bossMagicWindowTicks()) {
return windowCap;
}
return Math.max(0.0F, windowCap - state.accumulated);
}
public static boolean isMagicWarded(LivingEntity boss) {
Long expiryTick = MAGIC_WARD_EXPIRY_TICK.get(boss.getUUID());
return expiryTick != null && boss.level().getGameTime() < expiryTick;
}
public static void triggerMagicWard(LivingEntity boss, int durationTicks) {
MAGIC_WARD_EXPIRY_TICK.put(boss.getUUID(), boss.level().getGameTime() + durationTicks);
MAGIC_WARD_BREACH_COUNTS.remove(boss.getUUID());
}
public static int recordMagicWindowBreach(LivingEntity boss) {
return MAGIC_WARD_BREACH_COUNTS.merge(boss.getUUID(), 1, Integer::sum);
}
}
@@ -0,0 +1,15 @@
package com.craisinlord.antarchy.content.client;
import com.craisinlord.antarchy.content.entity.nightmare.NightmareEntity;
import com.craisinlord.antos.api.client.archive.ArchiveEntityPreviewRegistry;
import net.minecraft.resources.ResourceLocation;
public final class AntarchyArchivePreviews {
private AntarchyArchivePreviews() {}
public static void register() {
ArchiveEntityPreviewRegistry.register(new ResourceLocation("antarchy", "nightmare"), entity -> {
if (entity instanceof NightmareEntity nightmare) nightmare.setArchivePreviewFlying();
});
}
}
@@ -0,0 +1,27 @@
package com.craisinlord.antarchy.content.client;
import com.craisinlord.antarchy.content.network.AntarchyGamePayload;
import com.craisinlord.antarchy.content.network.AntarchyGameResultPayload;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import net.minecraft.core.BlockPos;
public final class AntarchyGameClientState {
private static final Map<String, String> GAME_STATES = new ConcurrentHashMap<>();
private AntarchyGameClientState() { }
public static void update(AntarchyGameResultPayload result) {
String[] envelope = result.data().split("\u0000", 3);
if (envelope.length < 3) return;
if (envelope[0].equals(Integer.toString(AntarchyGamePayload.BASILISK_STATE))
|| envelope[0].equals(Integer.toString(AntarchyGamePayload.ANTMAN_STATE))) {
GAME_STATES.put(key(result.pos(), envelope[0]),
result.success() ? envelope[2] : "");
}
}
public static String getGameState(BlockPos pos, int action) { return GAME_STATES.get(key(pos, Integer.toString(action))); }
public static void clearGameState(BlockPos pos, int action) { GAME_STATES.remove(key(pos, Integer.toString(action))); }
private static String key(BlockPos pos, String action) { return pos.asLong() + "|" + action; }
}
@@ -1,92 +0,0 @@
package com.craisinlord.antarchy.content.client;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.function.BooleanSupplier;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.phys.Vec3;
public final class CameraShakeClientState {
public static final double TORETERROR_RANGE = 48.0D;
public static final float TORETERROR_STRENGTH = 1.0F;
public static final double NIGHTMARE_RANGE = 32.0D;
public static final float NIGHTMARE_STRENGTH = 1.35F;
private record Source(double range, float strength, BooleanSupplier active) {
}
private record Impact(Vec3 position, float intensity, int remainingTicks, int totalTicks, float radius) {
private Impact ticked() {
return new Impact(position, intensity, remainingTicks - 1, totalTicks, radius);
}
}
private static final Map<LivingEntity, Source> SOURCES = new HashMap<>();
private static final java.util.List<Impact> IMPACTS = new java.util.ArrayList<>();
private CameraShakeClientState() {
}
public static void register(LivingEntity entity, double range, float strength, BooleanSupplier active) {
SOURCES.putIfAbsent(entity, new Source(range, strength, active));
}
public static float getStrength(Vec3 cameraPos) {
float total = 0.0F;
Iterator<Map.Entry<LivingEntity, Source>> iterator = SOURCES.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<LivingEntity, Source> entry = iterator.next();
LivingEntity entity = entry.getKey();
Source source = entry.getValue();
if (entity.isRemoved() || !entity.isAlive() || !source.active().getAsBoolean()) {
iterator.remove();
continue;
}
double distance = Math.sqrt(cameraPos.distanceToSqr(entity.position().add(0.0D, entity.getBbHeight() * 0.5D, 0.0D)));
if (distance > source.range()) {
continue;
}
total += (float) ((1.0D - distance / source.range()) * source.strength());
}
Iterator<Impact> impactIterator = IMPACTS.iterator();
while (impactIterator.hasNext()) {
Impact impact = impactIterator.next();
if (impact.remainingTicks() <= 0) {
impactIterator.remove();
continue;
}
double distance = Math.sqrt(cameraPos.distanceToSqr(impact.position()));
if (distance > impact.radius()) {
continue;
}
total += (float) ((1.0D - distance / impact.radius()) * impact.intensity() * (impact.remainingTicks() / (float) Math.max(1, impact.totalTicks())));
}
return total;
}
public static void triggerImpact(Vec3 position, float intensity, int durationTicks, float radius) {
int ticks = Math.max(1, durationTicks);
IMPACTS.add(new Impact(position, intensity, ticks, ticks, Math.max(1.0F, radius)));
}
public static void tick() {
for (int i = IMPACTS.size() - 1; i >= 0; i--) {
Impact impact = IMPACTS.get(i);
if (impact.remainingTicks() <= 1) {
IMPACTS.remove(i);
} else {
IMPACTS.set(i, impact.ticked());
}
}
}
public static void clear() {
SOURCES.clear();
IMPACTS.clear();
}
}
@@ -1,146 +0,0 @@
package com.craisinlord.antarchy.content.client;
import com.craisinlord.antarchy.content.time.TimeDilationFieldSnapshot;
import com.craisinlord.antarchy.content.time.TimeDilationFieldSnapshotSampler;
import com.craisinlord.antarchy.content.time.TimeDilationMath;
import java.util.List;
import java.util.Map;
import java.util.WeakHashMap;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.item.ItemEntity;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.util.Mth;
public final class ClientTimeDilationTicker {
private static volatile List<TimeDilationFieldSnapshot> activeFields = List.of();
private static ClientLevel activeLevel;
private static final Map<ItemEntity, ItemSpinState> ITEM_SPIN_STATES = new WeakHashMap<>();
private static final Map<LivingEntity, AttackAnimationState> ATTACK_ANIMATION_STATES = new WeakHashMap<>();
private static final Map<Entity, AnimationClockState> ANIMATION_CLOCKS = new WeakHashMap<>();
private ClientTimeDilationTicker() {
}
public static void tick(ClientLevel level) {
if (activeLevel != level) {
activeLevel = level;
activeFields = List.of();
ITEM_SPIN_STATES.clear();
ATTACK_ANIMATION_STATES.clear();
ANIMATION_CLOCKS.clear();
com.craisinlord.antarchy.content.time.TimeDilationApi.clearSyncedClientRates();
}
}
public static float dilateAnimationTime(Entity entity, float vanillaTime) {
double rate = com.craisinlord.antarchy.content.time.TimeDilationApi.getRate(entity);
if (rate >= TimeDilationMath.NORMAL_RATE) {
ANIMATION_CLOCKS.remove(entity);
return vanillaTime;
}
AnimationClockState state = ANIMATION_CLOCKS.computeIfAbsent(entity,
ignored -> new AnimationClockState(vanillaTime));
float delta = vanillaTime - state.lastVanillaTime;
if (delta >= 0.0F) {
state.temporalTime += delta * (float) rate;
}
state.lastVanillaTime = vanillaTime;
return state.temporalTime;
}
public static float dilateAttackAnimation(LivingEntity entity, float partialTick, float vanillaAnimation) {
double rate = com.craisinlord.antarchy.content.time.TimeDilationApi.getRate(entity);
if (rate >= TimeDilationMath.NORMAL_RATE) {
ATTACK_ANIMATION_STATES.remove(entity);
return vanillaAnimation;
}
AttackAnimationState state = ATTACK_ANIMATION_STATES.computeIfAbsent(
entity, ignored -> new AttackAnimationState(entity.tickCount, vanillaAnimation)
);
if (state.lastTick != entity.tickCount) {
state.previous = state.current;
state.current += (vanillaAnimation - state.current) * (float) rate;
state.lastTick = entity.tickCount;
}
return state.previous + (state.current - state.previous) * partialTick;
}
public static float dilateRotation(LivingEntity entity, float previous, float current, float partialTick) {
double rate = com.craisinlord.antarchy.content.time.TimeDilationApi.getRate(entity);
if (rate >= TimeDilationMath.NORMAL_RATE) {
return Mth.rotLerp(partialTick, previous, current);
}
return previous + Mth.wrapDegrees(current - previous) * partialTick * (float) rate;
}
public static float dilatePitch(LivingEntity entity, float previous, float current, float partialTick) {
double rate = com.craisinlord.antarchy.content.time.TimeDilationApi.getRate(entity);
if (rate >= TimeDilationMath.NORMAL_RATE) {
return Mth.lerp(partialTick, previous, current);
}
return previous + (current - previous) * partialTick * (float) rate;
}
public static float dilateWalkAmount(LivingEntity entity, float amount) {
double rate = com.craisinlord.antarchy.content.time.TimeDilationApi.getRate(entity);
return rate >= TimeDilationMath.NORMAL_RATE ? amount : amount * (float) rate;
}
public static float dilateItemSpin(ItemEntity item, float vanillaSpin) {
double rate = com.craisinlord.antarchy.content.time.TimeDilationApi.getRate(item);
if (rate >= TimeDilationMath.NORMAL_RATE) {
ITEM_SPIN_STATES.remove(item);
return vanillaSpin;
}
ItemSpinState state = ITEM_SPIN_STATES.computeIfAbsent(item, ignored -> new ItemSpinState(vanillaSpin));
float rawDelta = vanillaSpin - state.lastVanillaSpin;
state.lastVanillaSpin = vanillaSpin;
state.temporalSpin += rawDelta * (float) rate;
return state.temporalSpin;
}
private static final class ItemSpinState {
private float lastVanillaSpin;
private float temporalSpin;
private ItemSpinState(float initialSpin) {
this.lastVanillaSpin = initialSpin;
this.temporalSpin = initialSpin;
}
}
private static final class AttackAnimationState {
private int lastTick;
private float previous;
private float current;
private AttackAnimationState(int tick, float initial) {
this.lastTick = tick;
this.previous = initial;
this.current = initial;
}
}
private static final class AnimationClockState {
private float lastVanillaTime;
private float temporalTime;
private AnimationClockState(float initialTime) {
this.lastVanillaTime = initialTime;
this.temporalTime = initialTime;
}
}
public static double rateAt(double x, double y, double z) {
List<TimeDilationFieldSnapshot> fields = activeFields;
if (fields.isEmpty()) {
return TimeDilationMath.NORMAL_RATE;
}
return TimeDilationFieldSnapshotSampler.sample(fields, x, y, z);
}
public static void applyFields(List<TimeDilationFieldSnapshot> fields) {
activeFields = List.copyOf(fields);
}
}
@@ -1,62 +0,0 @@
package com.craisinlord.antarchy.content.client;
import com.craisinlord.antarchy.content.effect.RoyalEffectHooks;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.Iterator;
import java.util.Map;
import java.util.WeakHashMap;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.effect.MobEffect;
public final class ContractionAfterimages {
public static final int MAX_SAMPLES = 10;
public static final int SAMPLE_LIFETIME_TICKS = 13;
private static final Map<LivingEntity, Deque<Sample>> HISTORY = new WeakHashMap<>();
private ContractionAfterimages() {}
public static void tick(ClientLevel level) {
MobEffect contracted = RoyalEffectHooks.contractedHolder();
int now = (int) (level.getGameTime() & 0x7fffffffL);
Iterator<Map.Entry<LivingEntity, Deque<Sample>>> iterator = HISTORY.entrySet().iterator();
while (iterator.hasNext()) {
LivingEntity tracked = iterator.next().getKey();
if (tracked == null || tracked.isRemoved() || !tracked.isAlive() || contracted == null
|| !tracked.hasEffect(contracted)) {
iterator.remove();
}
}
if (contracted == null) return;
for (Entity entity : level.entitiesForRendering()) {
if (!(entity instanceof LivingEntity living) || living.isInvisible() || !living.hasEffect(contracted)) continue;
Deque<Sample> samples = HISTORY.computeIfAbsent(living, ignored -> new ArrayDeque<>());
samples.addFirst(new Sample(living, now));
while (samples.size() > MAX_SAMPLES) samples.removeLast();
}
}
public static Deque<Sample> samples(LivingEntity entity) { return HISTORY.get(entity); }
public static float fade(Sample sample, int now) {
return Math.max(0.0F, 1.0F - (now - sample.recordedTick) / (float) SAMPLE_LIFETIME_TICKS);
}
public static void clear() { HISTORY.clear(); }
public static final class Sample {
public final double x, y, z;
public final float bodyYaw, headYaw, xRot, limbPos, limbSpeed, ageInTicks;
public final int recordedTick;
Sample(LivingEntity entity, int recordedTick) {
this.x = entity.getX(); this.y = entity.getY(); this.z = entity.getZ();
this.bodyYaw = entity.yBodyRot; this.headYaw = entity.getYHeadRot(); this.xRot = entity.getXRot();
this.limbPos = entity.walkAnimation.position();
this.limbSpeed = Math.min(entity.walkAnimation.speed(), 1.0F);
this.ageInTicks = entity.tickCount; this.recordedTick = recordedTick;
}
}
}
@@ -0,0 +1,128 @@
package com.craisinlord.antarchy.content.client;
import com.craisinlord.antarchy.config.AntarchySettings;
import com.craisinlord.antarchy.content.AntarchySoundEvents;
import com.craisinlord.antarchy.content.entity.HoverboardEntity;
import com.craisinlord.antarchy.mixins.LivingEntityJumpingAccessor;
import net.minecraft.client.Minecraft;
import net.minecraft.client.resources.sounds.AbstractTickableSoundInstance;
import net.minecraft.client.resources.sounds.SoundInstance;
import net.minecraft.sounds.SoundSource;
import net.minecraft.util.Mth;
import net.minecraft.util.RandomSource;
import net.minecraft.world.entity.Entity;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
public final class HoverboardSoundSystem {
private static final float MIN_PITCH = 0.75F;
private static final float MAX_PITCH = 1.6F;
private static final float MIN_VOLUME = 0.45F;
private static final float MAX_VOLUME = 0.8F;
private static final Map<Integer, IdleLoopSound> ACTIVE = new HashMap<>();
private HoverboardSoundSystem() {
}
public static void tick() {
Minecraft mc = Minecraft.getInstance();
if (mc.level == null) {
stopAll(mc);
return;
}
if (mc.isPaused()) {
return;
}
Set<Integer> activeIds = new HashSet<>();
for (Entity entity : mc.level.entitiesForRendering()) {
if (!(entity instanceof HoverboardEntity hoverboard) || !hoverboard.isVehicle()) {
continue;
}
int id = hoverboard.getId();
activeIds.add(id);
IdleLoopSound sound = ACTIVE.get(id);
if (sound == null || !mc.getSoundManager().isActive(sound)) {
sound = new IdleLoopSound(hoverboard);
ACTIVE.put(id, sound);
mc.getSoundManager().play(sound);
}
}
ACTIVE.entrySet().removeIf(entry -> {
if (activeIds.contains(entry.getKey())) {
return false;
}
mc.getSoundManager().stop(entry.getValue());
return true;
});
}
private static void stopAll(Minecraft mc) {
ACTIVE.values().forEach(mc.getSoundManager()::stop);
ACTIVE.clear();
}
private static final class IdleLoopSound extends AbstractTickableSoundInstance {
private final HoverboardEntity hoverboard;
private double lastX;
private double lastZ;
private IdleLoopSound(HoverboardEntity hoverboard) {
super(AntarchySoundEvents.HOVERBOARD_IDLE.get(), SoundSource.NEUTRAL, RandomSource.create());
this.hoverboard = hoverboard;
this.looping = true;
this.delay = 0;
this.relative = false;
this.attenuation = SoundInstance.Attenuation.LINEAR;
this.volume = MIN_VOLUME;
this.pitch = MIN_PITCH;
this.x = hoverboard.getX();
this.y = hoverboard.getY();
this.z = hoverboard.getZ();
this.lastX = this.x;
this.lastZ = this.z;
}
@Override
public void tick() {
if (this.hoverboard.isRemoved() || !this.hoverboard.isVehicle()) {
this.stop();
return;
}
double dx = this.hoverboard.getX() - this.lastX;
double dz = this.hoverboard.getZ() - this.lastZ;
double speed = Math.sqrt(dx * dx + dz * dz);
this.lastX = this.hoverboard.getX();
this.lastZ = this.hoverboard.getZ();
this.x = this.hoverboard.getX();
this.y = this.hoverboard.getY();
this.z = this.hoverboard.getZ();
double maxSpeed = Math.max(AntarchySettings.hoverboardMaxSpeed(), 0.0001D);
float speedRatio = (float) Mth.clamp(speed / maxSpeed, 0.0D, 1.0D);
boolean jumping = false;
Entity passenger = this.hoverboard.getControllingPassenger();
if (passenger instanceof net.minecraft.world.entity.player.Player player) {
jumping = ((LivingEntityJumpingAccessor) player).antarchy$isJumping();
}
boolean movingUpward = this.hoverboard.getDeltaMovement().y > 0.02D;
float liftRatio = jumping || movingUpward ? 1.0F : 0.0F;
this.volume = Mth.lerp(speedRatio, MIN_VOLUME, MAX_VOLUME);
this.pitch = Mth.lerp(speedRatio, MIN_PITCH, MAX_PITCH)
+ Mth.lerp(liftRatio, 0.0F, 0.2F);
}
@Override
public boolean canStartSilent() {
return true;
}
}
}
@@ -0,0 +1,36 @@
package com.craisinlord.antarchy.content.client;
public final class KingJudgmentFlashClientState {
private static int flashTicks;
private static int flashDuration;
private KingJudgmentFlashClientState() {
}
public static void trigger(int ticks) {
int duration = Math.max(0, ticks);
if (duration >= flashTicks) {
flashTicks = duration;
flashDuration = duration;
}
}
public static void tick() {
if (flashTicks > 0) {
flashTicks--;
}
}
public static float alpha() {
if (flashTicks <= 0 || flashDuration <= 0) {
return 0.0F;
}
float remaining = flashTicks / (float) flashDuration;
return Math.min(0.92F, 0.92F * remaining * remaining);
}
public static void clear() {
flashTicks = 0;
flashDuration = 0;
}
}
@@ -0,0 +1,17 @@
package com.craisinlord.antarchy.content.client;
import net.minecraft.client.gui.GuiGraphics;
public final class KingJudgmentFlashHudRenderer {
private KingJudgmentFlashHudRenderer() {
}
public static void render(GuiGraphics guiGraphics) {
int alpha = Math.round(KingJudgmentFlashClientState.alpha() * 255.0F);
if (alpha <= 0) {
return;
}
guiGraphics.fill(0, 0, guiGraphics.guiWidth(), guiGraphics.guiHeight(),
alpha << 24 | 0x00FFFFFF);
}
}
@@ -0,0 +1,54 @@
package com.craisinlord.antarchy.content.client;
import com.craisinlord.antarchy.content.network.PortalGunMoonAimPayload;
import com.craisinlord.antarchy.content.portalgun.PortalGunMoonMath;
import java.lang.ref.WeakReference;
import java.util.function.Consumer;
import net.minecraft.client.Camera;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.world.phys.Vec3;
import org.joml.Matrix4f;
import org.joml.Quaterniond;
import org.joml.Vector3d;
import org.joml.Vector3f;
public final class MoonSkyTracker {
private static final long MAX_AGE_MILLIS = 1500L;
private static final double AIM_COSINE = Math.cos(Math.toRadians(PortalGunMoonMath.AIM_TOLERANCE_DEGREES));
private static Vec3 direction;
// Weak so a dimension change or logout doesn't pin the old client world in memory.
private static WeakReference<ClientLevel> level = new WeakReference<>(null);
private static long recordedAt;
private static Consumer<PortalGunMoonAimPayload> sender = payload -> {};
private MoonSkyTracker() {
}
public static void setSender(Consumer<PortalGunMoonAimPayload> payloadSender) {
sender = payloadSender;
}
public static void recordSphericalMoon(ClientLevel renderedLevel, Camera camera, Matrix4f skyMatrix, double theta, double phi) {
Vector3d local = new Quaterniond().rotateY(theta).mul(new Quaterniond().rotateX(phi)).transform(new Vector3d(0.0D, 1.0D, 0.0D));
Vector3f view = skyMatrix.transformDirection(new Vector3f((float) local.x, (float) local.y, (float) local.z));
if (view.lengthSquared() < 1.0E-8F) {
return;
}
Vector3f world = camera.rotation().transform(view.normalize());
direction = new Vec3(world.x, world.y, world.z).normalize();
level = new WeakReference<>(renderedLevel);
recordedAt = System.currentTimeMillis();
}
public static void report() {
Minecraft minecraft = Minecraft.getInstance();
if (minecraft.player == null) {
return;
}
boolean tracked = direction != null && level.get() == minecraft.level && System.currentTimeMillis() - recordedAt <= MAX_AGE_MILLIS;
boolean aiming = tracked && minecraft.gameRenderer.getMainCamera().getLookVector().dot(
new Vector3f((float) direction.x, (float) direction.y, (float) direction.z)) >= AIM_COSINE;
sender.accept(new PortalGunMoonAimPayload(tracked, aiming));
}
}
@@ -2,7 +2,6 @@ package com.craisinlord.antarchy.content.client;
import com.craisinlord.antarchy.config.AntarchySettings;
import com.craisinlord.antarchy.content.item.NightmareSwordItem;
import net.minecraft.ChatFormatting;
import net.minecraft.client.Minecraft;
import net.minecraft.network.chat.Component;
import net.minecraft.world.entity.player.Player;
@@ -16,14 +15,6 @@ public final class NightmareSwordTooltipHelper {
float damage = player != null
? NightmareSwordItem.calculateDamage(player)
: (float) AntarchySettings.nightmareSwordBaseDamage();
return Component.translatable("tooltip.antarchy.nightmare_sword_damage", formatDamage(damage)).withStyle(ChatFormatting.RED);
}
private static String formatDamage(float damage) {
float rounded = Math.round(damage * 10.0F) / 10.0F;
if (rounded == (int) rounded) {
return String.valueOf((int) rounded);
}
return String.valueOf(rounded);
return NightmareSwordItem.damageLine(damage);
}
}
@@ -0,0 +1,96 @@
package com.craisinlord.antarchy.content.client;
import com.craisinlord.antarchy.content.item.PortalGunItem;
import com.craisinlord.antarchy.content.network.PortalGunIndicatorPayload;
import com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity;
import com.mojang.blaze3d.systems.RenderSystem;
import java.lang.ref.WeakReference;
import java.util.EnumSet;
import java.util.Set;
import java.util.UUID;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.ItemStack;
public final class PortalGunCrosshairRenderer {
private static final int WIDTH = 34;
private static final int HEIGHT = 33;
private static final int HALF_WIDTH = 17;
private static final float MISSING_BRIGHTNESS = 0.3F;
private static final float MISSING_ALPHA = 0.75F;
// Weak so a dimension change or logout doesn't pin the old client world in memory.
private static WeakReference<ClientLevel> cachedLevel = new WeakReference<>(null);
private static long cachedGameTime = Long.MIN_VALUE;
private static UUID cachedGunId;
private static Set<PortalGunPortalEntity.PortalSide> cachedPortals = Set.of();
private PortalGunCrosshairRenderer() {
}
public static void render(GuiGraphics guiGraphics) {
Minecraft minecraft = Minecraft.getInstance();
if (minecraft.player == null || minecraft.level == null || minecraft.screen != null || minecraft.options.hideGui
|| !minecraft.options.getCameraType().isFirstPerson()) {
return;
}
ItemStack held = minecraft.player.getMainHandItem();
if (!(held.getItem() instanceof PortalGunItem)) {
held = minecraft.player.getOffhandItem();
if (!(held.getItem() instanceof PortalGunItem)) {
return;
}
}
ResourceLocation crosshair = ((PortalGunItem) held.getItem()).getVariant().crosshairTexture();
UUID gunId = PortalGunItem.getGunId(held);
PortalGunIndicatorClientState.requestIfNeeded(minecraft, gunId);
Set<PortalGunPortalEntity.PortalSide> portals = portalsFor(minecraft, gunId);
PortalGunIndicatorPayload serverState = PortalGunIndicatorClientState.get(gunId);
boolean blueAvailable = serverState == null
? portals.contains(PortalGunPortalEntity.PortalSide.BLUE)
: serverState.blueAvailable();
boolean orangeAvailable = serverState == null
? portals.contains(PortalGunPortalEntity.PortalSide.ORANGE)
: serverState.orangeAvailable();
int left = (minecraft.getWindow().getGuiScaledWidth() - WIDTH) / 2;
int top = (minecraft.getWindow().getGuiScaledHeight() - HEIGHT) / 2;
RenderSystem.enableBlend();
RenderSystem.defaultBlendFunc();
drawHalf(guiGraphics, crosshair, left + 6, top, 0.0F, blueAvailable);
drawHalf(guiGraphics, crosshair, left + 11, top, HALF_WIDTH, orangeAvailable);
guiGraphics.setColor(1.0F, 1.0F, 1.0F, 1.0F);
RenderSystem.disableBlend();
}
private static void drawHalf(GuiGraphics guiGraphics, ResourceLocation crosshair, int x, int y, float u, boolean available) {
if (available) {
guiGraphics.setColor(1.0F, 1.0F, 1.0F, 1.0F);
} else {
guiGraphics.setColor(MISSING_BRIGHTNESS, MISSING_BRIGHTNESS, MISSING_BRIGHTNESS, MISSING_ALPHA);
}
guiGraphics.blit(crosshair, x, y, u, 0.0F, HALF_WIDTH, HEIGHT, WIDTH, HEIGHT);
}
private static Set<PortalGunPortalEntity.PortalSide> portalsFor(Minecraft minecraft, UUID gunId) {
if (gunId == null) {
return Set.of();
}
ClientLevel level = minecraft.level;
long gameTime = level.getGameTime();
if (cachedLevel.get() != level || cachedGameTime != gameTime || !gunId.equals(cachedGunId)) {
Set<PortalGunPortalEntity.PortalSide> portals = EnumSet.noneOf(PortalGunPortalEntity.PortalSide.class);
for (com.craisinlord.anthology.portal.PortalEntity entry : com.craisinlord.anthology.portal.PortalRegistry.all(level)) {
if (entry instanceof PortalGunPortalEntity portal && portal.isAlive() && gunId.equals(portal.getGunId())) {
portals.add(portal.getPortalSide());
}
}
cachedLevel = new WeakReference<>(level);
cachedGameTime = gameTime;
cachedGunId = gunId;
cachedPortals = Set.copyOf(portals);
}
return cachedPortals;
}
}
@@ -1,101 +0,0 @@
package com.craisinlord.antarchy.content.client;
import com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity;
import com.craisinlord.antarchy.content.portalgun.PortalGunTransformUtil;
import java.util.ArrayDeque;
import java.util.Deque;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.phys.Vec3;
public final class PortalGunEntityTransformationStack {
private final Entity entity;
private final Deque<EntityTransformation> stack = new ArrayDeque<>();
public PortalGunEntityTransformationStack(Entity entity) {
this.entity = entity;
}
public void push() {
this.stack.push(EntityTransformation.capture(this.entity));
}
public void pop() {
if (!this.stack.isEmpty()) {
this.stack.pop().restore(this.entity);
}
}
public Vec3 moveEntity(PortalGunPortalEntity sourcePortal, PortalGunPortalEntity destinationPortal, float partialTick) {
Vec3 interpolated = new Vec3(
net.minecraft.util.Mth.lerp(partialTick, this.entity.xo, this.entity.getX()),
net.minecraft.util.Mth.lerp(partialTick, this.entity.yo, this.entity.getY()),
net.minecraft.util.Mth.lerp(partialTick, this.entity.zo, this.entity.getZ())
);
Vec3 previous = new Vec3(this.entity.xo, this.entity.yo, this.entity.zo);
Vec3 transformed = destinationPortal.position().add(PortalGunTransformUtil.transformPosition(sourcePortal, destinationPortal, interpolated.subtract(sourcePortal.position())));
Vec3 transformedPrevious = destinationPortal.position().add(PortalGunTransformUtil.transformPosition(sourcePortal, destinationPortal, previous.subtract(sourcePortal.position())));
Vec3 look = PortalGunTransformUtil.transformVector(sourcePortal, destinationPortal, this.entity.getLookAngle()).normalize();
Vec3 velocity = PortalGunTransformUtil.transformVector(sourcePortal, destinationPortal, this.entity.getDeltaMovement());
float yaw = PortalGunTransformUtil.yawFromLook(look);
float pitch = PortalGunTransformUtil.pitchFromLook(look);
this.entity.setPos(transformed.x, transformed.y, transformed.z);
this.entity.xo = transformedPrevious.x;
this.entity.yo = transformedPrevious.y;
this.entity.zo = transformedPrevious.z;
this.entity.setYRot(yaw);
this.entity.setXRot(pitch);
this.entity.yRotO = yaw;
this.entity.xRotO = pitch;
this.entity.setDeltaMovement(velocity);
if (this.entity instanceof LivingEntity living) {
living.setYHeadRot(yaw);
living.setYBodyRot(yaw);
}
return transformed;
}
public void reset() {
while (!this.stack.isEmpty()) {
this.pop();
}
}
private record EntityTransformation(
double x,
double y,
double z,
double xo,
double yo,
double zo,
float yRot,
float xRot,
float yRotO,
float xRotO,
float yHeadRot,
float yBodyRot,
Vec3 deltaMovement
) {
private static EntityTransformation capture(Entity entity) {
float yHeadRot = entity instanceof LivingEntity living ? living.getYHeadRot() : entity.getYRot();
float yBodyRot = entity instanceof LivingEntity living ? living.yBodyRot : entity.getYRot();
return new EntityTransformation(entity.getX(), entity.getY(), entity.getZ(), entity.xo, entity.yo, entity.zo, entity.getYRot(), entity.getXRot(), entity.yRotO, entity.xRotO, yHeadRot, yBodyRot, entity.getDeltaMovement());
}
private void restore(Entity entity) {
entity.setPos(this.x, this.y, this.z);
entity.xo = this.xo;
entity.yo = this.yo;
entity.zo = this.zo;
entity.setYRot(this.yRot);
entity.setXRot(this.xRot);
entity.yRotO = this.yRotO;
entity.xRotO = this.xRotO;
entity.setDeltaMovement(this.deltaMovement);
if (entity instanceof LivingEntity living) {
living.setYHeadRot(this.yHeadRot);
living.setYBodyRot(this.yBodyRot);
}
}
}
}
@@ -0,0 +1,61 @@
package com.craisinlord.antarchy.content.client;
import com.craisinlord.antarchy.content.network.PortalGunIndicatorPayload;
import com.craisinlord.antarchy.content.network.PortalGunIndicatorRequestPayload;
import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.function.Consumer;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ClientLevel;
public final class PortalGunIndicatorClientState {
private static final Map<UUID, PortalGunIndicatorPayload> STATES = new HashMap<>();
// Weak so a dimension change or logout doesn't pin the old client world in memory.
private static WeakReference<ClientLevel> level = new WeakReference<>(null);
private static long lastRequestTick = Long.MIN_VALUE;
private static UUID lastRequestedGun;
private static Consumer<PortalGunIndicatorRequestPayload> requestSender = payload -> {};
private PortalGunIndicatorClientState() {
}
public static void setRequestSender(Consumer<PortalGunIndicatorRequestPayload> sender) {
requestSender = sender;
}
public static void requestIfNeeded(Minecraft minecraft, UUID gunId) {
ClientLevel currentLevel = minecraft.level;
if (currentLevel == null || gunId == null || minecraft.player == null) {
return;
}
if (level.get() != currentLevel) {
level = new WeakReference<>(currentLevel);
STATES.clear();
lastRequestedGun = null;
lastRequestTick = Long.MIN_VALUE;
}
long currentTick = minecraft.player.tickCount;
if (!gunId.equals(lastRequestedGun) || currentTick - lastRequestTick >= 20L) {
lastRequestedGun = gunId;
lastRequestTick = currentTick;
requestSender.accept(new PortalGunIndicatorRequestPayload(gunId));
}
}
public static void update(PortalGunIndicatorPayload payload) {
STATES.put(payload.gunId(), payload);
}
public static PortalGunIndicatorPayload get(UUID gunId) {
return gunId == null ? null : STATES.get(gunId);
}
public static void clear() {
STATES.clear();
level = new WeakReference<>(null);
lastRequestedGun = null;
lastRequestTick = Long.MIN_VALUE;
}
}
@@ -1,139 +0,0 @@
package com.craisinlord.antarchy.content.client;
import com.craisinlord.antarchy.content.portalgun.PortalGunWorldPortalShape;
import java.util.ArrayDeque;
import java.util.Deque;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
import org.joml.Matrix4f;
public final class PortalGunPortalRenderState {
private static final ThreadLocal<Deque<PortalGunWorldPortalShape>> ACTIVE_DESTINATION_SHAPES = ThreadLocal.withInitial(ArrayDeque::new);
private static final ThreadLocal<Deque<PortalGunWorldPortalShape>> ACTIVE_SOURCE_SHAPES = ThreadLocal.withInitial(ArrayDeque::new);
private static final ThreadLocal<Deque<PortalRenderContext>> ACTIVE_CONTEXTS = ThreadLocal.withInitial(ArrayDeque::new);
private static final ThreadLocal<Integer> RENDER_ALL_DEPTH = ThreadLocal.withInitial(() -> 0);
private PortalGunPortalRenderState() {
}
public static void pushPortalView(PortalGunWorldPortalShape sourceShape, PortalGunWorldPortalShape destinationShape, boolean renderAll) {
ACTIVE_SOURCE_SHAPES.get().push(sourceShape);
ACTIVE_DESTINATION_SHAPES.get().push(destinationShape);
if (renderAll) {
RENDER_ALL_DEPTH.set(RENDER_ALL_DEPTH.get() + 1);
}
}
public static void pushPortalView(PortalRenderContext context) {
ACTIVE_CONTEXTS.get().push(context);
pushPortalView(context.sourceShape(), context.destinationShape(), context.renderAll());
}
public static void popPortalView(boolean renderAll) {
Deque<PortalGunWorldPortalShape> sourceShapes = ACTIVE_SOURCE_SHAPES.get();
if (!sourceShapes.isEmpty()) {
sourceShapes.pop();
}
if (sourceShapes.isEmpty()) {
ACTIVE_SOURCE_SHAPES.remove();
}
Deque<PortalGunWorldPortalShape> destinationShapes = ACTIVE_DESTINATION_SHAPES.get();
if (!destinationShapes.isEmpty()) {
destinationShapes.pop();
}
if (destinationShapes.isEmpty()) {
ACTIVE_DESTINATION_SHAPES.remove();
}
if (renderAll) {
int depth = Math.max(0, RENDER_ALL_DEPTH.get() - 1);
if (depth == 0) {
RENDER_ALL_DEPTH.remove();
} else {
RENDER_ALL_DEPTH.set(depth);
}
}
Deque<PortalRenderContext> contexts = ACTIVE_CONTEXTS.get();
if (!contexts.isEmpty()) {
contexts.pop();
}
if (contexts.isEmpty()) {
ACTIVE_CONTEXTS.remove();
}
}
public static void pushDestinationShape(PortalGunWorldPortalShape shape) {
ACTIVE_DESTINATION_SHAPES.get().push(shape);
}
public static void popDestinationShape() {
Deque<PortalGunWorldPortalShape> shapes = ACTIVE_DESTINATION_SHAPES.get();
if (!shapes.isEmpty()) {
shapes.pop();
}
if (shapes.isEmpty()) {
ACTIVE_DESTINATION_SHAPES.remove();
}
}
public static PortalGunWorldPortalShape getDestinationShape() {
Deque<PortalGunWorldPortalShape> shapes = ACTIVE_DESTINATION_SHAPES.get();
return shapes.isEmpty() ? null : shapes.peek();
}
public static PortalGunWorldPortalShape getSourceShape() {
Deque<PortalGunWorldPortalShape> shapes = ACTIVE_SOURCE_SHAPES.get();
return shapes.isEmpty() ? null : shapes.peek();
}
public static PortalRenderContext getContext() {
Deque<PortalRenderContext> contexts = ACTIVE_CONTEXTS.get();
return contexts.isEmpty() ? null : contexts.peek();
}
public static int getRenderLevel() {
return ACTIVE_CONTEXTS.get().size();
}
public static boolean renderAll() {
return RENDER_ALL_DEPTH.get() > 0;
}
public static boolean shouldRenderBounds(AABB bounds) {
if (renderAll()) {
return true;
}
PortalRenderContext context = getContext();
if (context == null) {
PortalGunWorldPortalShape destinationShape = getDestinationShape();
PortalGunWorldPortalShape sourceShape = getSourceShape();
return destinationShape == null || shouldRenderBounds(bounds, sourceShape, destinationShape, false);
}
return shouldRenderBounds(bounds, context.sourceShape(), context.destinationShape(), context.renderAll());
}
private static boolean shouldRenderBounds(AABB bounds, PortalGunWorldPortalShape sourceShape, PortalGunWorldPortalShape destinationShape, boolean renderAll) {
if (renderAll) {
return true;
}
if (!destinationShape.intersectsFront(bounds, 0.125D)) {
return false;
}
if (!destinationShape.intersectsPortalColumn(bounds, 1.5D, 1.5D, destinationShape.halfDepth() + 96.0D)) {
return false;
}
return sourceShape == null || !sourceShape.intersectsFront(bounds, -0.02D);
}
public record PortalRenderContext(
PortalGunWorldPortalShape sourceShape,
PortalGunWorldPortalShape destinationShape,
Vec3 cameraPos,
Vec3 look,
Vec3 up,
Matrix4f viewMatrix,
Matrix4f projectionMatrix,
int renderLevel,
boolean renderAll
) {
}
}
@@ -0,0 +1,51 @@
package com.craisinlord.antarchy.content.client;
import com.craisinlord.antarchy.content.network.PortalGunResetPayload;
import java.util.function.Consumer;
import net.minecraft.client.Minecraft;
public final class PortalGunResetClientState {
private static boolean resetDown;
private static boolean attackDown;
private static boolean useDown;
private static boolean selectedSide;
private PortalGunResetClientState() {
}
public static boolean tick(Minecraft minecraft, boolean enabled, boolean resetPressed, Consumer<PortalGunResetPayload> sender) {
boolean canInput = enabled && minecraft.player != null && minecraft.level != null && minecraft.screen == null;
boolean active = canInput && resetPressed;
boolean attack = canInput && minecraft.options.keyAttack.isDown();
boolean use = canInput && minecraft.options.keyUse.isDown();
if (active && !resetDown) {
sender.accept(new PortalGunResetPayload(com.craisinlord.antarchy.content.portalgun.PortalGunResetManager.ARM));
selectedSide = false;
}
if (active && attack && !attackDown) {
sender.accept(new PortalGunResetPayload(com.craisinlord.antarchy.content.portalgun.PortalGunResetManager.RESET_BLUE));
selectedSide = true;
}
if (active && use && !useDown) {
selectedSide = true;
}
if (!active && resetDown) {
int action = !canInput || selectedSide
? com.craisinlord.antarchy.content.portalgun.PortalGunResetManager.RELEASE
: com.craisinlord.antarchy.content.portalgun.PortalGunResetManager.RESET_ALL;
sender.accept(new PortalGunResetPayload(action));
selectedSide = false;
}
resetDown = active;
attackDown = attack;
useDown = use;
return active;
}
public static void clear() {
resetDown = false;
attackDown = false;
useDown = false;
selectedSide = false;
}
}
@@ -1,123 +0,0 @@
package com.craisinlord.antarchy.content.client;
import com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity;
import com.craisinlord.antarchy.content.portalgun.PortalGunTransformUtil;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import net.minecraft.client.Minecraft;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.phys.Vec3;
public final class PortalGunRollClientState {
private static final int TELEPORT_COOLDOWN_TICKS = 40;
private static final float ROLL_DECAY = 0.85F;
private static final float ROLL_EPSILON = 0.05F;
private static final Map<UUID, Long> TELEPORT_COOLDOWNS = new HashMap<>();
private static float previousRollDegrees;
private static float rollDegrees;
private PortalGunRollClientState() {
}
public static float getRollDegrees() {
return rollDegrees;
}
public static float getRollDegrees(float partialTick) {
return previousRollDegrees + (rollDegrees - previousRollDegrees) * partialTick;
}
public static boolean isEntityInsidePortal(Minecraft minecraft, Entity entity) {
if (minecraft.level == null || entity == null) {
return false;
}
for (Entity renderEntity : minecraft.level.entitiesForRendering()) {
if (renderEntity instanceof PortalGunPortalEntity portal && portal.isAlive() && portal.intersectsEntityBounds(entity)) {
return true;
}
}
return false;
}
public static boolean isCameraInsidePortal(Minecraft minecraft) {
Entity cameraEntity = minecraft.getCameraEntity();
return cameraEntity != null && isEntityInsidePortal(minecraft, cameraEntity);
}
public static void clear() {
previousRollDegrees = 0.0F;
rollDegrees = 0.0F;
TELEPORT_COOLDOWNS.clear();
}
public static void tick(Minecraft minecraft) {
if (minecraft.level == null || minecraft.player == null) {
clear();
return;
}
Entity cameraEntity = minecraft.getCameraEntity();
if (cameraEntity == null) {
return;
}
previousRollDegrees = rollDegrees;
rollDegrees *= ROLL_DECAY;
if (Math.abs(rollDegrees) < ROLL_EPSILON) {
rollDegrees = 0.0F;
}
long gameTime = minecraft.level.getGameTime();
TELEPORT_COOLDOWNS.entrySet().removeIf(entry -> entry.getValue() <= gameTime);
for (Entity entity : minecraft.level.entitiesForRendering()) {
if (!(entity instanceof PortalGunPortalEntity portal) || !portal.isAlive()) {
continue;
}
PortalGunPortalEntity linkedPortal = portal.getLinkedPortal();
if (linkedPortal == null || !linkedPortal.isAlive()) {
continue;
}
if (!portal.shouldRenderFront(cameraEntity.position())) {
continue;
}
if (!canTeleport(cameraEntity, gameTime)) {
continue;
}
Vec3 crossingProbe = portal.resolveCrossingProbe(cameraEntity);
if (crossingProbe == null) {
continue;
}
applyPredictedTeleport(cameraEntity, portal, linkedPortal, crossingProbe, gameTime);
break;
}
}
private static boolean canTeleport(Entity entity, long gameTime) {
if (!entity.isAlive() || entity instanceof PortalGunPortalEntity || entity.isPassenger() || entity.isVehicle()) {
return false;
}
long cooldownUntil = TELEPORT_COOLDOWNS.getOrDefault(entity.getUUID(), 0L);
return cooldownUntil <= gameTime;
}
private static void applyPredictedTeleport(Entity entity, PortalGunPortalEntity sourcePortal, PortalGunPortalEntity destinationPortal, Vec3 currentProbe, long gameTime) {
Vec3 transformedLook = PortalGunTransformUtil.transformVector(sourcePortal, destinationPortal, entity.getLookAngle()).normalize();
Vec3 transformedUp = PortalGunTransformUtil.transformVector(sourcePortal, destinationPortal, PortalGunTransformUtil.upVectorFromLookAndRoll(entity.getLookAngle(), rollDegrees)).normalize();
Vec3 relativeProbe = currentProbe.subtract(sourcePortal.position());
Vec3 transformedProbe = PortalGunTransformUtil.transformPosition(sourcePortal, destinationPortal, relativeProbe);
Vec3 transformedEntityOffset = PortalGunTransformUtil.transformVector(sourcePortal, destinationPortal, entity.position().subtract(currentProbe));
Vec3 destinationNormal = destinationPortal.getNormalVec().normalize();
double exitClearance = Math.max(0.35D, entity.getBbWidth() * 0.5D + 0.16D);
Vec3 exitProbe = destinationPortal.position().add(transformedProbe).add(destinationNormal.scale(exitClearance - transformedProbe.dot(destinationNormal)));
Vec3 exitPos = exitProbe.add(transformedEntityOffset);
Vec3 transformedMotion = PortalGunTransformUtil.transformVector(sourcePortal, destinationPortal, entity.getDeltaMovement());
double outward = transformedMotion.dot(destinationNormal);
if (outward < 0.42D) {
transformedMotion = transformedMotion.add(destinationNormal.scale(0.42D - outward));
}
rollDegrees = PortalGunTransformUtil.rollFromOrientation(transformedLook, transformedUp);
entity.teleportTo(exitPos.x, exitPos.y, exitPos.z);
entity.setDeltaMovement(transformedMotion);
entity.setYRot(PortalGunTransformUtil.yawFromLook(transformedLook));
entity.setXRot(PortalGunTransformUtil.pitchFromLook(transformedLook));
TELEPORT_COOLDOWNS.put(entity.getUUID(), gameTime + TELEPORT_COOLDOWN_TICKS);
}
}
@@ -0,0 +1,80 @@
package com.craisinlord.antarchy.content.client;
import com.craisinlord.antarchy.content.item.PortalGunItem;
import net.minecraft.client.Minecraft;
import net.minecraft.client.Options;
import net.minecraft.world.item.ItemStack;
public final class PortalGunZoomClientState {
private static final int MAX_PROGRESS = 5;
private static int progress;
private static boolean zooming;
private static Double originalSensitivity;
private PortalGunZoomClientState() {
}
public static void tick(Minecraft minecraft, boolean held, boolean togglePressed) {
if (minecraft.player == null || minecraft.level == null) {
zooming = false;
progress = 0;
restoreSensitivity(minecraft.options);
return;
}
ItemStack main = minecraft.player.getMainHandItem();
ItemStack off = minecraft.player.getOffhandItem();
boolean holdingPortalGun = held && (main.getItem() instanceof PortalGunItem || off.getItem() instanceof PortalGunItem);
if (togglePressed && holdingPortalGun) {
zooming = !zooming;
if (zooming && originalSensitivity == null) {
originalSensitivity = minecraft.options.sensitivity().get();
}
}
if (!holdingPortalGun) {
zooming = false;
}
progress = Math.max(0, Math.min(MAX_PROGRESS, progress + (zooming ? 1 : -1)));
if (originalSensitivity != null) {
if (progress == 0) {
restoreSensitivity(minecraft.options);
}
}
}
public static double applyFov(double fov, float partialTick, Options options) {
if (progress == 0 && !zooming) {
restoreSensitivity(options);
return fov;
}
double tickProgress = Math.max(0.0D, Math.min(1.0D, partialTick));
double easedProgress = progress == MAX_PROGRESS
? MAX_PROGRESS
: zooming
? progress - 1.0D + tickProgress
: progress + 1.0D - tickProgress;
easedProgress = Math.max(0.0D, Math.min(MAX_PROGRESS, easedProgress));
double zoomFactor = zoomFactor(easedProgress);
if (originalSensitivity != null) {
options.sensitivity().set(originalSensitivity * zoomFactor);
}
return fov * zoomFactor;
}
private static double zoomFactor(double progress) {
double normalizedProgress = Math.max(0.0D, Math.min(1.0D, progress / MAX_PROGRESS));
return 0.1D + 0.9D * (1.0D - Math.sin(Math.toRadians(90.0D * normalizedProgress)));
}
private static double zoomFactor(int progress) {
return zoomFactor((double) progress);
}
private static void restoreSensitivity(Options options) {
if (originalSensitivity != null) {
options.sensitivity().set(originalSensitivity);
originalSensitivity = null;
}
}
}
@@ -0,0 +1,113 @@
package com.craisinlord.antarchy.content.client;
import com.craisinlord.antarchy.content.entity.royal.QueenEntity;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.screens.DeathScreen;
import net.minecraft.client.resources.sounds.AbstractTickableSoundInstance;
import net.minecraft.client.resources.sounds.SoundInstance;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.sounds.SoundSource;
import net.minecraft.util.Mth;
import net.minecraft.util.RandomSource;
import java.util.Comparator;
import java.util.function.Supplier;
public final class QueenMusicHandler {
private static final double START_RADIUS = 256.0D;
private static final double STOP_RADIUS = 320.0D;
private static final float MUSIC_VOLUME_SCALE = 0.7F;
private static QueenMusicSound music;
private QueenMusicHandler() {
}
public static boolean isPlaying() {
return music != null;
}
public static void tick(Minecraft minecraft, Supplier<SoundEvent> soundEventSupplier) {
if (minecraft.level == null || minecraft.player == null || minecraft.isPaused()
|| !minecraft.player.isAlive() || minecraft.player.isRemoved()
|| minecraft.player.isDeadOrDying() || minecraft.screen instanceof DeathScreen) {
reset(minecraft);
return;
}
QueenEntity queen = minecraft.level.getEntitiesOfClass(
QueenEntity.class,
minecraft.player.getBoundingBox().inflate(STOP_RADIUS),
entity -> entity.isQueenMusicActive() || entity.shouldPlayQueenMusic()
).stream()
.min(Comparator.comparingDouble(entity -> entity.distanceToSqr(minecraft.player)))
.orElse(null);
if (queen == null) {
stop(minecraft);
return;
}
if (music != null && !minecraft.getSoundManager().isActive(music)) {
music = null;
}
if (music == null) {
SoundEvent soundEvent;
try {
soundEvent = soundEventSupplier.get();
} catch (RuntimeException exception) {
return;
}
if (soundEvent == null) {
return;
}
minecraft.getMusicManager().stopPlaying();
music = new QueenMusicSound(soundEvent);
minecraft.getSoundManager().play(music);
}
if (music != null) {
double distance = Math.sqrt(queen.distanceToSqr(minecraft.player));
float volume = distance <= START_RADIUS
? 1.0F
: (float) Mth.clamp(1.0D - (distance - START_RADIUS) / (STOP_RADIUS - START_RADIUS), 0.0D, 1.0D);
music.setVolume(volume * MUSIC_VOLUME_SCALE);
}
}
private static void stop(Minecraft minecraft) {
if (music != null) {
minecraft.getSoundManager().stop(music);
music = null;
}
}
private static void reset(Minecraft minecraft) {
stop(minecraft);
}
private static final class QueenMusicSound extends AbstractTickableSoundInstance {
private QueenMusicSound(SoundEvent soundEvent) {
super(soundEvent, SoundSource.MUSIC, RandomSource.create());
this.looping = true;
this.delay = 0;
this.relative = true;
this.attenuation = SoundInstance.Attenuation.NONE;
this.volume = 1.0F;
this.pitch = 1.0F;
}
private void setVolume(float volume) {
this.volume = volume;
}
@Override
public void tick() {
}
@Override
public boolean canStartSilent() {
return true;
}
}
}
@@ -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) {
}
}
Loaded 100 of 5075 files, more files were not shown because too many files have changed in this diff. Show more