Initial commit

This commit is contained in:
craisinlord committed 2026-06-22 14:47:55 -05:00
commit cd0af5a1d3
2557 files changed
+271980

No files matched your search

+153
View File
@@ -0,0 +1,153 @@
plugins {
id "com.github.johnrengelman.shadow" version "7.1.2"
}
architectury {
platformSetupLoomIde()
fabric()
}
configurations {
common
shadowCommon
compileClasspath.extendsFrom common
runtimeClasspath.extendsFrom common
developmentFabric.extendsFrom common
}
dependencies {
minecraft "com.mojang:minecraft:${project.minecraft_version}"
mappings loom.officialMojangMappings()
modImplementation "net.fabricmc:fabric-loader:${project.fabric_loader_version}"
modApi "net.fabricmc.fabric-api:fabric-api:${project.fabric_api_version}"
modImplementation "software.bernie.geckolib:geckolib-fabric-${project.minecraft_version}:${project.geckolib_version}"
compileOnly("curse.maven:intapi-${project.intapi_curseforge_id}:${project.intapi_curseforge_file_id_neoforge}")
compileOnly("curse.maven:create-${project.create_curseforge_id_neoforge}:${project.create_curseforge_file_id_neoforge}")
compileOnly("curse.maven:marvel-${project.marvel_curseforge_id}:${project.marvel_curseforge_file_id}")
compileOnly("curse.maven:stellaris-${project.stellaris_curseforge_id}:${project.stellaris_curseforge_file_id}")
compileOnly("curse.maven:aperture-${project.aperture_curseforge_id}:${project.aperture_curseforge_file_id}")
compileOnly("curse.maven:inventory_pets-${project.inventory_pets_curseforge_id}:${project.inventory_pets_curseforge_file_id}")
compileOnly("curse.maven:cataclysm-${project.cataclysm_curseforge_id}:${project.cataclysm_curseforge_file_id}")
compileOnly("curse.maven:iaf-${project.iaf_curseforge_id}:${project.iaf_curseforge_file_id}")
compileOnly("curse.maven:backpack-${project.backpack_curseforge_id}:${project.backpack_curseforge_file_id}")
compileOnly("curse.maven:regions_unexplored-${project.regions_unexplored_curseforge_id}:${project.regions_unexplored_curseforge_file_id}")
compileOnly(files("$rootDir/infinite_dimensions_jars/infinity-2.7.1+1.21.1-neoforge.jar"))
compileOnly(files("$rootDir/infinite_dimensions_jars/infinity-2.7.1+1.21.1-neoforge-sources.jar"))
compileOnly(files("$rootDir/infinite_dimensions_jars/gravity-api-1.0.7-1.20.1.jar"))
compileOnly(files("$rootDir/infinite_dimensions_jars/Cloro's Gravity Gun Mod (Release 1.3.1).jar"))
compileOnly(files("$rootDir/infinite_dimensions_jars/VanillaBackport-neoforge-1.21.1-1.1.7.6.jar"))
compileOnly(files("$rootDir/infinite_dimensions_jars/soul-fire-d-neoforge-1.21-6.1.0.jar"))
compileOnly(files("$rootDir/infinite_dimensions_jars/prometheus-neoforge-1.21-1.2.5.jar"))
compileOnly(files("$rootDir/infinite_dimensions_jars/cloth-config-15.0.140-fabric.jar"))
compileOnly(files("$rootDir/infinite_dimensions_jars/sable-neoforge-1.21.1-2.0.3.jar"))
annotationProcessor "org.spongepowered:mixin:0.8.7:processor"
common(project(path: ':common', configuration: 'namedElements')) { transitive false }
shadowCommon(project(path: ':common', configuration: 'namedElements')) { transitive false }
}
loom {
mixin {
useLegacyMixinAp = true
defaultRefmapName = "antarchy.refmap.json"
}
mods {
"${mod_id}" {
sourceSet sourceSets.main
sourceSet project(':common').sourceSets.main
}
}
runs {
configureEach {
runDir('run')
ideConfigGenerated(true)
}
client {
client()
setConfigName('Fabric Client')
}
server {
server()
setConfigName('Fabric Server')
programArg('--nogui')
}
}
}
tasks.named('compileJava', JavaCompile) {
source(project(':common').sourceSets.main.allJava)
}
tasks.named('javadoc', Javadoc).configure {
source(project(':common').sourceSets.main.allJava)
}
tasks.named('sourcesJar', Jar) {
from(project(':common').sourceSets.main.allSource)
exclude "antarchy-common.mixins.json"
}
shadowJar {
exclude 'architectury.common.json'
configurations = [project.configurations.shadowCommon]
archiveClassifier.set('dev-shadow')
}
remapJar {
injectAccessWidener = true
input.set shadowJar.archiveFile
dependsOn shadowJar
archiveClassifier.set(null)
}
jar {
archiveClassifier.set('dev')
}
components.java {
withVariantsFromConfiguration(project.configurations.shadowRuntimeElements) {
skip()
}
}
processResources {
inputs.property "version", project.version
inputs.property "minecraft_version", project.minecraft_version
inputs.property "fabric_loader_version", project.fabric_loader_version
inputs.property "mod_id", project.mod_id
inputs.property "mod_name", project.mod_name
inputs.property "mod_description", project.mod_description
inputs.property "mod_author", project.mod_author
inputs.property "license", project.license
inputs.property "homepage_url", project.homepage_url
inputs.property "issues_url", project.issues_url
inputs.property "sources_url", project.sources_url
inputs.property "logo_fabric", project.logo_fabric
from(project(':common').sourceSets.main.resources) {
exclude "antarchy-common.mixins.json"
}
filesMatching("fabric.mod.json") {
expand(
version: project.version,
minecraft_version: project.minecraft_version,
fabric_loader_version: project.fabric_loader_version,
mod_id: project.mod_id,
mod_name: project.mod_name,
mod_description: project.mod_description,
mod_author: project.mod_author,
license: project.license,
homepage_url: project.homepage_url,
issues_url: project.issues_url,
sources_url: project.sources_url,
logo_fabric: project.logo_fabric
)
}
}
@@ -0,0 +1,272 @@
package com.craisinlord.antarchy.fabric;
import com.craisinlord.antarchy.Antarchy;
import com.craisinlord.antarchy.config.AntarchySettings;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.JsonParser;
import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.resources.ResourceKey;
import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.Map;
public final class AntarchyConfigModuleFabric {
private static final Gson GSON = new GsonBuilder().disableHtmlEscaping().setPrettyPrinting().create();
private static final Path CONFIG_DIR = FabricLoader.getInstance()
.getConfigDir()
.resolve(Antarchy.MODID);
private static final Path LEGACY_CONFIG_PATH = CONFIG_DIR.resolve("antarchy.json");
private static final Map<String, SettingBinding> SETTINGS = buildBindings();
private AntarchyConfigModuleFabric() {
}
public static void init() {
JsonObject legacy = readConfig(LEGACY_CONFIG_PATH);
Map<ConfigSection, JsonObject> loaded = new LinkedHashMap<>();
Map<ConfigSection, JsonObject> normalized = new LinkedHashMap<>();
for (ConfigSection section : ConfigSection.values()) {
loaded.put(section, readConfig(section.path));
normalized.put(section, new JsonObject());
}
for (Map.Entry<String, SettingBinding> entry : SETTINGS.entrySet()) {
String key = entry.getKey();
SettingBinding binding = entry.getValue();
JsonObject loadedSection = loaded.get(binding.section);
JsonElement element = loadedSection != null ? loadedSection.get(key) : null;
if (element == null && legacy != null) {
element = legacy.get(key);
}
Object value = binding.readValue(element);
binding.apply(value);
normalized.get(binding.section).add(key, binding.toJson(value));
}
for (Map.Entry<ConfigSection, JsonObject> entry : normalized.entrySet()) {
writeConfig(entry.getKey().path, entry.getValue());
}
}
private static JsonObject readConfig(Path path) {
if (!Files.exists(path)) {
return null;
}
try (Reader reader = Files.newBufferedReader(path)) {
JsonElement parsed = JsonParser.parseReader(reader);
if (parsed.isJsonObject()) {
return parsed.getAsJsonObject();
}
Antarchy.LOGGER.warn("Fabric config at {} is not a JSON object, using defaults", path);
} catch (IOException | JsonParseException e) {
Antarchy.LOGGER.warn("Failed to read Fabric config at {}, using defaults", path, e);
}
return null;
}
private static void writeConfig(Path path, JsonObject root) {
try {
Files.createDirectories(path.getParent());
try (Writer writer = Files.newBufferedWriter(path)) {
GSON.toJson(root, writer);
}
} catch (IOException e) {
Antarchy.LOGGER.warn("Failed to write Fabric config at {}", path, e);
}
}
private static Map<String, SettingBinding> buildBindings() {
Map<String, Method> getters = new LinkedHashMap<>();
for (Method method : AntarchySettings.class.getMethods()) {
if (!isPublicStatic(method) || method.getParameterCount() != 0 || method.getReturnType() == Void.TYPE) {
continue;
}
getters.put(method.getName(), method);
}
return Arrays.stream(AntarchySettings.class.getMethods())
.filter(AntarchyConfigModuleFabric::isPublicStatic)
.filter(method -> method.getName().startsWith("set"))
.filter(method -> method.getParameterCount() == 1)
.sorted(Comparator.comparing(Method::getName))
.collect(LinkedHashMap::new, (bindings, setter) -> {
String propertyName = decapitalize(setter.getName().substring(3));
Method getter = getters.get(propertyName);
if (getter == null) {
return;
}
if (!isSupported(setter.getParameterTypes()[0], getter.getReturnType())) {
return;
}
bindings.put(propertyName, new SettingBinding(propertyName, getter, setter, ConfigSection.forProperty(propertyName)));
}, Map::putAll);
}
private static boolean isPublicStatic(Method method) {
int modifiers = method.getModifiers();
return java.lang.reflect.Modifier.isPublic(modifiers) && java.lang.reflect.Modifier.isStatic(modifiers);
}
private static boolean isSupported(Class<?> setterType, Class<?> getterType) {
if (setterType == boolean.class || setterType == int.class || setterType == double.class || setterType == float.class) {
return getterType == setterType;
}
return setterType == String.class && ResourceKey.class.isAssignableFrom(getterType);
}
private static String decapitalize(String value) {
if (value.isEmpty()) {
return value;
}
return Character.toLowerCase(value.charAt(0)) + value.substring(1);
}
private enum ConfigSection {
MOBS("antarchy_mobs.json"),
TOOLS("antarchy_tools.json"),
MISC("antarchy_misc.json");
private final Path path;
ConfigSection(String fileName) {
this.path = CONFIG_DIR.resolve(fileName);
}
private static ConfigSection forProperty(String name) {
if (isMiscSetting(name)) {
return MISC;
}
if (isToolSetting(name)) {
return TOOLS;
}
return MOBS;
}
private static boolean isMiscSetting(String name) {
return name.equals("disableInfinityBookPortalCreation")
|| name.equals("rainbowAntsLeadToInfinityDimensions")
|| name.equals("hushweedSporeLifetimeSeconds")
|| name.equals("elythiaFireflyParticlesEnabled")
|| name.equals("duplicatorTreeEnabled")
|| name.equals("glowingTorchflowers")
|| name.equals("glowVinesUnderLeaves")
|| name.equals("entitySpecificFireOverlayEnabled")
|| name.equals("dreamSandEnabled")
|| name.startsWith("dreamSand")
|| name.equals("ichorWitherEnabled")
|| name.startsWith("diamondMinecart");
}
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("scorpionWhip")
|| name.startsWith("bloodCrystal")
|| name.startsWith("nightmareHelmet")
|| name.startsWith("nightmareChestplate")
|| name.startsWith("nightmareLeggings")
|| name.startsWith("nightmareBoots")
|| name.startsWith("nightmareArmorDreadAura")
|| name.startsWith("nightmareSword")
|| name.startsWith("basiliskDagger")
|| name.startsWith("squidzooka")
|| name.equals("invertProjectilesFromInvertedPlayers")
|| name.startsWith("gravityGun")
|| name.equals("potentNyxiteInvertedDurationSeconds")
|| name.equals("corneaEarNightVisionSeconds")
|| name.startsWith("mogglesVision")
|| name.equals("ductTapeRepairPercentPerUse")
|| name.startsWith("fallenKingCrown");
}
}
private static final class SettingBinding {
private final String name;
private final Method getter;
private final Method setter;
private final Class<?> setterType;
private final ConfigSection section;
private SettingBinding(String name, Method getter, Method setter, ConfigSection section) {
this.name = name;
this.getter = getter;
this.setter = setter;
this.setterType = setter.getParameterTypes()[0];
this.section = section;
}
private Object readValue(JsonElement element) {
Object defaultValue = getCurrentValue();
if (element == null || element.isJsonNull()) {
return defaultValue;
}
try {
if (setterType == boolean.class) {
return element.getAsBoolean();
}
if (setterType == int.class) {
return element.getAsInt();
}
if (setterType == double.class) {
return element.getAsDouble();
}
if (setterType == float.class) {
return element.getAsFloat();
}
if (setterType == String.class) {
return element.getAsString();
}
} catch (RuntimeException e) {
Antarchy.LOGGER.warn("Invalid Fabric config value for {}, using default", name, e);
}
return defaultValue;
}
private void apply(Object value) {
try {
setter.invoke(null, value);
} catch (IllegalAccessException | InvocationTargetException e) {
throw new IllegalStateException("Failed to apply config value for " + name, e);
}
}
private JsonElement toJson(Object value) {
return GSON.toJsonTree(value);
}
private Object getCurrentValue() {
try {
Object value = getter.invoke(null);
if (setterType == String.class && value instanceof ResourceKey<?> resourceKey) {
return resourceKey.location().toString();
}
return value;
} catch (IllegalAccessException | InvocationTargetException e) {
throw new IllegalStateException("Failed to read config default for " + name, e);
}
}
}
}
@@ -0,0 +1,19 @@
package com.craisinlord.antarchy.fabric;
import com.craisinlord.antarchy.Antarchy;
import com.craisinlord.antarchy.content.item.BloodCrystalShardItem;
import com.craisinlord.antarchy.fabric.network.AntarchyFabricNetworking;
import net.fabricmc.api.ModInitializer;
public final class AntarchyFabric implements ModInitializer {
@Override
public void onInitialize() {
AntarchyFabricNetworking.register();
AntarchyConfigModuleFabric.init();
AntarchyFabricContent.register();
AntarchyFabricEvents.register();
BloodglassManager.register();
Antarchy.init();
BloodCrystalShardItem.SYNC_BLOODGLASS = BloodglassManager::syncBloodglass;
}
}
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,177 @@
package com.craisinlord.antarchy.fabric;
import com.craisinlord.antarchy.content.entity.ReverieEntity;
import com.craisinlord.antarchy.config.AntarchySettings;
import com.craisinlord.antarchy.content.AntarchyObjects;
import com.craisinlord.antarchy.content.block.DuctTapeBlock;
import com.craisinlord.antarchy.content.block.PotentNyxiteBlock;
import com.craisinlord.antarchy.content.entity.MissileSquidEntity;
import com.craisinlord.antarchy.content.entity.kraken.KrakenEntity;
import com.craisinlord.antarchy.content.gravity.AntarchyGravityApi;
import com.craisinlord.antarchy.content.gravity.AntarchyGravityDirection;
import com.craisinlord.antarchy.content.gravity.AntarchyGravityTransition;
import net.fabricmc.fabric.api.entity.event.v1.ServerLivingEntityEvents;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.level.block.ComposterBlock;
import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.tags.FluidTags;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.MobSpawnType;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.effect.MobEffectInstance;
import net.minecraft.world.effect.MobEffects;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
public final class AntarchyFabricEvents {
private static final AntarchyGravityTransition INVERTED_CLEAR_TRANSITION = new AntarchyGravityTransition(12);
private static final Set<UUID> invertedPlayers = new HashSet<>();
private AntarchyFabricEvents() {
}
public static void register() {
ComposterBlock.COMPOSTABLES.put(AntarchyFabricContent.UMBRAL_MOSS_BLOCK.get().asItem(), 0.65f);
ComposterBlock.COMPOSTABLES.put(AntarchyFabricContent.UMBRAL_MOSS_CARPET.get().asItem(), 0.3f);
ComposterBlock.COMPOSTABLES.put(AntarchyFabricContent.HUSHWEED.get().asItem(), 0.65f);
ComposterBlock.COMPOSTABLES.put(AntarchyFabricContent.CORNEA_EAR.get(), 0.65f);
ServerLivingEntityEvents.AFTER_DEATH.register((entity, damageSource) -> {
if (entity instanceof ServerPlayer sp) {
BloodglassManager.handleDeath(sp);
}
if (!(entity instanceof MissileSquidEntity)) {
return;
}
if (!(entity.level() instanceof ServerLevel serverLevel)) {
return;
}
BlockPos deathPos = entity.blockPosition();
if (AntarchySettings.krakenSquidSpawnEnabled() && serverLevel.random.nextInt(100) == 0) {
spawnKrakens(serverLevel, deathPos, 1);
}
if (AntarchySettings.krakenMassSpawnEnabled() && serverLevel.random.nextInt(100) == 0) {
spawnKrakens(serverLevel, deathPos, 10);
}
});
ServerTickEvents.END_SERVER_TICK.register(server -> {
Set<UUID> activeThisTick = new HashSet<>();
for (ServerLevel level : server.getAllLevels()) {
tickInvertedPlayers(level, activeThisTick);
tickDuctTapePlayers(level);
tickIchorPlayers(level);
tickBloodglassRecharge(level);
}
invertedPlayers.retainAll(activeThisTick);
});
}
private static void spawnKrakens(ServerLevel level, BlockPos origin, int count) {
for (int i = 0; i < count; i++) {
BlockPos spawnPos = findKrakenSpawnPos(level, origin);
KrakenEntity kraken = AntarchyObjects.KRAKEN.get().create(level);
if (kraken == null) {
continue;
}
kraken.moveTo(spawnPos.getX() + 0.5D, spawnPos.getY() + 1.0D, spawnPos.getZ() + 0.5D,
level.random.nextFloat() * 360.0F, 0.0F);
kraken.finalizeSpawn(level, level.getCurrentDifficultyAt(spawnPos), MobSpawnType.EVENT, null);
level.addFreshEntity(kraken);
}
}
private static BlockPos findKrakenSpawnPos(ServerLevel level, BlockPos origin) {
for (int attempt = 0; attempt < 20; attempt++) {
BlockPos candidate = origin.offset(
level.random.nextInt(33) - 16,
level.random.nextInt(9) - 4,
level.random.nextInt(33) - 16
);
if (level.getFluidState(candidate).is(FluidTags.WATER)) {
return candidate;
}
}
return origin.above(3);
}
private static void tickInvertedPlayers(ServerLevel level, Set<UUID> activeThisTick) {
for (Player player : level.players()) {
LivingEntity livingEntity = player;
if (livingEntity.hasEffect(AntarchyObjects.INVERTED_EFFECT.get())) {
activeThisTick.add(player.getUUID());
invertedPlayers.add(player.getUUID());
continue;
}
if (!invertedPlayers.remove(player.getUUID())) {
continue;
}
if (AntarchyGravityApi.getGravityDirection(player) == AntarchyGravityDirection.UP
|| AntarchyGravityApi.isGravityForced(player)) {
AntarchyGravityApi.setGravityDirection(player, AntarchyGravityDirection.DOWN, INVERTED_CLEAR_TRANSITION);
}
notifyNearbyReveriesOfInversionChange(player);
}
}
private static void notifyNearbyReveriesOfInversionChange(Player player) {
for (ReverieEntity reverie : player.level().getEntitiesOfClass(
ReverieEntity.class,
player.getBoundingBox().inflate(48.0D)
)) {
reverie.syncFocusInversionState();
}
}
private static void tickDuctTapePlayers(ServerLevel level) {
for (Player player : level.players()) {
DuctTapeBlock.tickStuckEntity(player);
}
}
private static void tickIchorPlayers(ServerLevel level) {
if (!AntarchySettings.ichorWitherEnabled()) {
return;
}
for (Player player : level.players()) {
if (!antarchy$isInIchor(player)) {
continue;
}
player.addEffect(new MobEffectInstance(MobEffects.WITHER, 40, 0, false, true, true));
}
}
private static void tickBloodglassRecharge(ServerLevel level) {
for (Player player : level.players()) {
if (player instanceof ServerPlayer sp) {
BloodglassManager.tickRecharge(sp);
}
}
}
private static boolean antarchy$isInIchor(LivingEntity entity) {
BlockPos min = BlockPos.containing(entity.getBoundingBox().minX, entity.getBoundingBox().minY, entity.getBoundingBox().minZ);
BlockPos max = BlockPos.containing(entity.getBoundingBox().maxX, entity.getBoundingBox().maxY, entity.getBoundingBox().maxZ);
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);
if (PotentNyxiteBlock.isIchor(entity.level().getFluidState(cursor))) {
return true;
}
}
}
}
return false;
}
}
@@ -0,0 +1,231 @@
package com.craisinlord.antarchy.fabric;
import com.craisinlord.antarchy.config.AntarchySettings;
import com.craisinlord.antarchy.content.AntarchyObjects;
import com.craisinlord.antarchy.content.bloodglass.BloodglassAccess;
import com.craisinlord.antarchy.content.item.BloodCrystalArmorItem;
import com.craisinlord.antarchy.fabric.network.AntarchyFabricNetworking;
import net.fabricmc.fabric.api.entity.event.v1.ServerLivingEntityEvents;
import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents;
import net.minecraft.core.particles.DustParticleOptions;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.sounds.SoundSource;
import net.minecraft.tags.TagKey;
import net.minecraft.world.damagesource.DamageType;
import net.minecraft.world.entity.EquipmentSlot;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.core.registries.Registries;
import org.joml.Vector3f;
public final class BloodglassManager {
private static final TagKey<DamageType> BYPASSES_BLOODGLASS = TagKey.create(
Registries.DAMAGE_TYPE,
ResourceLocation.fromNamespaceAndPath("antarchy", "bypasses_bloodglass")
);
private BloodglassManager() {}
public static void register() {
ServerLivingEntityEvents.ALLOW_DAMAGE.register((entity, source, amount) -> {
if (!(entity instanceof Player player)) return true;
if (entity.level().isClientSide()) return true;
if (amount <= 1.0f) return true;
if (source.is(BYPASSES_BLOODGLASS)) return true;
BloodglassAccess access = (BloodglassAccess) player;
if (access.antarchy$getTotalShieldsActive() <= 0) return true;
if (access.antarchy$getAppleShieldsActive() > 0) {
access.antarchy$setAppleShieldsActive(access.antarchy$getAppleShieldsActive() - 1);
access.antarchy$setAppleShieldLostCount(access.antarchy$getAppleShieldLostCount() + 1);
if (access.antarchy$getAppleRechargeTimer() == 0) {
access.antarchy$setAppleRechargeTimer(AntarchySettings.bloodCrystalAppleShieldRechargeTicks());
}
} else {
access.antarchy$setArmorShieldsActive(access.antarchy$getArmorShieldsActive() - 1);
access.antarchy$setArmorShieldLostCount(access.antarchy$getArmorShieldLostCount() + 1);
if (access.antarchy$getArmorRechargeTimer() == 0) {
access.antarchy$setArmorRechargeTimer(AntarchySettings.bloodCrystalArmorShieldRechargeTicks());
}
}
ServerLevel serverLevel = (ServerLevel) player.level();
serverLevel.broadcastEntityEvent(player, (byte) 2);
serverLevel.playSound(null, player.blockPosition(), SoundEvents.GLASS_BREAK, SoundSource.PLAYERS, 1.0f, 1.0f);
serverLevel.sendParticles(
new DustParticleOptions(new Vector3f(0.85f, 0.18f, 0.38f), 1.2f),
player.getX(), player.getEyeY(), player.getZ(),
35, 0.35, 0.55, 0.35, 0.12
);
syncBloodglass((ServerPlayer) player);
return false;
});
ServerPlayConnectionEvents.JOIN.register((handler, sender, server) -> handleLogin(handler.player));
}
public static void syncBloodglass(ServerPlayer player) {
BloodglassAccess access = (BloodglassAccess) player;
AntarchyFabricNetworking.syncBloodglass(player,
access.antarchy$getTotalShieldsActive(),
access.antarchy$getTotalShieldsMax());
}
public static void handleArmorEquipChange(Player player, EquipmentSlot slot, ItemStack from, ItemStack to) {
if (player.level().isClientSide()) return;
if (slot != EquipmentSlot.HEAD && slot != EquipmentSlot.CHEST
&& slot != EquipmentSlot.LEGS && slot != EquipmentSlot.FEET) return;
boolean fromIsBC = from.getItem() instanceof BloodCrystalArmorItem;
boolean toIsBC = to.getItem() instanceof BloodCrystalArmorItem;
if (!fromIsBC && !toIsBC) return;
BloodglassAccess access = (BloodglassAccess) player;
if (fromIsBC && !toIsBC) {
if (access.antarchy$getArmorShieldsActive() > 0) {
access.antarchy$setArmorShieldsActive(access.antarchy$getArmorShieldsActive() - 1);
} else if (access.antarchy$getArmorShieldLostCount() > 0) {
access.antarchy$setArmorShieldLostCount(access.antarchy$getArmorShieldLostCount() - 1);
if (access.antarchy$getArmorShieldLostCount() == 0) {
access.antarchy$setArmorRechargeTimer(0);
}
}
} else if (!fromIsBC) {
// Count BC pieces: explicitly include the new piece (to) since getItemBySlot(slot)
// may not reflect the new item yet when onEquipItem fires on Fabric.
int bcPieces = 1; // the piece being equipped now
for (EquipmentSlot s : new EquipmentSlot[]{EquipmentSlot.HEAD, EquipmentSlot.CHEST, EquipmentSlot.LEGS, EquipmentSlot.FEET}) {
if (s == slot) continue;
if (player.getItemBySlot(s).getItem() instanceof BloodCrystalArmorItem) bcPieces++;
}
int currentTotal = access.antarchy$getArmorShieldsActive() + access.antarchy$getArmorShieldLostCount();
if (currentTotal < bcPieces) {
access.antarchy$setArmorShieldLostCount(access.antarchy$getArmorShieldLostCount() + 1);
access.antarchy$setArmorRechargeTimer(AntarchySettings.bloodCrystalArmorShieldRechargeTicks());
}
} else {
// BC → BC swap: remove old slot, queue new one
if (access.antarchy$getArmorShieldsActive() > 0) {
access.antarchy$setArmorShieldsActive(access.antarchy$getArmorShieldsActive() - 1);
} else if (access.antarchy$getArmorShieldLostCount() > 0) {
access.antarchy$setArmorShieldLostCount(access.antarchy$getArmorShieldLostCount() - 1);
}
access.antarchy$setArmorShieldLostCount(access.antarchy$getArmorShieldLostCount() + 1);
access.antarchy$setArmorRechargeTimer(AntarchySettings.bloodCrystalArmorShieldRechargeTicks());
}
if (player instanceof ServerPlayer sp) syncBloodglass(sp);
}
public static void handleWardApplied(Player player, int amplifier) {
if (player.level().isClientSide()) return;
BloodglassAccess access = (BloodglassAccess) player;
int cap = Math.max(0, AntarchySettings.bloodCrystalHardMaxShields()
- access.antarchy$getArmorShieldsActive()
- access.antarchy$getArmorShieldLostCount());
access.antarchy$setAppleShieldsActive(Math.min(amplifier + 1, cap));
access.antarchy$setAppleShieldLostCount(0);
access.antarchy$setAppleRechargeTimer(0);
if (player instanceof ServerPlayer sp) syncBloodglass(sp);
}
public static void handleWardRemoved(Player player) {
if (player.level().isClientSide()) return;
BloodglassAccess access = (BloodglassAccess) player;
access.antarchy$setAppleShieldsActive(0);
access.antarchy$setAppleShieldLostCount(0);
access.antarchy$setAppleRechargeTimer(0);
if (player instanceof ServerPlayer sp) syncBloodglass(sp);
}
public static void tickRecharge(ServerPlayer player) {
BloodglassAccess access = (BloodglassAccess) player;
boolean changed = false;
int hardCap = AntarchySettings.bloodCrystalHardMaxShields();
if (access.antarchy$getArmorShieldLostCount() > 0) {
int timer = access.antarchy$getArmorRechargeTimer();
if (timer > 1) {
access.antarchy$setArmorRechargeTimer(timer - 1);
} else if (timer == 1) {
if (access.antarchy$getTotalShieldsActive() < hardCap) {
access.antarchy$setArmorShieldsActive(access.antarchy$getArmorShieldsActive() + 1);
access.antarchy$setArmorShieldLostCount(access.antarchy$getArmorShieldLostCount() - 1);
changed = true;
}
access.antarchy$setArmorRechargeTimer(access.antarchy$getArmorShieldLostCount() > 0
? AntarchySettings.bloodCrystalArmorShieldRechargeTicks() : 0);
}
}
if (access.antarchy$getAppleShieldLostCount() > 0 && player.hasEffect(AntarchyObjects.BLOODGLASS_WARD.get())) {
int timer = access.antarchy$getAppleRechargeTimer();
if (timer > 1) {
access.antarchy$setAppleRechargeTimer(timer - 1);
} else if (timer == 1) {
if (access.antarchy$getTotalShieldsActive() < hardCap) {
access.antarchy$setAppleShieldsActive(access.antarchy$getAppleShieldsActive() + 1);
access.antarchy$setAppleShieldLostCount(access.antarchy$getAppleShieldLostCount() - 1);
changed = true;
}
access.antarchy$setAppleRechargeTimer(access.antarchy$getAppleShieldLostCount() > 0
? AntarchySettings.bloodCrystalAppleShieldRechargeTicks() : 0);
}
}
if (changed) {
syncBloodglass(player);
player.playNotifySound(SoundEvents.AMETHYST_BLOCK_CHIME, SoundSource.PLAYERS, 0.6f, 1.6f);
}
}
public static void handleDeath(ServerPlayer player) {
BloodglassAccess access = (BloodglassAccess) player;
access.antarchy$setArmorShieldsActive(0);
access.antarchy$setArmorShieldLostCount(0);
access.antarchy$setArmorRechargeTimer(0);
access.antarchy$setAppleShieldsActive(0);
access.antarchy$setAppleShieldLostCount(0);
access.antarchy$setAppleRechargeTimer(0);
syncBloodglass(player);
}
public static void handleRespawn(ServerPlayer player) {
BloodglassAccess access = (BloodglassAccess) player;
int shields = Math.min(countBCArmorPieces(player), AntarchySettings.bloodCrystalHardMaxShields());
access.antarchy$setArmorShieldsActive(shields);
access.antarchy$setArmorShieldLostCount(0);
access.antarchy$setArmorRechargeTimer(0);
syncBloodglass(player);
}
private static void handleLogin(ServerPlayer player) {
BloodglassAccess access = (BloodglassAccess) player;
if (player.hasEffect(AntarchyObjects.BLOODGLASS_WARD.get())) {
var effect = player.getEffect(AntarchyObjects.BLOODGLASS_WARD.get());
if (effect != null) {
int cap = Math.max(0, AntarchySettings.bloodCrystalHardMaxShields()
- access.antarchy$getArmorShieldsActive()
- access.antarchy$getArmorShieldLostCount());
access.antarchy$setAppleShieldsActive(Math.min(effect.getAmplifier() + 1, cap));
access.antarchy$setAppleShieldLostCount(0);
access.antarchy$setAppleRechargeTimer(0);
}
}
syncBloodglass(player);
}
private static int countBCArmorPieces(Player player) {
int count = 0;
for (EquipmentSlot slot : new EquipmentSlot[]{EquipmentSlot.HEAD, EquipmentSlot.CHEST, EquipmentSlot.LEGS, EquipmentSlot.FEET}) {
if (player.getItemBySlot(slot).getItem() instanceof BloodCrystalArmorItem) count++;
}
return count;
}
}
@@ -0,0 +1,44 @@
package com.craisinlord.antarchy.fabric;
import com.craisinlord.antarchy.compat.infinity.InfinityCompatBridge;
import net.lerariemann.infinity.util.InfinityMethods;
import net.lerariemann.infinity.util.teleport.WarpLogic;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.util.RandomSource;
public final class FabricInfinityCompat implements InfinityCompatBridge {
public static boolean isAvailableOnClasspath() {
try {
Class.forName("net.lerariemann.infinity.util.InfinityMethods");
Class.forName("net.lerariemann.infinity.util.teleport.WarpLogic");
return true;
} catch (Throwable ignored) {
return false;
}
}
@Override
public boolean isAvailable() {
return true;
}
@Override
public ResourceLocation getRandomDimensionId(RandomSource random) {
try {
return InfinityMethods.getRandomId(random);
} catch (Throwable throwable) {
return null;
}
}
@Override
public boolean requestWarp(ServerPlayer player, ResourceLocation dimensionId) {
try {
WarpLogic.requestWarp(player, dimensionId, false);
return true;
} catch (Throwable throwable) {
return false;
}
}
}
@@ -0,0 +1,13 @@
package com.craisinlord.antarchy.fabric.client;
import com.craisinlord.antarchy.fabric.network.AntarchyFabricClientNetworking;
import net.fabricmc.api.ClientModInitializer;
public final class AntarchyFabricClient implements ClientModInitializer {
@Override
public void onInitializeClient() {
AntarchyFabricClientNetworking.register();
AntarchyFabricClientBootstrap.register();
}
}
@@ -0,0 +1,312 @@
package com.craisinlord.antarchy.fabric.client;
import com.craisinlord.antarchy.Antarchy;
import com.craisinlord.antarchy.content.client.renderer.*;
import com.craisinlord.antarchy.content.client.particle.*;
import com.craisinlord.antarchy.content.client.renderer.AntiwaterFluidRenderer;
import com.craisinlord.antarchy.fabric.AntarchyFabricContent;
import net.fabricmc.fabric.api.client.rendering.v1.LivingEntityFeatureRendererRegistrationCallback;
import net.minecraft.world.entity.EntityType;
import net.fabricmc.fabric.api.blockrenderlayer.v1.BlockRenderLayerMap;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
import net.fabricmc.fabric.api.client.render.fluid.v1.FluidRenderHandlerRegistry;
import net.fabricmc.fabric.api.client.particle.v1.ParticleFactoryRegistry;
import net.fabricmc.fabric.api.client.rendering.v1.BlockEntityRendererRegistry;
import net.fabricmc.fabric.api.client.rendering.v1.ColorProviderRegistry;
import net.fabricmc.fabric.api.client.rendering.v1.EntityModelLayerRegistry;
import net.fabricmc.fabric.api.client.rendering.v1.EntityRendererRegistry;
import net.fabricmc.fabric.api.client.rendering.v1.HudRenderCallback;
import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents;
import net.minecraft.client.model.BoatModel;
import net.minecraft.client.model.ChestBoatModel;
import net.minecraft.client.renderer.BiomeColors;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.texture.TextureAtlas;
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider;
import net.minecraft.client.renderer.blockentity.CampfireRenderer;
import net.minecraft.client.renderer.entity.ThrownItemRenderer;
import net.minecraft.client.renderer.item.ItemProperties;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.Mth;
import net.minecraft.world.item.CrossbowItem;
import net.minecraft.world.item.Items;
import net.minecraft.world.item.component.ChargedProjectiles;
import net.minecraft.world.level.FoliageColor;
import net.fabricmc.fabric.api.client.render.fluid.v1.SimpleFluidRenderHandler;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.material.FluidState;
import net.minecraft.world.level.BlockAndTintGetter;
import com.mojang.blaze3d.vertex.VertexConsumer;
import java.util.function.Function;
public final class AntarchyFabricClientBootstrap {
private AntarchyFabricClientBootstrap() {
}
public static void register() {
AntarchyKeyBindings.register();
registerRenderers();
registerEntityLayers();
registerColors();
registerParticles();
registerFluids();
registerRenderLayers();
registerItemProperties();
registerClientCallbacks();
}
private static void registerRenderers() {
BlockEntityRendererRegistry.register(
(net.minecraft.world.level.block.entity.BlockEntityType) AntarchyFabricContent.DREAM_CAMPFIRE_BLOCK_ENTITY.get(),
(BlockEntityRendererProvider) CampfireRenderer::new
);
EntityRendererRegistry.register(AntarchyFabricContent.EASTER_BUNNY.get(), EasterBunnyRenderer::new);
EntityRendererRegistry.register(AntarchyFabricContent.FLYING_SQUIRREL.get(), FlyingSquirrelRenderer::new);
EntityRendererRegistry.register(AntarchyFabricContent.CATERPILLAR.get(), CaterpillarRenderer::new);
EntityRendererRegistry.register(AntarchyFabricContent.BUTTERFLY.get(), ButterflyRenderer::new);
EntityRendererRegistry.register(AntarchyFabricContent.REVERIE.get(), ReverieRenderer::new);
EntityRendererRegistry.register(AntarchyFabricContent.BRUTALFLY.get(), BrutalflyRenderer::new);
EntityRendererRegistry.register(AntarchyFabricContent.RED_ANT.get(), AntRenderer::new);
EntityRendererRegistry.register(AntarchyFabricContent.BROWN_ANT.get(), AntRenderer::new);
EntityRendererRegistry.register(AntarchyFabricContent.RAINBOW_ANT.get(), AntRenderer::new);
EntityRendererRegistry.register(AntarchyFabricContent.MOLEWORM.get(), MolewormRenderer::new);
EntityRendererRegistry.register(AntarchyFabricContent.MANTIS.get(), MantisRenderer::new);
EntityRendererRegistry.register(AntarchyFabricContent.MOLEVORE.get(), MolevoreRenderer::new);
EntityRendererRegistry.register(AntarchyFabricContent.TRIFFID.get(), TriffidRenderer::new);
EntityRendererRegistry.register(AntarchyFabricContent.OURANWOOD_BOAT_ENTITY.get(), context -> new OuranwoodBoatRenderer<>(context, ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "textures/entity/boat/ouranwood.png"), false));
EntityRendererRegistry.register(AntarchyFabricContent.OURANWOOD_CHEST_BOAT_ENTITY.get(), context -> new OuranwoodBoatRenderer<>(context, ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "textures/entity/chest_boat/ouranwood.png"), true));
EntityRendererRegistry.register(AntarchyFabricContent.APPLE_COW.get(), AppleCowRenderer::new);
EntityRendererRegistry.register(AntarchyFabricContent.GOLDEN_APPLE_COW.get(), AppleCowRenderer::new);
EntityRendererRegistry.register(AntarchyFabricContent.ENCHANTED_GOLDEN_APPLE_COW.get(), AppleCowRenderer::new);
EntityRendererRegistry.register(AntarchyFabricContent.DIAMOND_MINECART.get(), DiamondMinecartRenderer::new);
EntityRendererRegistry.register(AntarchyFabricContent.DR_TRAYAURUS.get(), context -> new DrTrayaurusRenderer(context, ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "textures/entity/dr_trayaurus.png")));
EntityRendererRegistry.register(AntarchyFabricContent.CLOUD_SHARK.get(), CloudSharkRenderer::new);
EntityRendererRegistry.register(AntarchyFabricContent.KRAKEN.get(), KrakenRenderer::new);
EntityRendererRegistry.register(AntarchyFabricContent.MISSILE_SQUID.get(), MissileSquidRenderer::new);
EntityRendererRegistry.register(AntarchyFabricContent.NIGHTMARE.get(), NightmareRenderer::new);
EntityRendererRegistry.register(AntarchyFabricContent.LUCID.get(), LucidRenderer::new);
EntityRendererRegistry.register(AntarchyFabricContent.BED_BUG.get(), BedBugRenderer::new);
EntityRendererRegistry.register(AntarchyFabricContent.WASP.get(), WaspRenderer::new);
EntityRendererRegistry.register(AntarchyFabricContent.BOMBER.get(), BomberRenderer::new);
EntityRendererRegistry.register(AntarchyFabricContent.BASILISK.get(), BasiliskRenderer::new);
EntityRendererRegistry.register(AntarchyFabricContent.SHRINK_RAY_PROJECTILE.get(), SizeRayProjectileRenderer::new);
EntityRendererRegistry.register(AntarchyFabricContent.GROWTH_RAY_PROJECTILE.get(), SizeRayProjectileRenderer::new);
EntityRendererRegistry.register(AntarchyFabricContent.LUCID_BOLT.get(), LucidBoltRenderer::new);
EntityRendererRegistry.register(AntarchyFabricContent.LUCID_PEARL_PROJECTILE.get(), ThrownItemRenderer::new);
EntityRendererRegistry.register(AntarchyFabricContent.HUSH_PROJECTILE.get(), HushProjectileRenderer::new);
EntityRendererRegistry.register(AntarchyFabricContent.BRUTALFLY_ORB.get(), BrutalflyOrbRenderer::new);
EntityRendererRegistry.register(AntarchyFabricContent.UPWARD_FALLING_BLOCK.get(), UpwardFallingBlockRenderer::new);
EntityRendererRegistry.register(AntarchyFabricContent.SCORPION.get(), ScorpionRenderer::new);
EntityRendererRegistry.register(AntarchyFabricContent.EMPEROR_SCORPION.get(), EmperorScorpionRenderer::new);
EntityModelLayerRegistry.registerModelLayer(OuranwoodBoatRenderer.boatLayer(), BoatModel::createBodyModel);
EntityModelLayerRegistry.registerModelLayer(OuranwoodBoatRenderer.chestBoatLayer(), ChestBoatModel::createBodyModel);
}
private static void registerColors() {
ColorProviderRegistry.BLOCK.register(
(state, level, pos, tintIndex) -> level != null && pos != null
? BiomeColors.getAverageFoliageColor(level, pos)
: FoliageColor.getDefaultColor(),
AntarchyFabricContent.OURANWOOD_LEAVES.get()
);
ColorProviderRegistry.BLOCK.register((state, level, pos, tintIndex) -> 0xFF4A0000, AntarchyFabricContent.ANTIWATER_BLOCK.get());
ColorProviderRegistry.ITEM.register((stack, tintIndex) -> FoliageColor.getDefaultColor(), AntarchyFabricContent.OURANWOOD_LEAVES_ITEM.get());
}
private static void registerFluids() {
FluidRenderHandlerRegistry.INSTANCE.register(
AntarchyFabricContent.ICHOR.get(),
AntarchyFabricContent.FLOWING_ICHOR.get(),
new SimpleFluidRenderHandler(
ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "block/antiwater_still"),
ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "block/antiwater_flow"),
ResourceLocation.withDefaultNamespace("block/water_overlay"),
0xFF2A0306
)
);
FluidRenderHandlerRegistry.INSTANCE.register(
AntarchyFabricContent.ANTIWATER.get(),
AntarchyFabricContent.FLOWING_ANTIWATER.get(),
new SimpleFluidRenderHandler(
ResourceLocation.withDefaultNamespace("block/water_still"),
ResourceLocation.withDefaultNamespace("block/water_flow"),
ResourceLocation.withDefaultNamespace("block/water_overlay"),
0xFFFF1A1A
) {
@Override
public void renderFluid(BlockPos pos, BlockAndTintGetter world, VertexConsumer vertexConsumer, BlockState blockState, FluidState fluidState) {
TextureAtlasSprite[] sprites = this.getFluidSprites(world, pos, fluidState);
AntiwaterFluidRenderer.render(
world,
pos,
vertexConsumer,
blockState,
fluidState,
sprites[0],
sprites[1],
sprites.length > 2 ? sprites[2] : null,
this.getFluidColor(world, pos, fluidState)
);
}
}
);
}
private static void registerParticles() {
ParticleFactoryRegistry registry = ParticleFactoryRegistry.getInstance();
registry.register(AntarchyFabricContent.DREAM_FIRE_FLAME.get(), DreamFlameParticle.Provider::new);
registry.register(AntarchyFabricContent.HYPNOTIC_GAS.get(), HypnoticGasParticle.Provider::new);
registry.register(AntarchyFabricContent.HYPNOTIC_GAS_DOWN.get(), sprites -> new HypnoticGasParticle.Provider(sprites, true));
registry.register(AntarchyFabricContent.HYPNOTIC_GAS_CLOUD.get(), new HypnoticGasCloudParticle.Provider());
registry.register(AntarchyFabricContent.HYPNOTIC_GAS_CLOUD_DOWN.get(), new HypnoticGasCloudParticle.Provider(true));
registry.register(AntarchyFabricContent.INVERTED_GEYSER_BASE.get(), InvertedGeyserBaseParticle.Provider::new);
registry.register(AntarchyFabricContent.INVERTED_GEYSER_PLUME.get(), InvertedGeyserPlumeParticle.Provider::new);
registry.register(AntarchyFabricContent.INVERTED_GEYSER_POOF.get(), InvertedGeyserBaseParticle.Provider::new);
registry.register(AntarchyFabricContent.INVERTED_GEYSER_ERUPTION.get(), new InvertedGeyserEruptionParticle.Provider());
registry.register(AntarchyFabricContent.FIREFLY.get(), FireflyParticle.Provider::new);
}
private static void registerRenderLayers() {
BlockRenderLayerMap.INSTANCE.putBlock(AntarchyFabricContent.OURANWOOD_LEAVES.get(), RenderType.cutout());
BlockRenderLayerMap.INSTANCE.putBlock(AntarchyFabricContent.OURANWOOD_DOOR.get(), RenderType.cutout());
BlockRenderLayerMap.INSTANCE.putBlock(AntarchyFabricContent.OURANWOOD_TRAPDOOR.get(), RenderType.cutout());
BlockRenderLayerMap.INSTANCE.putBlock(AntarchyFabricContent.OURANWOOD_ACORN_BLOCK.get(), RenderType.cutout());
BlockRenderLayerMap.INSTANCE.putBlock(AntarchyFabricContent.DUPLICATOR_SAPLING.get(), RenderType.cutout());
BlockRenderLayerMap.INSTANCE.putBlock(AntarchyFabricContent.ORANGE_MILKWEED.get(), RenderType.cutout());
BlockRenderLayerMap.INSTANCE.putBlock(AntarchyFabricContent.PINK_MILKWEED.get(), RenderType.cutout());
BlockRenderLayerMap.INSTANCE.putBlock(AntarchyFabricContent.TORCHFLOWER_BUSH.get(), RenderType.cutout());
BlockRenderLayerMap.INSTANCE.putBlock(AntarchyFabricContent.HUSHWEED.get(), RenderType.cutout());
BlockRenderLayerMap.INSTANCE.putBlock(AntarchyFabricContent.CORNEA_STALK.get(), RenderType.cutout());
BlockRenderLayerMap.INSTANCE.putBlock(AntarchyFabricContent.ANTIMETAL_SCAFFOLDING.get(), RenderType.cutout());
BlockRenderLayerMap.INSTANCE.putBlock(AntarchyFabricContent.UMBRAL_MOSS_CARPET.get(), RenderType.cutout());
BlockRenderLayerMap.INSTANCE.putBlock(AntarchyFabricContent.NYXITE_SPIKE.get(), RenderType.cutout());
BlockRenderLayerMap.INSTANCE.putBlock(AntarchyFabricContent.SMALL_BLOOD_CRYSTAL_BUD.get(), RenderType.cutout());
BlockRenderLayerMap.INSTANCE.putBlock(AntarchyFabricContent.MEDIUM_BLOOD_CRYSTAL_BUD.get(), RenderType.cutout());
BlockRenderLayerMap.INSTANCE.putBlock(AntarchyFabricContent.LARGE_BLOOD_CRYSTAL_BUD.get(), RenderType.cutout());
BlockRenderLayerMap.INSTANCE.putBlock(AntarchyFabricContent.BLOOD_CRYSTAL_CRYSTAL.get(), RenderType.cutout());
BlockRenderLayerMap.INSTANCE.putBlock(AntarchyFabricContent.DREAM_TORCH.get(), RenderType.cutout());
BlockRenderLayerMap.INSTANCE.putBlock(AntarchyFabricContent.DREAM_WALL_TORCH.get(), RenderType.cutout());
BlockRenderLayerMap.INSTANCE.putBlock(AntarchyFabricContent.DREAM_LANTERN.get(), RenderType.cutout());
BlockRenderLayerMap.INSTANCE.putBlock(AntarchyFabricContent.DREAM_CAMPFIRE.get(), RenderType.cutout());
BlockRenderLayerMap.INSTANCE.putBlock(AntarchyFabricContent.DREAM_FIRE.get(), RenderType.cutout());
BlockRenderLayerMap.INSTANCE.putBlock(AntarchyFabricContent.DREAM_CEILING_FIRE.get(), RenderType.cutout());
BlockRenderLayerMap.INSTANCE.putBlock(AntarchyFabricContent.ANTIWATER_BLOCK.get(), RenderType.translucent());
BlockRenderLayerMap.INSTANCE.putBlock(AntarchyFabricContent.TRIFFID_GOO_BLOCK.get(), RenderType.translucent());
BlockRenderLayerMap.INSTANCE.putBlock(AntarchyFabricContent.URANIUM_DOOR.get(), RenderType.cutout());
BlockRenderLayerMap.INSTANCE.putBlock(AntarchyFabricContent.TITANIUM_DOOR.get(), RenderType.cutout());
BlockRenderLayerMap.INSTANCE.putBlock(AntarchyFabricContent.URANIUM_TRAPDOOR.get(), RenderType.cutout());
BlockRenderLayerMap.INSTANCE.putBlock(AntarchyFabricContent.TITANIUM_TRAPDOOR.get(), RenderType.cutout());
BlockRenderLayerMap.INSTANCE.putBlock(AntarchyFabricContent.URANIUM_BARS.get(), RenderType.cutout());
BlockRenderLayerMap.INSTANCE.putBlock(AntarchyFabricContent.TITANIUM_BARS.get(), RenderType.cutout());
BlockRenderLayerMap.INSTANCE.putBlock(AntarchyFabricContent.CLOUD_BLOCK.get(), RenderType.translucent());
BlockRenderLayerMap.INSTANCE.putFluids(
RenderType.translucent(),
AntarchyFabricContent.ICHOR.get(),
AntarchyFabricContent.FLOWING_ICHOR.get(),
AntarchyFabricContent.ANTIWATER.get(),
AntarchyFabricContent.FLOWING_ANTIWATER.get()
);
}
private static void registerItemProperties() {
ItemProperties.register(
AntarchyFabricContent.ULTIMATE_BOW.get(),
ResourceLocation.withDefaultNamespace("pull"),
(stack, level, entity, seed) -> entity == null || entity.getUseItem() != stack
? 0.0F
: (stack.getItem().getUseDuration(stack, entity) - entity.getUseItemRemainingTicks()) / 20.0F
);
ItemProperties.register(
AntarchyFabricContent.ULTIMATE_BOW.get(),
ResourceLocation.withDefaultNamespace("pulling"),
(stack, level, entity, seed) -> entity != null && entity.isUsingItem() && entity.getUseItem() == stack ? 1.0F : 0.0F
);
ItemProperties.register(
AntarchyFabricContent.ULTIMATE_CROSSBOW.get(),
ResourceLocation.withDefaultNamespace("pull"),
(stack, level, entity, seed) -> {
if (entity == null || CrossbowItem.isCharged(stack) || entity.getUseItem() != stack) {
return 0.0F;
}
float adjustedChargeDuration = Math.max(1.0F, stack.getItem().getUseDuration(stack, entity) - 3.0F);
return Mth.clamp((stack.getItem().getUseDuration(stack, entity) - entity.getUseItemRemainingTicks()) / adjustedChargeDuration, 0.0F, 1.0F);
}
);
ItemProperties.register(
AntarchyFabricContent.ULTIMATE_CROSSBOW.get(),
ResourceLocation.withDefaultNamespace("pulling"),
(stack, level, entity, seed) -> entity != null && entity.isUsingItem() && entity.getUseItem() == stack && !CrossbowItem.isCharged(stack) ? 1.0F : 0.0F
);
ItemProperties.register(
AntarchyFabricContent.ULTIMATE_CROSSBOW.get(),
ResourceLocation.withDefaultNamespace("charged"),
(stack, level, entity, seed) -> CrossbowItem.isCharged(stack) ? 1.0F : 0.0F
);
ItemProperties.register(
AntarchyFabricContent.ULTIMATE_CROSSBOW.get(),
ResourceLocation.withDefaultNamespace("firework"),
(stack, level, entity, seed) -> CrossbowItem.isCharged(stack) && ChargedProjectiles.of(stack).contains(Items.FIREWORK_ROCKET) ? 1.0F : 0.0F
);
}
@SuppressWarnings({"unchecked", "rawtypes"})
private static void registerEntityLayers() {
LivingEntityFeatureRendererRegistrationCallback.EVENT.register((entityType, renderer, registrationHelper, context) -> {
if (entityType == EntityType.PLAYER) {
if (renderer instanceof net.minecraft.client.renderer.entity.player.PlayerRenderer playerRenderer) {
registrationHelper.register(new ParalyzedStonePlayerLayer(playerRenderer));
registrationHelper.register(new BrutalflyElytraLayer(playerRenderer));
registrationHelper.register(new FallenKingCrownLayer(playerRenderer));
}
return;
}
if (software.bernie.geckolib.renderer.GeoEntityRenderer.class.isAssignableFrom(renderer.getClass())) {
software.bernie.geckolib.renderer.GeoEntityRenderer geoRenderer =
(software.bernie.geckolib.renderer.GeoEntityRenderer) (Object) renderer;
geoRenderer.addRenderLayer(new ParalyzedStoneGeoLayer(geoRenderer));
return;
}
registrationHelper.register(new ParalyzedStoneLivingLayer(renderer));
});
}
private static void registerClientCallbacks() {
ClientTickEvents.END_CLIENT_TICK.register(client -> {
if (client.player != null) {
ParalyzedClientHandler.clampPlayerInput(client.player);
}
BrutalflyElytraClientHandler.tick();
DiamondMinecartClientHandler.tick();
ParalyzedClientHandler.tick();
DreadClientHandler.tick();
BloodCrystalKatanaTrailHandler.tick();
ScorpionWhipTetherRenderHandler.tick();
});
GravityGunClientHandler.register();
ElythiaFireflyManager.register();
LucidSoundHandler.register();
MogglesClientRenderer.register();
ReverieTrailHandler.register();
HudRenderCallback.EVENT.register((guiGraphics, partialTick) -> {
DreadHudRenderer.render(guiGraphics);
ParalyzedHudRenderer.render(guiGraphics);
BrutalflyElytraHudRenderer.render(guiGraphics);
BloodglassHudRenderer.render(guiGraphics);
TriffidGooHudRenderer.render(guiGraphics);
});
WorldRenderEvents.AFTER_TRANSLUCENT.register(context -> {
BloodCrystalKatanaTrailHandler.render(context);
ScorpionWhipTetherRenderHandler.render(context);
});
}
}
@@ -0,0 +1,29 @@
package com.craisinlord.antarchy.fabric.client;
import com.craisinlord.antarchy.Antarchy;
import com.mojang.blaze3d.platform.InputConstants;
import org.lwjgl.glfw.GLFW;
import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper;
import net.minecraft.client.KeyMapping;
import net.minecraft.client.Minecraft;
public final class AntarchyKeyBindings {
public static final String CATEGORY = "key.categories.antarchy";
public static final KeyMapping BRUTALFLY_FLAP = new KeyMapping(
"key.antarchy.brutalfly_flap",
InputConstants.Type.KEYSYM,
GLFW.GLFW_KEY_G,
CATEGORY
);
private AntarchyKeyBindings() {}
public static void register() {
KeyBindingHelper.registerKeyBinding(BRUTALFLY_FLAP);
}
public static boolean isBrutalflyFlapPressed() {
return Minecraft.getInstance().screen == null && BRUTALFLY_FLAP.isDown();
}
}
@@ -0,0 +1,42 @@
package com.craisinlord.antarchy.fabric.client;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
public final class BloodCrystalKatanaTrailClientState {
private static final Map<Integer, Integer> ACTIVE = new HashMap<>();
private BloodCrystalKatanaTrailClientState() {}
public static void trigger(int entityId, int durationTicks) {
if (durationTicks <= 0) {
ACTIVE.remove(entityId);
return;
}
ACTIVE.put(entityId, durationTicks);
}
public static int getRemainingTicks(int entityId) {
return ACTIVE.getOrDefault(entityId, 0);
}
public static void tick() {
Iterator<Map.Entry<Integer, Integer>> iterator = ACTIVE.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<Integer, Integer> entry = iterator.next();
int remaining = entry.getValue() - 1;
if (remaining <= 0) {
iterator.remove();
} else {
entry.setValue(remaining);
}
}
}
public static void clear() {
ACTIVE.clear();
}
}
@@ -0,0 +1,208 @@
package com.craisinlord.antarchy.fabric.client;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.BufferBuilder;
import com.mojang.blaze3d.vertex.BufferUploader;
import com.mojang.blaze3d.vertex.DefaultVertexFormat;
import com.mojang.blaze3d.vertex.MeshData;
import com.mojang.blaze3d.vertex.Tesselator;
import com.mojang.blaze3d.vertex.VertexFormat;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import net.minecraft.client.Camera;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.GameRenderer;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.phys.Vec3;
import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext;
import org.joml.Matrix4f;
public final class BloodCrystalKatanaTrailHandler {
private static final int MAX_TRAIL_POINTS = 16;
private static final int POINT_LIFETIME_TICKS = 10;
private static final double MIN_DISTANCE_BETWEEN_POINTS = 0.05D;
private static final float CORE_WIDTH = 0.06F;
private static final float GLOW_WIDTH = 0.16F;
private static final Map<Integer, ArrayDeque<TrailPoint>> TRAILS = new HashMap<>();
private BloodCrystalKatanaTrailHandler() {}
private static final class TrailPoint {
private final Vec3 position;
private int age;
private TrailPoint(Vec3 position) {
this.position = position;
}
private float alpha() {
return 1.0F - ((float) age / POINT_LIFETIME_TICKS);
}
private boolean expired() {
return age >= POINT_LIFETIME_TICKS || alpha() <= 0.01F;
}
}
public static void tick() {
Minecraft mc = Minecraft.getInstance();
if (mc.level == null) {
TRAILS.clear();
BloodCrystalKatanaTrailClientState.clear();
return;
}
if (mc.isPaused()) {
return;
}
BloodCrystalKatanaTrailClientState.tick();
Set<Integer> activeIds = new HashSet<>();
for (Entity entity : mc.level.entitiesForRendering()) {
int remaining = BloodCrystalKatanaTrailClientState.getRemainingTicks(entity.getId());
if (remaining <= 0) {
continue;
}
activeIds.add(entity.getId());
Vec3 position = entity.position().add(0.0D, entity.getBbHeight() * 0.45D, 0.0D);
ArrayDeque<TrailPoint> points = TRAILS.computeIfAbsent(entity.getId(), id -> new ArrayDeque<>());
if (points.isEmpty() || points.peekFirst().position.distanceToSqr(position) >= MIN_DISTANCE_BETWEEN_POINTS * MIN_DISTANCE_BETWEEN_POINTS) {
points.addFirst(new TrailPoint(position));
while (points.size() > MAX_TRAIL_POINTS) {
points.pollLast();
}
}
}
TRAILS.keySet().removeIf(id -> !activeIds.contains(id));
for (ArrayDeque<TrailPoint> points : TRAILS.values()) {
Iterator<TrailPoint> iterator = points.iterator();
while (iterator.hasNext()) {
TrailPoint point = iterator.next();
point.age++;
if (point.expired()) {
iterator.remove();
}
}
}
TRAILS.values().removeIf(ArrayDeque::isEmpty);
}
public static void render(WorldRenderContext context) {
if (TRAILS.isEmpty()) {
return;
}
if (context.world() == null || context.matrixStack() == null) {
return;
}
Minecraft mc = Minecraft.getInstance();
if (mc.level == null) {
return;
}
Camera camera = context.camera();
Vec3 cameraPos = camera.getPosition();
Matrix4f matrix = context.matrixStack().last().pose();
RenderSystem.enableBlend();
RenderSystem.defaultBlendFunc();
RenderSystem.depthMask(false);
RenderSystem.disableCull();
RenderSystem.setShader(GameRenderer::getPositionColorShader);
Tesselator tesselator = Tesselator.getInstance();
List<List<TrailPoint>> snapshots = new ArrayList<>(TRAILS.size());
for (ArrayDeque<TrailPoint> deque : TRAILS.values()) {
if (deque.size() >= 2) {
snapshots.add(new ArrayList<>(deque));
}
}
if (snapshots.isEmpty()) {
RenderSystem.enableCull();
RenderSystem.depthMask(true);
RenderSystem.disableBlend();
return;
}
BufferBuilder glowBuffer = tesselator.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_COLOR);
for (List<TrailPoint> points : snapshots) {
buildTrail(glowBuffer, matrix, points, cameraPos, GLOW_WIDTH, 0.95F, 0.14F, 0.18F, 0.38F);
}
MeshData glowMesh = glowBuffer.build();
if (glowMesh != null) {
BufferUploader.drawWithShader(glowMesh);
}
BufferBuilder coreBuffer = tesselator.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_COLOR);
for (List<TrailPoint> points : snapshots) {
buildTrail(coreBuffer, matrix, points, cameraPos, CORE_WIDTH, 1.0F, 0.32F, 0.36F, 0.92F);
}
MeshData coreMesh = coreBuffer.build();
if (coreMesh != null) {
BufferUploader.drawWithShader(coreMesh);
}
RenderSystem.enableCull();
RenderSystem.depthMask(true);
RenderSystem.disableBlend();
}
private static void buildTrail(BufferBuilder builder, Matrix4f matrix, List<TrailPoint> points, Vec3 cameraPos,
float width, float red, float green, float blue, float alphaScale) {
for (int i = 0; i < points.size() - 1; i++) {
TrailPoint newer = points.get(i);
TrailPoint older = points.get(i + 1);
Vec3 side = cameraFacingRight(newer.position, older.position, cameraPos);
if (side == null) {
continue;
}
float nearAlpha = newer.alpha() * alphaScale;
float farAlpha = older.alpha() * alphaScale * 0.85F;
Vec3 nearOffset = side.scale(width);
Vec3 farOffset = side.scale(width);
Vec3 nearPos = newer.position.subtract(cameraPos);
Vec3 farPos = older.position.subtract(cameraPos);
builder.addVertex(matrix, (float) (nearPos.x + nearOffset.x), (float) (nearPos.y + nearOffset.y), (float) (nearPos.z + nearOffset.z)).setColor(red, green, blue, nearAlpha);
builder.addVertex(matrix, (float) (nearPos.x - nearOffset.x), (float) (nearPos.y - nearOffset.y), (float) (nearPos.z - nearOffset.z)).setColor(red, green, blue, nearAlpha);
builder.addVertex(matrix, (float) (farPos.x - farOffset.x), (float) (farPos.y - farOffset.y), (float) (farPos.z - farOffset.z)).setColor(red, green, blue, farAlpha);
builder.addVertex(matrix, (float) (farPos.x + farOffset.x), (float) (farPos.y + farOffset.y), (float) (farPos.z + farOffset.z)).setColor(red, green, blue, farAlpha);
}
}
private static Vec3 cameraFacingRight(Vec3 current, Vec3 next, Vec3 cameraPos) {
Vec3 segmentDirection = next.subtract(current);
if (segmentDirection.lengthSqr() < 1.0E-8D) {
return null;
}
segmentDirection = segmentDirection.normalize();
Vec3 midpoint = current.add(next).scale(0.5D);
Vec3 cameraDirection = cameraPos.subtract(midpoint);
if (cameraDirection.lengthSqr() < 1.0E-8D) {
return null;
}
cameraDirection = cameraDirection.normalize();
Vec3 side = segmentDirection.cross(cameraDirection);
if (side.lengthSqr() < 1.0E-8D) {
side = segmentDirection.cross(new Vec3(0.0D, 1.0D, 0.0D));
if (side.lengthSqr() < 1.0E-8D) {
return null;
}
}
return side.normalize();
}
}
@@ -0,0 +1,64 @@
package com.craisinlord.antarchy.fabric.client;
import com.craisinlord.antarchy.Antarchy;
import com.craisinlord.antarchy.content.client.BloodglassClientState;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.Mth;
import net.minecraft.world.entity.EquipmentSlot;
import net.minecraft.world.entity.player.Player;
public final class BloodglassHudRenderer {
private static final ResourceLocation HEART_CONTAINER =
ResourceLocation.withDefaultNamespace("textures/gui/sprites/hud/heart/container.png");
private static final ResourceLocation HEART_FULL =
ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "textures/gui/sprites/hud/bloodglass_heart.png");
private static final ResourceLocation HEART_CRACKED =
ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "textures/gui/sprites/hud/bloodglass_heart_cracked.png");
private static final int HEART_SIZE = 9;
private static final int HEART_PITCH = 8;
private BloodglassHudRenderer() {}
public static void render(GuiGraphics gui) {
Minecraft mc = Minecraft.getInstance();
if (mc.options.hideGui) return;
Player player = mc.player;
if (player == null || player.isDeadOrDying()) return;
// Don't show in creative or spectator — no health bar visible there
if (player.isCreative() || player.isSpectator()) return;
int total = BloodglassClientState.getShieldsMax();
if (total <= 0) return;
int active = BloodglassClientState.getShieldsActive();
int w = mc.getWindow().getGuiScaledWidth();
int h = mc.getWindow().getGuiScaledHeight();
// Vanilla health bar bottom row is at y = h - 39; each extra row (absorption, extra hearts) is 10px above.
// The armor protection icons sit one row above the bottom health row (h - 49).
// We place bloodglass hearts above both health+absorption stack AND the armor row.
int maxHealth = Mth.ceil(player.getMaxHealth());
int absorption = Mth.ceil(player.getAbsorptionAmount());
int healthHearts = Mth.ceil(maxHealth / 2.0f);
int absorptionHearts = Mth.ceil(absorption / 2.0f);
int totalVanillaRows = Mth.ceil((healthHearts + absorptionHearts) / 10.0f);
boolean hasArmor = player.getArmorValue() > 0;
// +1 to clear the armor icon row only when armor is actually equipped
int baseTop = h - 39 - (totalVanillaRows * 10) - (hasArmor ? 10 : 0);
int left = w / 2 - 91;
// Wrap across multiple rows if more than 10 bloodglass hearts
for (int i = 0; i < total; i++) {
int row = i / 10;
int col = i % 10;
int x = left + col * HEART_PITCH;
int y = baseTop - row * 10;
ResourceLocation tex = (i < active) ? HEART_FULL : HEART_CRACKED;
gui.blit(HEART_CONTAINER, x, y, 0, 0, HEART_SIZE, HEART_SIZE, HEART_SIZE, HEART_SIZE);
gui.blit(tex, x, y, 0, 0, HEART_SIZE, HEART_SIZE, HEART_SIZE, HEART_SIZE);
}
}
}
@@ -0,0 +1,73 @@
package com.craisinlord.antarchy.fabric.client;
import com.craisinlord.antarchy.content.client.BrutalflyElytraClientState;
import com.craisinlord.antarchy.content.item.BrutalflyElytraFlightHelper;
import com.craisinlord.antarchy.content.item.BrutalflyElytraItem;
import com.craisinlord.antarchy.content.network.BrutalflyElytraFlapPayload;
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking;
import net.minecraft.client.Minecraft;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public final class BrutalflyElytraClientHandler {
private static final Logger LOGGER = LoggerFactory.getLogger("Antarchy/BrutalflyElytra");
private static boolean lastJumpDown;
private static int chargeTicks;
static {
BrutalflyElytraItem.FLAP_KEY_NAME = () -> AntarchyKeyBindings.BRUTALFLY_FLAP.getTranslatedKeyMessage();
}
private BrutalflyElytraClientHandler() {
}
public static void tick() {
BrutalflyElytraClientState.tick();
Minecraft minecraft = Minecraft.getInstance();
if (minecraft.player == null || minecraft.level == null || minecraft.screen != null) {
reset();
return;
}
if (!BrutalflyElytraItem.isWearingBrutalflyElytra(minecraft.player)) {
reset();
return;
}
if (!minecraft.player.isFallFlying()) {
reset();
return;
}
boolean sneakDown = AntarchyKeyBindings.isBrutalflyFlapPressed();
boolean wasSneakDown = lastJumpDown;
lastJumpDown = sneakDown;
if (sneakDown) {
chargeTicks = Math.min(chargeTicks + 1, BrutalflyElytraFlightHelper.FLAP_CHARGE_TICKS_MAX);
return;
}
if (wasSneakDown && chargeTicks > 0) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("[BrutalflyElytra] Sending flap payload chargeTicks={} fallFlying={} deltaMovement={}", chargeTicks, minecraft.player.isFallFlying(), minecraft.player.getDeltaMovement());
}
ClientPlayNetworking.send(new BrutalflyElytraFlapPayload(chargeTicks));
}
chargeTicks = 0;
}
private static void reset() {
lastJumpDown = false;
chargeTicks = 0;
}
public static boolean isCharging() {
return chargeTicks > 0 && lastJumpDown;
}
public static int getChargeTicks() {
return chargeTicks;
}
}
@@ -0,0 +1,46 @@
package com.craisinlord.antarchy.fabric.client;
import com.craisinlord.antarchy.content.item.BrutalflyElytraFlightHelper;
import com.craisinlord.antarchy.content.item.BrutalflyElytraItem;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.Mth;
import net.minecraft.world.entity.player.Player;
public final class BrutalflyElytraHudRenderer {
private static final ResourceLocation JUMP_BAR_BACKGROUND = ResourceLocation.withDefaultNamespace("textures/gui/sprites/hud/jump_bar_background.png");
private static final ResourceLocation JUMP_BAR_PROGRESS = ResourceLocation.withDefaultNamespace("textures/gui/sprites/hud/jump_bar_progress.png");
private static final int BAR_WIDTH = 182;
private static final int BAR_HEIGHT = 5;
private BrutalflyElytraHudRenderer() {
}
public static void render(GuiGraphics guiGraphics) {
Minecraft minecraft = Minecraft.getInstance();
Player player = minecraft.player;
if (player == null || minecraft.screen != null || player.isDeadOrDying()) {
return;
}
if (!BrutalflyElytraItem.isWearingBrutalflyElytra(player) || !player.isFallFlying()) {
return;
}
int width = minecraft.getWindow().getGuiScaledWidth();
int height = minecraft.getWindow().getGuiScaledHeight();
int x0 = width / 2 - BAR_WIDTH / 2;
int y0 = height - 29;
guiGraphics.blit(JUMP_BAR_BACKGROUND, x0, y0, 0, 0, BAR_WIDTH, BAR_HEIGHT, BAR_WIDTH, BAR_HEIGHT);
int chargeTicks = BrutalflyElytraClientHandler.getChargeTicks();
float chargeRatio = Mth.clamp(chargeTicks / (float) BrutalflyElytraFlightHelper.FLAP_CHARGE_TICKS_MAX, 0.0F, 1.0F);
int filledWidth = Mth.clamp((int) (chargeRatio * BAR_WIDTH), 0, BAR_WIDTH);
if (filledWidth > 0) {
guiGraphics.setColor(1.0F, 1.0F, 1.0F, 0.35F + chargeRatio * 0.65F);
guiGraphics.blit(JUMP_BAR_PROGRESS, x0, y0, 0, 0, filledWidth, BAR_HEIGHT, BAR_WIDTH, BAR_HEIGHT);
guiGraphics.setColor(1.0F, 1.0F, 1.0F, 1.0F);
}
}
}
@@ -0,0 +1,62 @@
package com.craisinlord.antarchy.fabric.client;
import com.craisinlord.antarchy.Antarchy;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import net.minecraft.client.model.geom.ModelLayerLocation;
import net.minecraft.client.model.geom.ModelPart;
import net.minecraft.client.model.geom.PartPose;
import net.minecraft.client.model.geom.builders.CubeListBuilder;
import net.minecraft.client.model.geom.builders.LayerDefinition;
import net.minecraft.client.model.geom.builders.MeshDefinition;
import net.minecraft.client.model.geom.builders.PartDefinition;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.Mth;
public final class BrutalflyElytraModel {
public static final ModelLayerLocation LAYER_LOCATION =
new ModelLayerLocation(ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "brutalfly_elytra"), "main");
private final ModelPart root;
private final ModelPart leftWing;
private final ModelPart rightWing;
public BrutalflyElytraModel(ModelPart root) {
this.root = root;
this.leftWing = root.getChild("left_wing");
this.rightWing = root.getChild("right_wing");
}
public static LayerDefinition createBodyLayer() {
MeshDefinition meshDefinition = new MeshDefinition();
PartDefinition root = meshDefinition.getRoot();
root.addOrReplaceChild(
"left_wing",
CubeListBuilder.create().texOffs(22, 0).addBox(-10.0F, 0.0F, -1.0F, 10.0F, 20.0F, 2.0F),
PartPose.offset(5.0F, 0.0F, -1.5F)
);
root.addOrReplaceChild(
"right_wing",
CubeListBuilder.create().mirror().texOffs(22, 0).addBox(0.0F, 0.0F, -1.0F, 10.0F, 20.0F, 2.0F),
PartPose.offset(-5.0F, 0.0F, -1.5F)
);
return LayerDefinition.create(meshDefinition, 64, 32);
}
public void setupAnimation(float progress, float strength, boolean crouching) {
float wave = Mth.sin(progress * Mth.PI);
float flap = wave * strength;
float basePitch = crouching ? -0.18F : -0.05F;
float wingSpread = 0.55F;
this.leftWing.xRot = basePitch - flap * 1.2F;
this.rightWing.xRot = basePitch - flap * 1.2F;
this.leftWing.yRot = -wingSpread;
this.rightWing.yRot = -wingSpread;
this.leftWing.zRot = -0.35F - flap * 0.7F;
this.rightWing.zRot = -0.35F - flap * 0.7F;
}
public void renderToBuffer(PoseStack poseStack, VertexConsumer consumer, int packedLight, int overlay) {
this.root.render(poseStack, consumer, packedLight, overlay);
}
}
@@ -0,0 +1,42 @@
package com.craisinlord.antarchy.fabric.client;
import com.craisinlord.antarchy.config.AntarchySettings;
import com.craisinlord.antarchy.content.entity.DiamondMinecartEntity;
import com.craisinlord.antarchy.content.network.DiamondMinecartInputPayload;
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking;
import net.minecraft.client.Minecraft;
public final class DiamondMinecartClientHandler {
private DiamondMinecartClientHandler() {
}
private static byte lastFlags = 0;
public static void tick() {
Minecraft mc = Minecraft.getInstance();
if (!AntarchySettings.diamondMinecartEnabled()) return;
if (mc.player == null || mc.level == null) return;
if (!(mc.player.getVehicle() instanceof DiamondMinecartEntity)) return;
if (mc.screen != null) {
if (lastFlags != 0) {
ClientPlayNetworking.send(new DiamondMinecartInputPayload((byte) 0));
lastFlags = 0;
}
return;
}
byte flags = 0;
if (mc.options.keyUp.isDown()) flags |= DiamondMinecartInputPayload.FLAG_FORWARD;
if (mc.options.keyDown.isDown()) flags |= DiamondMinecartInputPayload.FLAG_BACK;
if (mc.options.keyLeft.isDown()) flags |= DiamondMinecartInputPayload.FLAG_LEFT;
if (mc.options.keyRight.isDown()) flags |= DiamondMinecartInputPayload.FLAG_RIGHT;
if (flags != lastFlags) {
ClientPlayNetworking.send(new DiamondMinecartInputPayload(flags));
lastFlags = flags;
}
}
}
@@ -0,0 +1,384 @@
package com.craisinlord.antarchy.fabric.client;
import com.craisinlord.antarchy.config.AntarchySettings;
import com.craisinlord.antarchy.content.AntarchyTags;
import com.craisinlord.antarchy.fabric.AntarchyFabricContent;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.client.gui.screens.DeathScreen;
import net.minecraft.core.Holder;
import net.minecraft.core.HolderSet;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.sounds.SoundSource;
import net.minecraft.util.Mth;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.phys.Vec3;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Optional;
import java.util.Random;
public final class DreadClientHandler {
private static final Holder<net.minecraft.world.effect.MobEffect> DREAD_EFFECT =
BuiltInRegistries.MOB_EFFECT.wrapAsHolder(AntarchyFabricContent.DREAD.get());
private static final int FOOTSTEP_MIN_INTERVAL_TICKS = 10;
private static final int FOOTSTEP_MAX_INTERVAL_TICKS = 22;
private static final int APPARITION_MIN_DURATION_TICKS = 16;
private static final int APPARITION_MAX_DURATION_TICKS = 34;
private static final int LUNGE_MIN_DURATION_TICKS = 10;
private static final int LUNGE_MAX_DURATION_TICKS = 16;
private static int nextSoundTick = 0;
private static int nextApparitionTick = 0;
private static int nextFootstepTick = 0;
private static final List<ApparitionEntry> ACTIVE_APPARITIONS = new ArrayList<>();
private static final Random RANDOM = new Random();
private DreadClientHandler() {
}
private enum ApparitionType {
PERIPHERAL_SILHOUETTE,
CROSSING_SHADOW,
LUNGE_FLASH
}
private static final class ApparitionEntry {
private final ApparitionType type;
private final float scale;
private final int lifetime;
private final int baseColor;
private float xNorm;
private float yNorm;
private float xVelocity;
private float yVelocity;
private int age;
private ApparitionEntry(ApparitionType type, float xNorm, float yNorm, float xVelocity, float yVelocity, float scale, int lifetime, int baseColor) {
this.type = type;
this.xNorm = xNorm;
this.yNorm = yNorm;
this.xVelocity = xVelocity;
this.yVelocity = yVelocity;
this.scale = scale;
this.lifetime = lifetime;
this.baseColor = baseColor;
}
private void tick() {
this.age++;
this.xNorm += this.xVelocity;
this.yNorm += this.yVelocity;
}
private boolean expired() {
return this.age >= this.lifetime;
}
private float alpha() {
float progress = this.lifetime <= 0 ? 1.0F : (float) this.age / (float) this.lifetime;
return Mth.clamp(Mth.sin(progress * (float) Math.PI), 0.0F, 1.0F);
}
}
public static void tick() {
Minecraft mc = Minecraft.getInstance();
if (mc.level == null || mc.player == null) {
resetState();
return;
}
if (mc.isPaused()) {
return;
}
Player player = mc.player;
if (!player.isAlive() || player.isRemoved() || player.deathTime > 0 || player.isDeadOrDying() || mc.screen instanceof DeathScreen) {
resetState();
return;
}
tickApparitions();
if (!player.hasEffect(DREAD_EFFECT)) {
resetState();
return;
}
if (AntarchySettings.dreadHallucinationSoundsEnabled()) {
nextSoundTick--;
if (nextSoundTick <= 0) {
playHallucinationSound(mc, player);
nextSoundTick = randomIntervalTicks(
AntarchySettings.dreadHallucinationSoundMinInterval(),
AntarchySettings.dreadHallucinationSoundMaxInterval()
);
}
} else {
nextSoundTick = 0;
}
nextFootstepTick--;
if (nextFootstepTick <= 0) {
if (player.onGround() && player.getDeltaMovement().horizontalDistanceSqr() > 0.0025D) {
playFootstepSound(mc, player);
nextFootstepTick = randomTickInterval(FOOTSTEP_MIN_INTERVAL_TICKS, FOOTSTEP_MAX_INTERVAL_TICKS);
} else {
nextFootstepTick = 4;
}
}
if (AntarchySettings.dreadHallucinationMobsEnabled()) {
nextApparitionTick--;
if (nextApparitionTick <= 0) {
spawnApparition(mc, player);
nextApparitionTick = randomIntervalTicks(
AntarchySettings.dreadHallucinationMobMinInterval(),
AntarchySettings.dreadHallucinationMobMaxInterval()
);
}
} else {
nextApparitionTick = 0;
ACTIVE_APPARITIONS.clear();
}
}
public static void render(GuiGraphics guiGraphics) {
Minecraft mc = Minecraft.getInstance();
Player player = mc.player;
if (player == null || !player.hasEffect(DREAD_EFFECT) || ACTIVE_APPARITIONS.isEmpty()) {
return;
}
int width = mc.getWindow().getGuiScaledWidth();
int height = mc.getWindow().getGuiScaledHeight();
for (ApparitionEntry entry : ACTIVE_APPARITIONS) {
float alpha = entry.alpha();
if (alpha <= 0.01F) {
continue;
}
switch (entry.type) {
case PERIPHERAL_SILHOUETTE -> renderPeripheralSilhouette(guiGraphics, width, height, entry, alpha);
case CROSSING_SHADOW -> renderCrossingShadow(guiGraphics, width, height, entry, alpha);
case LUNGE_FLASH -> renderLungeFlash(guiGraphics, width, height, entry, alpha);
}
}
}
private static void tickApparitions() {
Iterator<ApparitionEntry> iterator = ACTIVE_APPARITIONS.iterator();
while (iterator.hasNext()) {
ApparitionEntry entry = iterator.next();
entry.tick();
if (entry.expired()) {
iterator.remove();
}
}
}
private static void playHallucinationSound(Minecraft mc, Player player) {
Optional<HolderSet.Named<SoundEvent>> tagOpt =
BuiltInRegistries.SOUND_EVENT.getTag(AntarchyTags.SoundEvents.DREAD_HALLUCINATION_SOUNDS);
if (tagOpt.isEmpty()) {
return;
}
List<Holder<SoundEvent>> sounds = tagOpt.get().stream().toList();
if (sounds.isEmpty()) {
return;
}
SoundEvent soundEvent = sounds.get(RANDOM.nextInt(sounds.size())).value();
Vec3 look = player.getLookAngle();
double baseAngle = Math.atan2(-look.x, -look.z);
double spread = (RANDOM.nextDouble() - 0.5D) * Math.PI;
double angle = baseAngle + spread;
double distance = 10.0D + RANDOM.nextDouble() * 15.0D;
double soundX = player.getX() + Math.sin(angle) * distance;
double soundY = player.getY() + RANDOM.nextDouble() * 4.0D - 1.0D;
double soundZ = player.getZ() + Math.cos(angle) * distance;
mc.level.playLocalSound(
soundX,
soundY,
soundZ,
soundEvent,
SoundSource.HOSTILE,
1.0F,
0.85F + RANDOM.nextFloat() * 0.3F,
false
);
}
private static void spawnApparition(Minecraft mc, Player player) {
double roll = RANDOM.nextDouble();
if (roll < 0.5D) {
spawnPeripheralSilhouette();
return;
}
if (roll < 0.82D) {
spawnCrossingShadow();
return;
}
spawnLungeFlash(mc, player);
}
private static void spawnPeripheralSilhouette() {
boolean leftSide = RANDOM.nextBoolean();
float xNorm = leftSide ? 0.08F + RANDOM.nextFloat() * 0.08F : 0.84F + RANDOM.nextFloat() * 0.08F;
float yNorm = 0.48F + (RANDOM.nextFloat() - 0.5F) * 0.26F;
float scale = 0.85F + RANDOM.nextFloat() * 0.4F;
int lifetime = randomTickInterval(APPARITION_MIN_DURATION_TICKS, APPARITION_MAX_DURATION_TICKS);
ACTIVE_APPARITIONS.add(new ApparitionEntry(
ApparitionType.PERIPHERAL_SILHOUETTE,
xNorm,
yNorm,
leftSide ? -0.0015F : 0.0015F,
0.0F,
scale,
lifetime,
0x160000
));
}
private static void spawnCrossingShadow() {
boolean leftToRight = RANDOM.nextBoolean();
float xNorm = leftToRight ? -0.18F : 1.18F;
float yNorm = 0.38F + RANDOM.nextFloat() * 0.36F;
float velocity = leftToRight ? 0.04F + RANDOM.nextFloat() * 0.015F : -(0.04F + RANDOM.nextFloat() * 0.015F);
float scale = 0.9F + RANDOM.nextFloat() * 0.45F;
int lifetime = randomTickInterval(APPARITION_MIN_DURATION_TICKS, APPARITION_MAX_DURATION_TICKS);
ACTIVE_APPARITIONS.add(new ApparitionEntry(
ApparitionType.CROSSING_SHADOW,
xNorm,
yNorm,
velocity,
0.0F,
scale,
lifetime,
0x1A0000
));
}
private static void spawnLungeFlash(Minecraft mc, Player player) {
float xNorm = 0.5F + (RANDOM.nextBoolean() ? -1.0F : 1.0F) * (0.12F + RANDOM.nextFloat() * 0.12F);
float yNorm = 0.58F + (RANDOM.nextFloat() - 0.5F) * 0.12F;
float scale = 0.95F + RANDOM.nextFloat() * 0.35F;
int lifetime = randomTickInterval(LUNGE_MIN_DURATION_TICKS, LUNGE_MAX_DURATION_TICKS);
ACTIVE_APPARITIONS.add(new ApparitionEntry(
ApparitionType.LUNGE_FLASH,
xNorm,
yNorm,
0.0F,
-0.002F,
scale,
lifetime,
0x2A0000
));
mc.level.playLocalSound(
player.getX(),
player.getY() + 1.0D,
player.getZ(),
AntarchyFabricContent.NIGHTMARE_BITE.get(),
SoundSource.HOSTILE,
0.4F,
0.88F + RANDOM.nextFloat() * 0.12F,
false
);
}
private static void playFootstepSound(Minecraft mc, Player player) {
double soundX = player.getX() + (RANDOM.nextDouble() - 0.5D) * 1.2D;
double soundY = player.getY() - 0.95D;
double soundZ = player.getZ() + (RANDOM.nextDouble() - 0.5D) * 1.2D;
mc.level.playLocalSound(
soundX,
soundY,
soundZ,
SoundEvents.SILVERFISH_STEP,
SoundSource.HOSTILE,
0.45F,
0.85F + RANDOM.nextFloat() * 0.25F,
false
);
}
private static void renderPeripheralSilhouette(GuiGraphics guiGraphics, int width, int height, ApparitionEntry entry, float alpha) {
int centerX = Math.round(entry.xNorm * width);
int baseY = Math.round(entry.yNorm * height);
int bodyWidth = Math.round(width * 0.045F * entry.scale);
int bodyHeight = Math.round(height * 0.24F * entry.scale);
int headSize = Math.round(bodyWidth * 0.78F);
int x0 = centerX - bodyWidth / 2;
int y0 = baseY - bodyHeight / 2;
int outerColor = alphaColor(entry.baseColor, alpha * 0.8F);
int innerColor = alphaColor(0x5A0000, alpha * 0.22F);
guiGraphics.fill(x0, y0, x0 + bodyWidth, y0 + bodyHeight, outerColor);
guiGraphics.fill(x0 - headSize / 2 + bodyWidth / 2, y0 - headSize / 2, x0 + bodyWidth / 2 + headSize / 2, y0 + headSize / 2, outerColor);
guiGraphics.fill(x0 + bodyWidth / 6, y0 + bodyHeight / 6, x0 + bodyWidth - bodyWidth / 6, y0 + bodyHeight - bodyHeight / 7, innerColor);
}
private static void renderCrossingShadow(GuiGraphics guiGraphics, int width, int height, ApparitionEntry entry, float alpha) {
int centerX = Math.round(entry.xNorm * width);
int centerY = Math.round(entry.yNorm * height);
int shadowWidth = Math.round(width * 0.22F * entry.scale);
int shadowHeight = Math.round(height * 0.035F * entry.scale);
int outerColor = alphaColor(entry.baseColor, alpha * 0.65F);
int innerColor = alphaColor(0x340000, alpha * 0.28F);
guiGraphics.fill(centerX - shadowWidth / 2, centerY - shadowHeight / 2, centerX + shadowWidth / 2, centerY + shadowHeight / 2, outerColor);
guiGraphics.fill(centerX - shadowWidth / 3, centerY - shadowHeight, centerX + shadowWidth / 3, centerY + shadowHeight, innerColor);
}
private static void renderLungeFlash(GuiGraphics guiGraphics, int width, int height, ApparitionEntry entry, float alpha) {
int centerX = Math.round(entry.xNorm * width);
int centerY = Math.round(entry.yNorm * height);
int faceWidth = Math.round(width * 0.11F * entry.scale);
int faceHeight = Math.round(height * 0.12F * entry.scale);
int shadowColor = alphaColor(entry.baseColor, alpha * 0.82F);
int glowColor = alphaColor(0x4A0000, alpha * 0.25F);
int eyeColor = alphaColor(0xB02020, alpha * 0.8F);
guiGraphics.fill(centerX - faceWidth / 2, centerY - faceHeight / 2, centerX + faceWidth / 2, centerY + faceHeight / 2, shadowColor);
guiGraphics.fill(centerX - faceWidth, centerY - faceHeight / 3, centerX + faceWidth, centerY + faceHeight, glowColor);
int eyeWidth = Math.max(2, faceWidth / 7);
int eyeHeight = Math.max(2, faceHeight / 12);
int eyeY = centerY - faceHeight / 8;
int eyeOffset = faceWidth / 4;
guiGraphics.fill(centerX - eyeOffset - eyeWidth, eyeY, centerX - eyeOffset + eyeWidth, eyeY + eyeHeight, eyeColor);
guiGraphics.fill(centerX + eyeOffset - eyeWidth, eyeY, centerX + eyeOffset + eyeWidth, eyeY + eyeHeight, eyeColor);
}
private static void resetState() {
ACTIVE_APPARITIONS.clear();
nextSoundTick = 0;
nextApparitionTick = 0;
nextFootstepTick = 0;
}
private static int randomIntervalTicks(double minSeconds, double maxSeconds) {
double seconds = minSeconds + RANDOM.nextDouble() * Math.max(0.0D, maxSeconds - minSeconds);
return Math.max(1, (int) (seconds * 20.0D));
}
private static int randomTickInterval(int minTicks, int maxTicks) {
return minTicks + RANDOM.nextInt(Math.max(1, maxTicks - minTicks + 1));
}
private static int alphaColor(int rgb, float alpha) {
int alphaByte = Mth.clamp((int) (alpha * 255.0F), 0, 255);
return (alphaByte << 24) | rgb;
}
}
@@ -0,0 +1,33 @@
package com.craisinlord.antarchy.fabric.client;
import com.craisinlord.antarchy.fabric.AntarchyFabricContent;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.core.Holder;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.util.Mth;
import net.minecraft.world.effect.MobEffect;
import net.minecraft.world.entity.player.Player;
public final class DreadHudRenderer {
private static final Holder<MobEffect> DREAD_EFFECT = BuiltInRegistries.MOB_EFFECT.wrapAsHolder(AntarchyFabricContent.DREAD.get());
private DreadHudRenderer() {
}
public static void render(GuiGraphics guiGraphics) {
Minecraft minecraft = Minecraft.getInstance();
Player player = minecraft.player;
if (player == null || minecraft.screen != null || player.isDeadOrDying() || !player.hasEffect(DREAD_EFFECT)) {
return;
}
float pulse = Math.max(0.0F, Mth.sin(player.tickCount * ((float) Math.PI * 2.0F / 80.0F)));
int alpha = Mth.clamp(18 + Mth.floor(pulse * 64.0F), 0, 255);
int color = (alpha << 24) | 0x120000;
int width = minecraft.getWindow().getGuiScaledWidth();
int height = minecraft.getWindow().getGuiScaledHeight();
guiGraphics.fill(0, 0, width, height, color);
DreadClientHandler.render(guiGraphics);
}
}
@@ -0,0 +1,82 @@
package com.craisinlord.antarchy.fabric.client;
import com.craisinlord.antarchy.config.AntarchySettings;
import com.craisinlord.antarchy.content.AntarchySoundEvents;
import com.craisinlord.antarchy.content.client.renderer.ElythiaSkyRenderer;
import com.craisinlord.antarchy.fabric.AntarchyFabricContent;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.core.BlockPos;
import net.minecraft.sounds.SoundSource;
import net.minecraft.world.entity.player.Player;
public final class ElythiaFireflyManager {
private static final long NIGHT_START = 13000L;
private static final long NIGHT_END = 23000L;
private static final int SPAWN_INTERVAL = 3;
private static final int SPAWN_RADIUS_H = 20;
private static final int SPAWN_Y_MIN = -2;
private static final int SPAWN_Y_MAX = 10;
private static final int SOUND_INTERVAL_MIN = 250;
private static final int SOUND_INTERVAL_MAX = 450;
private static int nextSoundTick = SOUND_INTERVAL_MIN;
private ElythiaFireflyManager() {}
public static void register() {
ClientTickEvents.END_CLIENT_TICK.register(client -> {
ClientLevel level = client.level;
if (level == null || client.player == null) return;
if (client.isPaused()) return;
if (!ElythiaSkyRenderer.shouldRender(level)) return;
long dayTime = level.getDayTime() % 24000L;
if (dayTime < NIGHT_START || dayTime > NIGHT_END) return;
long gameTime = level.getGameTime();
if (AntarchySettings.elythiaFireflyParticlesEnabled()) {
if (gameTime % SPAWN_INTERVAL == 0) {
Player player = client.player;
int px = (int) player.getX();
int py = (int) player.getY();
int pz = (int) player.getZ();
int attempts = 1 + level.random.nextInt(3);
for (int i = 0; i < attempts; i++) {
int ox = level.random.nextInt(SPAWN_RADIUS_H * 2 + 1) - SPAWN_RADIUS_H;
int oy = SPAWN_Y_MIN + level.random.nextInt(SPAWN_Y_MAX - SPAWN_Y_MIN + 1);
int oz = level.random.nextInt(SPAWN_RADIUS_H * 2 + 1) - SPAWN_RADIUS_H;
BlockPos candidate = new BlockPos(px + ox, py + oy, pz + oz);
if (!level.getBlockState(candidate).isAir()) continue;
level.addParticle(
AntarchyFabricContent.FIREFLY.get(),
candidate.getX() + level.random.nextDouble(),
candidate.getY() + level.random.nextDouble(),
candidate.getZ() + level.random.nextDouble(),
0.0, 0.0, 0.0
);
}
}
}
nextSoundTick--;
if (nextSoundTick <= 0) {
nextSoundTick = SOUND_INTERVAL_MIN + level.random.nextInt(SOUND_INTERVAL_MAX - SOUND_INTERVAL_MIN + 1);
Player player = client.player;
level.playLocalSound(
player.getX(), player.getY(), player.getZ(),
AntarchySoundEvents.ELYTHIA_FIREFLY_AMBIENT.get(),
SoundSource.AMBIENT,
0.6f + level.random.nextFloat() * 0.3f,
0.9f + level.random.nextFloat() * 0.2f,
false
);
}
});
}
}
@@ -0,0 +1,61 @@
package com.craisinlord.antarchy.fabric.client;
import com.craisinlord.antarchy.fabric.AntarchyFabricContent;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.entity.ItemRenderer;
import net.minecraft.client.model.PlayerModel;
import net.minecraft.client.player.AbstractClientPlayer;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.entity.LivingEntityRenderer;
import net.minecraft.client.renderer.entity.RenderLayerParent;
import net.minecraft.client.renderer.entity.layers.RenderLayer;
import net.minecraft.client.resources.model.ModelResourceLocation;
import net.minecraft.world.entity.EquipmentSlot;
import net.minecraft.world.item.ItemDisplayContext;
import net.minecraft.world.item.ItemStack;
public final class FallenKingCrownLayer extends RenderLayer<AbstractClientPlayer, PlayerModel<AbstractClientPlayer>> {
private static final ModelResourceLocation WORN_MODEL = ModelResourceLocation.inventory(
net.minecraft.resources.ResourceLocation.fromNamespaceAndPath("antarchy", "fallen_king_crown_worn")
);
private final ItemRenderer itemRenderer;
public FallenKingCrownLayer(RenderLayerParent<AbstractClientPlayer, PlayerModel<AbstractClientPlayer>> renderer) {
super(renderer);
this.itemRenderer = Minecraft.getInstance().getItemRenderer();
}
@Override
public void render(
PoseStack poseStack,
MultiBufferSource buffer,
int packedLight,
AbstractClientPlayer player,
float limbSwing,
float limbSwingAmount,
float partialTick,
float ageInTicks,
float netHeadYaw,
float headPitch
) {
ItemStack headStack = player.getItemBySlot(EquipmentSlot.HEAD);
if (!headStack.is(AntarchyFabricContent.FALLEN_KING_CROWN.get()) || player.isInvisible()) {
return;
}
poseStack.pushPose();
this.getParentModel().head.translateAndRotate(poseStack);
this.itemRenderer.render(
headStack,
ItemDisplayContext.HEAD,
false,
poseStack,
buffer,
packedLight,
LivingEntityRenderer.getOverlayCoords(player, 0.0F),
Minecraft.getInstance().getModelManager().getModel(WORN_MODEL)
);
poseStack.popPose();
}
}
@@ -0,0 +1,63 @@
package com.craisinlord.antarchy.fabric.client;
import com.craisinlord.antarchy.config.AntarchySettings;
import com.craisinlord.antarchy.content.item.GravityGunItem;
import com.craisinlord.antarchy.content.network.GravityGunPrimaryPayload;
import com.craisinlord.antarchy.content.network.GravityGunScrollPayload;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.util.Mth;
import org.lwjgl.glfw.GLFW;
public final class GravityGunClientHandler {
private static final double SCROLL_STEP_DISTANCE = 0.75D;
private static boolean lastAttackDown;
private static double scrollAccumulator;
private GravityGunClientHandler() {
}
public static void register() {
ClientTickEvents.END_CLIENT_TICK.register(client -> tickMouse(client));
}
private static void tickMouse(Minecraft mc) {
if (mc.player == null || mc.level == null || mc.screen != null || !AntarchySettings.gravityGunEnabled()) {
lastAttackDown = false;
return;
}
if (!(mc.player.getMainHandItem().getItem() instanceof GravityGunItem)) {
lastAttackDown = false;
return;
}
long window = mc.getWindow().getWindow();
boolean attackDown = org.lwjgl.glfw.GLFW.glfwGetMouseButton(window, GLFW.GLFW_MOUSE_BUTTON_LEFT) == GLFW.GLFW_PRESS;
if (attackDown && !lastAttackDown) {
ClientPlayNetworking.send(new GravityGunPrimaryPayload());
}
lastAttackDown = attackDown;
}
public static boolean onScroll(double verticalAmount) {
Minecraft mc = Minecraft.getInstance();
if (mc.player == null || mc.level == null || mc.screen != null || !Screen.hasControlDown() || !AntarchySettings.gravityGunEnabled()) {
return false;
}
if (!(mc.player.getMainHandItem().getItem() instanceof GravityGunItem) || !GravityGunItem.hasHeldTarget(mc.player.getMainHandItem())) {
return false;
}
double scrollAmount = Mth.clamp(verticalAmount, -1.0D, 1.0D);
if (Math.abs(scrollAmount) < 1.0E-4D) {
return false;
}
ClientPlayNetworking.send(new GravityGunScrollPayload(scrollAmount * SCROLL_STEP_DISTANCE));
return true;
}
}
@@ -0,0 +1,78 @@
package com.craisinlord.antarchy.fabric.client;
import com.craisinlord.antarchy.content.client.particle.InvertedGeyserBaseParticleOptions;
import com.craisinlord.antarchy.content.client.particle.InvertedGeyserParticleOptions;
import com.craisinlord.antarchy.fabric.AntarchyFabricContent;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.particle.NoRenderParticle;
import net.minecraft.client.particle.Particle;
import net.minecraft.client.particle.ParticleProvider;
public final class InvertedGeyserEruptionParticle extends NoRenderParticle {
private final int fluidBlocks;
private final double motionX;
private final double motionY;
private final double motionZ;
private final InvertedGeyserParticleOptions plumeParticle;
private final InvertedGeyserBaseParticleOptions baseParticle;
private final InvertedGeyserBaseParticleOptions poofParticle;
protected InvertedGeyserEruptionParticle(
ClientLevel level,
double x,
double y,
double z,
double xd,
double yd,
double zd,
InvertedGeyserParticleOptions options
) {
super(level, x, y, z);
this.motionX = xd;
this.motionY = yd;
this.motionZ = zd;
this.fluidBlocks = options.fluidBlocks();
this.lifetime = 20;
this.plumeParticle = new InvertedGeyserParticleOptions(AntarchyFabricContent.INVERTED_GEYSER_PLUME.get(), this.fluidBlocks, options.direction());
this.baseParticle = new InvertedGeyserBaseParticleOptions(AntarchyFabricContent.INVERTED_GEYSER_BASE.get(), this.fluidBlocks, 1.5F, options.direction());
this.poofParticle = new InvertedGeyserBaseParticleOptions(AntarchyFabricContent.INVERTED_GEYSER_POOF.get(), this.fluidBlocks, 2.0F, options.direction());
}
@Override
public void tick() {
super.tick();
if (this.age % 2 == 0) {
for (int i = 0; i < 2; i++) {
this.level.addParticle(this.baseParticle, this.x, this.y, this.z, this.motionX, this.motionY, this.motionZ);
}
}
for (int i = 0; i < this.fluidBlocks + 2; i++) {
this.level.addParticle(this.plumeParticle, this.x, this.y, this.z, this.motionX, this.motionY, this.motionZ);
}
if (this.age % 10 == 0) {
for (int i = 0; i < 20; i++) {
this.level.addParticle(this.poofParticle, this.x, this.y, this.z, this.motionX, this.motionY, this.motionZ);
}
}
}
public static final class Provider implements ParticleProvider<InvertedGeyserParticleOptions> {
@Override
public Particle createParticle(
InvertedGeyserParticleOptions options,
ClientLevel level,
double x,
double y,
double z,
double xd,
double yd,
double zd
) {
return new InvertedGeyserEruptionParticle(level, x, y, z, xd, yd, zd, options);
}
}
}
@@ -0,0 +1,226 @@
package com.craisinlord.antarchy.fabric.client;
import com.craisinlord.antarchy.content.entity.lucid.LucidEntity;
import com.craisinlord.antarchy.fabric.AntarchyFabricContent;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
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.RandomSource;
import org.jetbrains.annotations.Nullable;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.UUID;
public final class LucidSoundHandler {
private static final double START_SCAN_RADIUS = 96.0D;
private static final int ANIM_ATTACK = 2;
private static final int ANIM_DEATH = 3;
private static final Map<UUID, LucidLoopSet> ACTIVE_LOOPS = new HashMap<>();
private LucidSoundHandler() {
}
public static void register() {
ClientTickEvents.END_CLIENT_TICK.register(client -> {
if (client.level == null || client.player == null || client.isPaused()) {
resetAll(client);
return;
}
if (!client.player.isAlive() || client.player.isRemoved() || client.player.isDeadOrDying()
|| client.screen instanceof DeathScreen) {
resetAll(client);
return;
}
pruneStaleLoops(client);
refreshNearbyLucids(client);
});
}
private static void refreshNearbyLucids(Minecraft mc) {
List<LucidEntity> lucids = mc.level.getEntitiesOfClass(
LucidEntity.class,
mc.player.getBoundingBox().inflate(START_SCAN_RADIUS)
);
for (LucidEntity lucid : lucids) {
if (!lucid.shouldPlayAmbientLoop()) continue;
LucidLoopSet set = ACTIVE_LOOPS.get(lucid.getUUID());
if (set == null) {
set = new LucidLoopSet(lucid);
ACTIVE_LOOPS.put(lucid.getUUID(), set);
set.start(mc);
} else {
set.refresh(mc, lucid);
}
}
}
private static void pruneStaleLoops(Minecraft mc) {
Iterator<Map.Entry<UUID, LucidLoopSet>> iterator = ACTIVE_LOOPS.entrySet().iterator();
while (iterator.hasNext()) {
LucidLoopSet set = iterator.next().getValue();
if (!set.isValid()) {
set.stop(mc);
iterator.remove();
} else if (!set.entity.shouldPlayAmbientLoop()) {
set.stop(mc);
iterator.remove();
}
}
}
private static void resetAll(Minecraft mc) {
for (LucidLoopSet set : ACTIVE_LOOPS.values()) {
set.stop(mc);
}
ACTIVE_LOOPS.clear();
}
private static final class LucidLoopSet {
private LucidEntity entity;
@Nullable private LucidLoopSound ambient;
@Nullable private LucidLoopSound flying;
private int lastAnimationState = -1;
private LucidLoopSet(LucidEntity entity) {
this.entity = entity;
}
private boolean isValid() {
return entity != null && entity.isAlive() && !entity.isRemoved();
}
private void start(Minecraft mc) {
playAttackSoundIfNeeded(mc, entity);
if (ambient == null || !mc.getSoundManager().isActive(ambient)) {
ambient = new LucidLoopSound(entity, AntarchyFabricContent.LUCID_AMBIENT.get(),
SoundSource.HOSTILE, 0.14F, 0.97F, true);
mc.getSoundManager().play(ambient);
}
if (entity.shouldPlayFlyingLoop() && (flying == null || !mc.getSoundManager().isActive(flying))) {
flying = new LucidLoopSound(entity, AntarchyFabricContent.LUCID_FLYING.get(),
SoundSource.HOSTILE, 0.18F, 1.0F, false);
mc.getSoundManager().play(flying);
}
}
private void refresh(Minecraft mc, LucidEntity entity) {
this.entity = entity;
playAttackSoundIfNeeded(mc, entity);
if (ambient == null || !mc.getSoundManager().isActive(ambient)) {
ambient = new LucidLoopSound(entity, AntarchyFabricContent.LUCID_AMBIENT.get(),
SoundSource.HOSTILE, 0.14F, 0.97F, true);
mc.getSoundManager().play(ambient);
} else {
ambient.setEntity(entity);
}
if (entity.shouldPlayFlyingLoop()) {
if (flying == null || !mc.getSoundManager().isActive(flying)) {
flying = new LucidLoopSound(entity, AntarchyFabricContent.LUCID_FLYING.get(),
SoundSource.HOSTILE, 0.18F, 1.0F, false);
mc.getSoundManager().play(flying);
} else {
flying.setEntity(entity);
}
} else if (flying != null) {
mc.getSoundManager().stop(flying);
flying = null;
}
}
private void stop(Minecraft mc) {
if (ambient != null) { mc.getSoundManager().stop(ambient); ambient = null; }
if (flying != null) { mc.getSoundManager().stop(flying); flying = null; }
lastAnimationState = -1;
}
private void playAttackSoundIfNeeded(Minecraft mc, LucidEntity entity) {
int currentState = entity.getAnimationState();
if (currentState == ANIM_ATTACK && lastAnimationState != ANIM_ATTACK) {
mc.getSoundManager().play(new LucidOneShotSound(entity, AntarchyFabricContent.LUCID_ATTACK.get(),
SoundSource.HOSTILE, 1.0F, 1.02F + entity.getRandom().nextFloat() * 0.08F));
}
lastAnimationState = currentState;
}
}
private static final class LucidLoopSound extends AbstractTickableSoundInstance {
private LucidEntity entity;
private final boolean ambient;
private LucidLoopSound(LucidEntity entity, SoundEvent soundEvent, SoundSource source,
float volume, float pitch, boolean ambient) {
super(soundEvent, source, RandomSource.create());
this.entity = entity;
this.ambient = ambient;
this.looping = true;
this.delay = 0;
this.relative = false;
this.attenuation = SoundInstance.Attenuation.LINEAR;
this.volume = volume;
this.pitch = pitch;
this.x = entity.getX();
this.y = entity.getY();
this.z = entity.getZ();
}
private void setEntity(LucidEntity entity) { this.entity = entity; }
@Override
public void tick() {
if (entity == null || entity.isRemoved() || entity.isDeadOrDying()
|| entity.getAnimationState() == ANIM_DEATH || !entity.shouldPlayAmbientLoop()) {
stop();
return;
}
if (!ambient && !entity.shouldPlayFlyingLoop()) { stop(); return; }
x = entity.getX(); y = entity.getY(); z = entity.getZ();
}
@Override
public boolean canStartSilent() { return true; }
}
private static final class LucidOneShotSound extends AbstractTickableSoundInstance {
private final LucidEntity entity;
private LucidOneShotSound(LucidEntity entity, SoundEvent soundEvent, SoundSource source,
float volume, float pitch) {
super(soundEvent, source, RandomSource.create());
this.entity = entity;
this.looping = false;
this.delay = 0;
this.relative = false;
this.attenuation = SoundInstance.Attenuation.LINEAR;
this.volume = volume;
this.pitch = pitch;
this.x = entity.getX();
this.y = entity.getY();
this.z = entity.getZ();
}
@Override
public void tick() {
if (entity == null || entity.isRemoved() || entity.isDeadOrDying()
|| entity.getAnimationState() == ANIM_DEATH) {
stop();
return;
}
x = entity.getX(); y = entity.getY(); z = entity.getZ();
}
}
}
@@ -0,0 +1,54 @@
package com.craisinlord.antarchy.fabric.client;
import com.craisinlord.antarchy.content.item.MogglesCaveDetector;
import com.craisinlord.antarchy.content.item.MogglesLogic;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
import net.minecraft.client.Minecraft;
import net.minecraft.core.BlockPos;
import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.Vec3;
public final class MogglesClientRenderer {
private MogglesClientRenderer() {}
public static void register() {
ClientTickEvents.END_CLIENT_TICK.register(client -> {
Player player = client.player;
Level level = client.level;
if (player == null || level == null) return;
if (!MogglesLogic.isWearingMoggles(player) || level.canSeeSky(player.blockPosition())) {
MogglesCaveDetector.clear();
return;
}
MogglesCaveDetector.tick(player, level);
BlockPos target = MogglesCaveDetector.getCaveTarget();
if (target == null) return;
if (level.getGameTime() % 2 != 0) return;
Vec3 eye = player.getEyePosition();
Vec3 end = Vec3.atCenterOf(target);
double totalDist = eye.distanceTo(end);
if (totalDist < 1.5) return;
Vec3 dir = end.subtract(eye).normalize();
int steps = Math.min(5, (int) (totalDist / 1.5));
double spacing = (totalDist - 1.5) / Math.max(steps, 1);
for (int i = 0; i < steps; i++) {
double t = 1.5 + i * spacing;
Vec3 p = eye.add(dir.scale(t));
Vec3 vel = dir.scale(0.04);
level.addParticle(ParticleTypes.END_ROD,
p.x, p.y, p.z,
vel.x, vel.y, vel.z);
}
});
}
}
@@ -0,0 +1,89 @@
package com.craisinlord.antarchy.fabric.client;
import com.craisinlord.antarchy.fabric.AntarchyFabricContent;
import net.minecraft.core.Holder;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.player.LocalPlayer;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.core.particles.BlockParticleOption;
import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.state.BlockState;
public final class ParalyzedClientHandler {
private static final Holder<net.minecraft.world.effect.MobEffect> PARALYZED_EFFECT =
BuiltInRegistries.MOB_EFFECT.wrapAsHolder(AntarchyFabricContent.PARALYZED.get());
private static final int PARTICLE_INTERVAL = 3;
private static final int PARTICLES_PER_ENTITY = 4;
private static final double PARTICLE_RADIUS = 24.0;
private ParalyzedClientHandler() {
}
public static void clampPlayerInput(LocalPlayer player) {
if (!player.hasEffect(PARALYZED_EFFECT)) {
return;
}
var input = player.input;
input.left = false;
input.right = false;
input.up = false;
input.down = false;
input.leftImpulse = 0.0F;
input.forwardImpulse = 0.0F;
input.jumping = false;
}
public static void tick() {
Minecraft mc = Minecraft.getInstance();
if (mc.player == null || mc.level == null) {
return;
}
LocalPlayer player = mc.player;
if (player.hasEffect(PARALYZED_EFFECT)) {
while (mc.options.keyAttack.consumeClick()) { }
while (mc.options.keyUse.consumeClick()) { }
}
if (player.tickCount % PARTICLE_INTERVAL != 0) {
return;
}
ClientLevel level = mc.level;
level.getEntitiesOfClass(
LivingEntity.class,
player.getBoundingBox().inflate(PARTICLE_RADIUS),
entity -> entity != player
&& entity.isAlive()
&& entity.hasEffect(PARALYZED_EFFECT)
).forEach(entity -> spawnStoneParticles(level, entity));
}
private static void spawnStoneParticles(ClientLevel level, LivingEntity entity) {
BlockState stone = Blocks.STONE.defaultBlockState();
BlockParticleOption particle = new BlockParticleOption(ParticleTypes.BLOCK, stone);
float w = entity.getBbWidth();
float h = entity.getBbHeight();
for (int i = 0; i < PARTICLES_PER_ENTITY; i++) {
double px = entity.getX() + (Math.random() - 0.5) * w;
double py = entity.getY() + Math.random() * h;
double pz = entity.getZ() + (Math.random() - 0.5) * w;
double vx = (Math.random() - 0.5) * 0.08;
double vy = Math.random() * 0.06;
double vz = (Math.random() - 0.5) * 0.08;
level.addParticle(particle, px, py, pz, vx, vy, vz);
}
}
}
@@ -0,0 +1,48 @@
package com.craisinlord.antarchy.fabric.client;
import com.craisinlord.antarchy.fabric.AntarchyFabricContent;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.core.Holder;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.util.Mth;
import net.minecraft.world.effect.MobEffect;
import net.minecraft.world.effect.MobEffectInstance;
import net.minecraft.world.entity.player.Player;
public final class ParalyzedHudRenderer {
private static final Holder<MobEffect> PARALYZED_EFFECT = BuiltInRegistries.MOB_EFFECT.wrapAsHolder(AntarchyFabricContent.PARALYZED.get());
private ParalyzedHudRenderer() {
}
public static void render(GuiGraphics guiGraphics) {
Minecraft mc = Minecraft.getInstance();
Player player = mc.player;
if (player == null || player.isDeadOrDying()) {
return;
}
MobEffectInstance effect = player.getEffect(PARALYZED_EFFECT);
if (effect == null) {
return;
}
int duration = effect.getDuration();
float fade = effect.isInfiniteDuration()
? 1.0F
: Math.min(1.0F, Math.max(duration, 20) / 20.0F);
float pulse = 0.85F + 0.15F * Mth.sin(player.tickCount * ((float) Math.PI * 2.0F / 120.0F));
int alpha = Mth.clamp((int) (fade * pulse * 0xC0), 0, 0xFF);
int width = mc.getWindow().getGuiScaledWidth();
int height = mc.getWindow().getGuiScaledHeight();
int color = (alpha << 24) | 0x303030;
guiGraphics.fill(0, 0, width, height, color);
int innerAlpha = Mth.clamp(alpha / 3, 0, 0xFF);
int innerColor = (innerAlpha << 24) | 0x606060;
int margin = Math.min(width, height) / 6;
guiGraphics.fill(margin, margin, width - margin, height - margin, innerColor);
}
}
@@ -0,0 +1,254 @@
package com.craisinlord.antarchy.fabric.client;
import com.craisinlord.antarchy.content.entity.ReverieEntity;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.BufferBuilder;
import com.mojang.blaze3d.vertex.BufferUploader;
import com.mojang.blaze3d.vertex.DefaultVertexFormat;
import com.mojang.blaze3d.vertex.MeshData;
import com.mojang.blaze3d.vertex.Tesselator;
import com.mojang.blaze3d.vertex.VertexFormat;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents;
import net.minecraft.client.Camera;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.GameRenderer;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.phys.Vec3;
import org.joml.Matrix4f;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
public final class ReverieTrailHandler {
private static final int MAX_TRAIL_POINTS = 20;
private static final int IDLE_LIFETIME_TICKS = 8;
private static final int NORMAL_LIFETIME_TICKS = 14;
private static final int DASH_LIFETIME_TICKS = 22;
private static final float CORE_LINE_WIDTH = 0.035f;
private static final float GLOW_LINE_WIDTH = 0.10f;
private static final double MIN_DISTANCE_BETWEEN_POINTS = 0.05;
private static final double DASH_DISTANCE_THRESHOLD = 0.35;
private static final Map<Integer, ArrayDeque<TrailPoint>> TRAILS = new HashMap<>();
private ReverieTrailHandler() {}
private static final class TrailPoint {
final Vec3 pos;
final ReverieEntity.Mood mood;
int age;
final int lifetime;
TrailPoint(Vec3 pos, ReverieEntity.Mood mood, int lifetime) {
this.pos = pos;
this.mood = mood;
this.age = 0;
this.lifetime = lifetime;
}
float progress() { return lifetime <= 0 ? 1f : (float) age / (float) lifetime; }
boolean expired() { return age >= lifetime; }
}
public static void register() {
ClientTickEvents.END_CLIENT_TICK.register(client -> {
if (client.level == null) { TRAILS.clear(); return; }
if (client.isPaused()) return;
Set<Integer> activeIds = new HashSet<>();
for (Entity entity : client.level.entitiesForRendering()) {
if (!(entity instanceof ReverieEntity reverie)) continue;
int id = reverie.getId();
activeIds.add(id);
ReverieEntity.Mood mood = reverie.getMood();
Vec3 pos = reverie.position().add(0.0, reverie.getBbHeight() * 0.5, 0.0);
ArrayDeque<TrailPoint> points = TRAILS.computeIfAbsent(id, k -> new ArrayDeque<>());
if (points.isEmpty()) {
points.addFirst(new TrailPoint(pos, mood, lifetimeFor(mood, false)));
} else {
Vec3 lastPos = points.peekFirst().pos;
double distSq = pos.distanceToSqr(lastPos);
if (distSq >= MIN_DISTANCE_BETWEEN_POINTS * MIN_DISTANCE_BETWEEN_POINTS) {
boolean isDash = distSq >= DASH_DISTANCE_THRESHOLD * DASH_DISTANCE_THRESHOLD;
points.addFirst(new TrailPoint(pos, mood, lifetimeFor(mood, isDash)));
while (points.size() > MAX_TRAIL_POINTS) points.pollLast();
}
}
Iterator<TrailPoint> it = points.iterator();
while (it.hasNext()) {
TrailPoint p = it.next();
p.age++;
if (p.expired()) it.remove();
}
}
TRAILS.keySet().removeIf(id -> !activeIds.contains(id));
});
WorldRenderEvents.AFTER_TRANSLUCENT.register(context -> {
if (TRAILS.isEmpty()) return;
Minecraft mc = Minecraft.getInstance();
if (mc.level == null) return;
Camera camera = context.camera();
Vec3 camPos = camera.getPosition();
Matrix4f mat = context.matrixStack().last().pose();
RenderSystem.enableBlend();
RenderSystem.defaultBlendFunc();
RenderSystem.depthMask(false);
RenderSystem.disableCull();
RenderSystem.setShader(GameRenderer::getPositionColorShader);
Tesselator tesselator = Tesselator.getInstance();
List<List<TrailPoint>> snapshots = new ArrayList<>(TRAILS.size());
for (ArrayDeque<TrailPoint> deque : TRAILS.values()) {
if (deque.size() >= 2) snapshots.add(new ArrayList<>(deque));
}
if (!snapshots.isEmpty()) {
BufferBuilder glowBuf = tesselator.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_COLOR);
for (List<TrailPoint> points : snapshots) buildPass(glowBuf, mat, points, camPos, true);
MeshData glowMesh = glowBuf.build();
if (glowMesh != null) BufferUploader.drawWithShader(glowMesh);
BufferBuilder coreBuf = tesselator.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_COLOR);
for (List<TrailPoint> points : snapshots) buildPass(coreBuf, mat, points, camPos, false);
MeshData coreMesh = coreBuf.build();
if (coreMesh != null) BufferUploader.drawWithShader(coreMesh);
}
RenderSystem.enableCull();
RenderSystem.depthMask(true);
RenderSystem.disableBlend();
});
}
private static int lifetimeFor(ReverieEntity.Mood mood, boolean isDash) {
if (isDash || mood == ReverieEntity.Mood.PURPLE) return DASH_LIFETIME_TICKS;
if (mood == ReverieEntity.Mood.WHITE) return IDLE_LIFETIME_TICKS;
return NORMAL_LIFETIME_TICKS;
}
private static void buildPass(BufferBuilder buf, Matrix4f mat, List<TrailPoint> points, Vec3 camPos, boolean glow) {
int n = points.size();
for (int i = 0; i < n - 1; i++) {
TrailPoint newer = points.get(i);
TrailPoint older = points.get(i + 1);
float alphaNearer = (glow ? glowAlpha(newer.mood) : coreAlpha(newer.mood)) * indexAlpha(i, n) * (1f - newer.progress());
float alphaFarther = (glow ? glowAlpha(older.mood) : coreAlpha(older.mood)) * indexAlpha(i + 1, n) * (1f - older.progress());
if (alphaNearer < 0.005f && alphaFarther < 0.005f) continue;
Vec3 right = cameraFacingRight(newer.pos, older.pos, camPos);
if (right == null) continue;
float halfWidth = glow ? glowHalfWidth(newer.mood) : coreHalfWidth(newer.mood);
right = right.scale(halfWidth);
float[] cn = color(newer.mood);
float[] co = color(older.mood);
Vec3 na = newer.pos.subtract(camPos);
Vec3 nb = older.pos.subtract(camPos);
buf.addVertex(mat, (float)(na.x + right.x), (float)(na.y + right.y), (float)(na.z + right.z)).setColor(cn[0], cn[1], cn[2], alphaNearer);
buf.addVertex(mat, (float)(na.x - right.x), (float)(na.y - right.y), (float)(na.z - right.z)).setColor(cn[0], cn[1], cn[2], alphaNearer);
buf.addVertex(mat, (float)(nb.x - right.x), (float)(nb.y - right.y), (float)(nb.z - right.z)).setColor(co[0], co[1], co[2], alphaFarther);
buf.addVertex(mat, (float)(nb.x + right.x), (float)(nb.y + right.y), (float)(nb.z + right.z)).setColor(co[0], co[1], co[2], alphaFarther);
}
}
private static Vec3 cameraFacingRight(Vec3 a, Vec3 b, Vec3 camPos) {
Vec3 segDir = b.subtract(a);
if (segDir.lengthSqr() < 1e-12) return null;
segDir = segDir.normalize();
Vec3 mid = a.add(b).scale(0.5);
Vec3 toCamera = camPos.subtract(mid);
if (toCamera.lengthSqr() < 1e-12) return null;
toCamera = toCamera.normalize();
Vec3 right = segDir.cross(toCamera);
if (right.lengthSqr() < 1e-10) {
right = segDir.cross(new Vec3(0.0, 1.0, 0.0));
if (right.lengthSqr() < 1e-10) return null;
}
return right.normalize();
}
private static float indexAlpha(int i, int n) {
if (n <= 1) return 1f;
return 1f - (float) i / (float) (n - 1);
}
private static final float[] COLOR_WHITE = { 1.0f, 1.0f, 1.0f };
private static final float[] COLOR_YELLOW = { 1.0f, 0.85f, 0.1f };
private static final float[] COLOR_RED = { 1.0f, 0.1f, 0.05f };
private static final float[] COLOR_PURPLE = { 0.7f, 0.1f, 1.0f };
private static final float[] COLOR_BLUE = { 0.1f, 0.5f, 1.0f };
private static float[] color(ReverieEntity.Mood mood) {
return switch (mood) {
case WHITE -> COLOR_WHITE;
case YELLOW -> COLOR_YELLOW;
case RED -> COLOR_RED;
case PURPLE -> COLOR_PURPLE;
case BLUE -> COLOR_BLUE;
};
}
private static float glowAlpha(ReverieEntity.Mood mood) {
return switch (mood) {
case WHITE -> 0.15f;
case YELLOW -> 0.30f;
case RED -> 0.55f;
case PURPLE -> 0.65f;
case BLUE -> 0.35f;
};
}
private static float coreAlpha(ReverieEntity.Mood mood) {
return switch (mood) {
case WHITE -> 0.35f;
case YELLOW -> 0.55f;
case RED -> 0.85f;
case PURPLE -> 0.95f;
case BLUE -> 0.60f;
};
}
private static float glowHalfWidth(ReverieEntity.Mood mood) {
float base = GLOW_LINE_WIDTH * 0.5f;
return switch (mood) {
case WHITE -> base * 0.60f;
case YELLOW -> base * 0.80f;
case RED -> base * 1.00f;
case PURPLE -> base * 1.25f;
case BLUE -> base * 0.90f;
};
}
private static float coreHalfWidth(ReverieEntity.Mood mood) {
float base = CORE_LINE_WIDTH * 0.5f;
return switch (mood) {
case WHITE -> base * 0.60f;
case YELLOW -> base * 0.80f;
case RED -> base * 1.20f;
case PURPLE -> base * 1.50f;
case BLUE -> base * 1.00f;
};
}
}
@@ -0,0 +1,144 @@
package com.craisinlord.antarchy.fabric.client;
import com.craisinlord.antarchy.content.client.ScorpionWhipTetherClientState;
import com.craisinlord.antarchy.content.item.ScorpionWhipItem;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.BufferBuilder;
import com.mojang.blaze3d.vertex.BufferUploader;
import com.mojang.blaze3d.vertex.DefaultVertexFormat;
import com.mojang.blaze3d.vertex.MeshData;
import com.mojang.blaze3d.vertex.Tesselator;
import com.mojang.blaze3d.vertex.VertexFormat;
import java.util.Map;
import net.minecraft.client.Camera;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.GameRenderer;
import net.minecraft.util.Mth;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.HumanoidArm;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.phys.Vec3;
import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext;
import org.joml.Matrix4f;
public final class ScorpionWhipTetherRenderHandler {
private ScorpionWhipTetherRenderHandler() {
}
public static void tick() {
Minecraft minecraft = Minecraft.getInstance();
if (minecraft.level == null) {
ScorpionWhipTetherClientState.clear();
}
}
public static void render(WorldRenderContext context) {
if (context.world() == null || context.matrixStack() == null) {
return;
}
Minecraft minecraft = Minecraft.getInstance();
if (minecraft.level == null) {
return;
}
Map<Integer, Integer> tethers = ScorpionWhipTetherClientState.snapshot();
if (tethers.isEmpty()) {
return;
}
Camera camera = context.camera();
Vec3 cameraPos = camera.getPosition();
Matrix4f pose = context.matrixStack().last().pose();
RenderSystem.enableBlend();
RenderSystem.defaultBlendFunc();
RenderSystem.depthMask(false);
RenderSystem.disableCull();
RenderSystem.setShader(GameRenderer::getPositionColorShader);
BufferBuilder buffer = Tesselator.getInstance().begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_COLOR);
for (Map.Entry<Integer, Integer> entry : tethers.entrySet()) {
Entity source = minecraft.level.getEntity(entry.getKey());
Entity target = minecraft.level.getEntity(entry.getValue());
if (!(source instanceof Player player) || target == null || target.isRemoved()) {
continue;
}
buildTether(
buffer,
pose,
tetherStart(player, context.tickCounter().getGameTimeDeltaPartialTick(false)),
target.position().add(0.0D, target.getBbHeight() * 0.5D, 0.0D),
cameraPos
);
}
MeshData mesh = buffer.build();
if (mesh != null) {
BufferUploader.drawWithShader(mesh);
}
RenderSystem.enableCull();
RenderSystem.depthMask(true);
RenderSystem.disableBlend();
}
private static Vec3 tetherStart(Player player, float partialTick) {
Vec3 start = player.getEyePosition(partialTick).add(0.0D, -0.45D, 0.0D);
boolean mainHandWhip = player.getMainHandItem().getItem() instanceof ScorpionWhipItem;
boolean offHandWhip = player.getOffhandItem().getItem() instanceof ScorpionWhipItem;
if (!mainHandWhip && !offHandWhip) {
return start;
}
boolean useMainHand = mainHandWhip || !offHandWhip;
HumanoidArm arm = useMainHand ? player.getMainArm() : player.getMainArm().getOpposite();
float bodyYaw = Mth.rotLerp(partialTick, player.yBodyRotO, player.yBodyRot);
double radians = Math.toRadians(bodyYaw + 90.0F);
double side = arm == HumanoidArm.RIGHT ? -0.28D : 0.28D;
return start.add(Math.cos(radians) * side, -0.12D, Math.sin(radians) * side);
}
private static void buildTether(BufferBuilder buffer, Matrix4f pose, Vec3 start, Vec3 end, Vec3 cameraPos) {
Vec3 delta = end.subtract(start);
double length = delta.length();
if (length < 0.001D) {
return;
}
Vec3 forward = delta.scale(1.0D / length);
Vec3 right = forward.cross(new Vec3(0.0D, 1.0D, 0.0D));
if (right.lengthSqr() < 1.0E-6D) {
right = forward.cross(new Vec3(1.0D, 0.0D, 0.0D));
}
right = right.normalize();
int segments = Math.max(16, Mth.ceil(length * 8.0D));
for (int i = 0; i < segments; i++) {
double t0 = (double) i / (double) segments;
double t1 = (double) (i + 1) / (double) segments;
Vec3 p0 = ropePoint(start, delta, t0, length);
Vec3 p1 = ropePoint(start, delta, t1, length);
float width = 0.045F;
Vec3 offset = right.scale(width);
Vec3 a = p0.subtract(cameraPos);
Vec3 b = p1.subtract(cameraPos);
float shade = i % 2 == 0 ? 0.82F : 0.62F;
float red = 0.36F * shade;
float green = 0.20F * shade;
float blue = 0.11F * shade;
buffer.addVertex(pose, (float) (a.x + offset.x), (float) (a.y + offset.y), (float) (a.z + offset.z)).setColor(red, green, blue, 1.0F);
buffer.addVertex(pose, (float) (a.x - offset.x), (float) (a.y - offset.y), (float) (a.z - offset.z)).setColor(red, green, blue, 1.0F);
buffer.addVertex(pose, (float) (b.x - offset.x), (float) (b.y - offset.y), (float) (b.z - offset.z)).setColor(red, green, blue, 1.0F);
buffer.addVertex(pose, (float) (b.x + offset.x), (float) (b.y + offset.y), (float) (b.z + offset.z)).setColor(red, green, blue, 1.0F);
}
}
private static Vec3 ropePoint(Vec3 start, Vec3 delta, double t, double length) {
double sag = Math.sin(t * Math.PI) * Math.min(0.45D, length * 0.07D);
return start.add(delta.scale(t)).add(0.0D, -sag, 0.0D);
}
}
@@ -0,0 +1,46 @@
package com.craisinlord.antarchy.fabric.client;
import com.craisinlord.antarchy.fabric.AntarchyFabricContent;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.core.BlockPos;
import net.minecraft.util.Mth;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.block.Block;
public final class TriffidGooHudRenderer {
private TriffidGooHudRenderer() {
}
private static float fadeProgress = 0f;
private static final float FADE_SPEED = 0.05f;
private static final int MAX_ALPHA = 0x55;
public static void render(GuiGraphics guiGraphics) {
Minecraft minecraft = Minecraft.getInstance();
Player player = minecraft.player;
if (player == null || player.isDeadOrDying()) {
fadeProgress = Math.max(0f, fadeProgress - FADE_SPEED);
if (fadeProgress <= 0f) return;
} else {
Block goo = AntarchyFabricContent.TRIFFID_GOO_BLOCK.get();
BlockPos feet = player.blockPosition();
boolean inGoo = player.level().getBlockState(feet).is(goo)
|| player.level().getBlockState(feet.above()).is(goo);
if (inGoo) {
fadeProgress = Math.min(1f, fadeProgress + FADE_SPEED);
} else {
fadeProgress = Math.max(0f, fadeProgress - FADE_SPEED);
}
if (fadeProgress <= 0f || minecraft.screen != null) return;
}
int alpha = Mth.floor(MAX_ALPHA * fadeProgress);
int color = (alpha << 24) | 0x003300;
int width = minecraft.getWindow().getGuiScaledWidth();
int height = minecraft.getWindow().getGuiScaledHeight();
guiGraphics.fill(0, 0, width, height, color);
}
}
@@ -0,0 +1,442 @@
package com.craisinlord.antarchy.fabric.content.fluid;
import com.craisinlord.antarchy.fabric.AntarchyFabricContent;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.world.item.Item;
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.LiquidBlock;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.material.FlowingFluid;
import net.minecraft.world.level.material.Fluid;
import net.minecraft.world.level.material.FluidState;
import net.minecraft.world.level.material.Fluids;
import net.minecraft.tags.FluidTags;
import net.minecraft.world.phys.Vec3;
public abstract class AntiwaterFluid extends FlowingFluid {
private static final int MAX_HORIZONTAL_SPREAD_AMOUNT = 5;
private static final int MAX_VERTICAL_FLOW_AMOUNT = 7;
@Override
public Fluid getFlowing() {
return AntarchyFabricContent.FLOWING_ANTIWATER.get();
}
@Override
public Fluid getSource() {
return AntarchyFabricContent.ANTIWATER.get();
}
@Override
public Item getBucket() {
return AntarchyFabricContent.ANTIWATER_BUCKET.get();
}
@Override
public boolean isSame(Fluid fluid) {
return fluid == AntarchyFabricContent.ANTIWATER.get() || fluid == AntarchyFabricContent.FLOWING_ANTIWATER.get();
}
@Override
protected boolean canConvertToSource(Level level) {
return false;
}
@Override
protected void beforeDestroyingBlock(LevelAccessor level, BlockPos pos, BlockState state) {
net.minecraft.world.level.block.Block.dropResources(state, level, pos, state.hasBlockEntity() ? level.getBlockEntity(pos) : null);
}
@Override
protected int getSlopeFindDistance(LevelReader level) {
return 4;
}
@Override
protected int getDropOff(LevelReader level) {
return 1;
}
@Override
public int getTickDelay(LevelReader level) {
return 5;
}
@Override
protected float getExplosionResistance() {
return 100.0F;
}
@Override
protected BlockState createLegacyBlock(FluidState state) {
return AntarchyFabricContent.ANTIWATER_BLOCK.get()
.defaultBlockState()
.setValue(LiquidBlock.LEVEL, getLegacyLevel(state));
}
@Override
protected boolean canBeReplacedWith(FluidState state, BlockGetter level, BlockPos pos, Fluid fluid, Direction direction) {
return direction != Direction.DOWN && !this.isSame(fluid);
}
@Override
protected boolean canSpreadTo(
BlockGetter level,
BlockPos fromPos,
BlockState fromState,
Direction direction,
BlockPos toPos,
BlockState toState,
FluidState toFluidState,
Fluid fluid
) {
if (direction == Direction.DOWN) {
return false;
}
return super.canSpreadTo(level, fromPos, fromState, direction, toPos, toState, toFluidState, fluid);
}
@Override
public Optional<SoundEvent> getPickupSound() {
return Optional.of(SoundEvents.BUCKET_FILL);
}
@Override
protected int getSpreadDelay(Level level, BlockPos pos, FluidState currentState, FluidState newState) {
return this.getTickDelay(level);
}
@Override
public Vec3 getFlow(BlockGetter level, BlockPos pos, FluidState state) {
double flowX = 0.0D;
double flowZ = 0.0D;
for (Direction direction : Direction.Plane.HORIZONTAL) {
BlockPos neighborPos = pos.relative(direction);
FluidState neighborFluid = level.getFluidState(neighborPos);
int neighborAmount = this.isSame(neighborFluid.getType()) ? neighborFluid.getAmount() : 0;
int currentAmount = state.getAmount();
int delta = neighborAmount - currentAmount;
if (delta != 0) {
flowX += direction.getStepX() * delta;
flowZ += direction.getStepZ() * delta;
}
}
Vec3 flow = new Vec3(flowX, 1.0D, flowZ);
return flow.lengthSqr() < 1.0E-6D ? new Vec3(0.0D, 1.0D, 0.0D) : flow.normalize();
}
@Override
protected FluidState getNewLiquid(Level level, BlockPos pos, BlockState blockState) {
FluidState currentState = blockState.getFluidState();
if (!currentState.isSource() && !this.isFlowingStateValid(level, pos, currentState)) {
return Fluids.EMPTY.defaultFluidState();
}
if (this.canCreateSourceAt(level, pos)) {
return this.getSource(false);
}
if (this.isValidVerticalSupport(level, pos)) {
return this.getFlowing(MAX_VERTICAL_FLOW_AMOUNT, false);
}
int bestNeighborAmount = this.getBestHorizontalNeighborAmount(level, pos);
if (bestNeighborAmount > 1) {
return this.getFlowing(bestNeighborAmount - 1, false);
}
return Fluids.EMPTY.defaultFluidState();
}
@Override
protected void spread(Level level, BlockPos pos, FluidState state) {
if (state.isEmpty()) {
return;
}
if (!state.isSource() && !this.isFlowingStateValid(level, pos, state)) {
level.setBlockAndUpdate(pos, net.minecraft.world.level.block.Blocks.AIR.defaultBlockState());
this.scheduleCleanup(level, pos);
return;
}
BlockState blockState = level.getBlockState(pos);
this.spreadUpward(level, pos, state, blockState);
if (!this.canHangAt(level, pos)) {
return;
}
int nextAmount = state.getAmount() - 1;
if (nextAmount <= 0) {
return;
}
this.spreadHorizontally(level, pos, blockState, nextAmount);
}
@Override
public void tick(Level level, BlockPos pos, FluidState state) {
if (!state.isSource() && !this.isFlowingStateValid(level, pos, state)) {
level.setBlockAndUpdate(pos, net.minecraft.world.level.block.Blocks.AIR.defaultBlockState());
this.scheduleDependentCleanup(level, pos);
return;
}
super.tick(level, pos, state);
}
public static final class Source extends AntiwaterFluid {
@Override
public int getAmount(FluidState state) {
return 8;
}
@Override
public boolean isSource(FluidState state) {
return true;
}
}
public static final class Flowing extends AntiwaterFluid {
public Flowing() {
this.registerDefaultState(this.getStateDefinition().any().setValue(LEVEL, 7));
}
@Override
protected void createFluidStateDefinition(net.minecraft.world.level.block.state.StateDefinition.Builder<Fluid, FluidState> builder) {
super.createFluidStateDefinition(builder);
builder.add(LEVEL);
}
@Override
public int getAmount(FluidState state) {
return state.getValue(LEVEL);
}
@Override
public boolean isSource(FluidState state) {
return false;
}
}
private boolean hasCeiling(Level level, BlockPos pos) {
return level.getBlockState(pos.above()).isSolid();
}
private boolean canHangAt(Level level, BlockPos pos) {
return this.hasCeiling(level, pos);
}
private boolean hasHorizontalSupport(Level level, BlockPos pos) {
FluidState state = level.getFluidState(pos);
if (!this.isAntiwater(state)) {
return false;
}
int currentAmount = state.getAmount();
for (Direction direction : Direction.Plane.HORIZONTAL) {
FluidState neighbor = level.getFluidState(pos.relative(direction));
if (!this.isAntiwater(neighbor)) {
continue;
}
if (this.getHorizontalSpreadAmount(neighbor) > currentAmount) {
return true;
}
}
return false;
}
private boolean canSpreadHorizontallyInto(Level level, BlockPos pos) {
return true;
}
private int getBestHorizontalNeighborAmount(Level level, BlockPos pos) {
int bestAmount = 0;
for (Direction direction : Direction.Plane.HORIZONTAL) {
BlockPos neighborPos = pos.relative(direction);
FluidState neighborFluid = level.getFluidState(neighborPos);
if (neighborFluid.isEmpty() || !neighborFluid.getType().isSame(this)) {
continue;
}
if (!this.hasSourceConnection(level, neighborPos, neighborFluid, new HashSet<>())) {
continue;
}
bestAmount = Math.max(bestAmount, this.getHorizontalSpreadAmount(neighborFluid));
}
return bestAmount;
}
private int getHorizontalSpreadAmount(FluidState state) {
if (state.isSource() || this.isValidVerticalSupportState(state)) {
return MAX_HORIZONTAL_SPREAD_AMOUNT + 1;
}
return state.getAmount();
}
private boolean isValidVerticalSupportState(FluidState state) {
return this.isAntiwater(state) && !state.isSource() && state.getAmount() >= MAX_VERTICAL_FLOW_AMOUNT;
}
private boolean isFlowingStateValid(Level level, BlockPos pos, FluidState state) {
return state.isSource()
|| this.hasImmediateVerticalSupport(level, pos)
|| this.hasHorizontalSupport(level, pos);
}
private boolean canCreateSourceAt(Level level, BlockPos pos) {
if (!this.canHangAt(level, pos)) {
return false;
}
int sourceNeighbors = 0;
for (Direction direction : Direction.Plane.HORIZONTAL) {
FluidState neighbor = level.getFluidState(pos.relative(direction));
if (this.isAntiwater(neighbor) && neighbor.isSource()) {
sourceNeighbors++;
if (sourceNeighbors >= 2) {
return true;
}
}
}
return false;
}
private boolean isValidVerticalSupport(Level level, BlockPos pos) {
return this.hasImmediateVerticalSupport(level, pos);
}
private boolean isAntiwater(FluidState state) {
return !state.isEmpty() && state.getType().isSame(this);
}
private boolean hasImmediateVerticalSupport(Level level, BlockPos pos) {
FluidState below = level.getFluidState(pos.below());
return this.isAntiwater(below);
}
private void spreadUpward(Level level, BlockPos pos, FluidState state, BlockState blockState) {
BlockPos abovePos = pos.above();
BlockState aboveState = level.getBlockState(abovePos);
FluidState aboveFluid = level.getFluidState(abovePos);
if (aboveFluid.is(FluidTags.WATER)) {
BlockState result = state.isSource()
? AntarchyFabricContent.ANTIMETAL.get().defaultBlockState()
: AntarchyFabricContent.NYXITE.get().defaultBlockState();
level.setBlockAndUpdate(pos, result);
level.levelEvent(1501, pos, 0);
return;
}
FluidState upwardState = this.getFlowing(MAX_VERTICAL_FLOW_AMOUNT, false);
if (!this.canSpreadTo(level, pos, blockState, Direction.UP, abovePos, aboveState, aboveFluid, upwardState.getType())) {
return;
}
if (aboveFluid.getType().isSame(this) && aboveFluid.getAmount() >= upwardState.getAmount()) {
return;
}
this.spreadTo(level, abovePos, aboveState, Direction.UP, upwardState);
}
private void spreadHorizontally(Level level, BlockPos pos, BlockState blockState, int nextAmount) {
FluidState horizontalState = this.getFlowing(Math.min(nextAmount, MAX_HORIZONTAL_SPREAD_AMOUNT), false);
for (Direction direction : Direction.Plane.HORIZONTAL) {
BlockPos targetPos = pos.relative(direction);
BlockState targetState = level.getBlockState(targetPos);
FluidState targetFluid = targetState.getFluidState();
if (!this.canSpreadHorizontallyInto(level, targetPos)) {
continue;
}
if (targetFluid.getType().isSame(this) && targetFluid.getAmount() >= horizontalState.getAmount()) {
continue;
}
if (!this.canSpreadTo(level, pos, blockState, direction, targetPos, targetState, targetFluid, horizontalState.getType())) {
continue;
}
this.spreadTo(level, targetPos, targetState, direction, horizontalState);
}
}
private boolean hasSourceConnection(Level level, BlockPos pos, FluidState state, Set<BlockPos> visited) {
if (!this.isAntiwater(state)) {
return false;
}
if (state.isSource()) {
return true;
}
if (!visited.add(pos)) {
return false;
}
FluidState below = level.getFluidState(pos.below());
if (this.hasSourceConnection(level, pos.below(), below, visited)) {
return true;
}
int currentAmount = state.getAmount();
for (Direction direction : Direction.Plane.HORIZONTAL) {
BlockPos neighborPos = pos.relative(direction);
FluidState neighbor = level.getFluidState(neighborPos);
if (!this.isAntiwater(neighbor)) {
continue;
}
if (this.getHorizontalSpreadAmount(neighbor) <= currentAmount) {
continue;
}
if (this.hasSourceConnection(level, neighborPos, neighbor, visited)) {
return true;
}
}
return false;
}
private void scheduleCleanup(Level level, BlockPos pos) {
int tickDelay = this.getTickDelay(level);
level.scheduleTick(pos, this, tickDelay);
level.scheduleTick(pos.above(), this, tickDelay);
level.scheduleTick(pos.below(), this, tickDelay);
for (Direction direction : Direction.Plane.HORIZONTAL) {
level.scheduleTick(pos.relative(direction), this, tickDelay);
}
}
private void scheduleDependentCleanup(Level level, BlockPos pos) {
int tickDelay = this.getTickDelay(level);
level.scheduleTick(pos.above(), this, tickDelay);
for (Direction direction : Direction.Plane.HORIZONTAL) {
level.scheduleTick(pos.relative(direction), this, tickDelay);
}
}
}
@@ -0,0 +1,61 @@
package com.craisinlord.antarchy.fabric.content.fluid;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.LiquidBlock;
import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.material.FlowingFluid;
public class AntiwaterLiquidBlock extends LiquidBlock {
public AntiwaterLiquidBlock(FlowingFluid fluid, BlockBehaviour.Properties properties) {
super(fluid, properties);
}
@Override
protected void onPlace(BlockState state, Level level, BlockPos pos, BlockState oldState, boolean movedByPiston) {
super.onPlace(state, level, pos, oldState, movedByPiston);
scheduleCurrentAntiwaterTick(level, pos);
}
@Override
protected void neighborChanged(BlockState state, Level level, BlockPos pos, Block neighborBlock, BlockPos neighborPos, boolean movedByPiston) {
super.neighborChanged(state, level, pos, neighborBlock, neighborPos, movedByPiston);
scheduleCurrentAntiwaterTick(level, pos);
}
@Override
protected BlockState updateShape(
BlockState state,
Direction direction,
BlockState neighborState,
LevelAccessor level,
BlockPos pos,
BlockPos neighborPos
) {
scheduleCurrentAntiwaterTick(level, pos);
return super.updateShape(state, direction, neighborState, level, pos, neighborPos);
}
@Override
protected void onRemove(BlockState state, Level level, BlockPos pos, BlockState newState, boolean movedByPiston) {
if (!state.is(newState.getBlock())) {
scheduleDependentAntiwaterTicks(level, pos);
}
super.onRemove(state, level, pos, newState, movedByPiston);
}
private void scheduleCurrentAntiwaterTick(LevelAccessor level, BlockPos pos) {
level.scheduleTick(pos, this.fluid, this.fluid.getTickDelay(level));
}
private void scheduleDependentAntiwaterTicks(LevelAccessor level, BlockPos pos) {
level.scheduleTick(pos.above(), this.fluid, this.fluid.getTickDelay(level));
for (Direction direction : Direction.Plane.HORIZONTAL) {
level.scheduleTick(pos.relative(direction), this.fluid, this.fluid.getTickDelay(level));
}
}
}
@@ -0,0 +1,171 @@
package com.craisinlord.antarchy.fabric.content.fluid;
import java.util.Optional;
import java.util.function.Supplier;
import com.craisinlord.antarchy.Antarchy;
import net.minecraft.core.BlockPos;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.world.item.Item;
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.LiquidBlock;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.core.Direction;
import net.minecraft.world.level.material.FlowingFluid;
import net.minecraft.world.level.material.Fluid;
import net.minecraft.world.level.material.FluidState;
public abstract class SimpleFluid extends FlowingFluid {
private final Supplier<? extends FlowingFluid> source;
private final Supplier<? extends FlowingFluid> flowing;
private final Supplier<? extends Item> bucket;
private final String blockId;
private final int slopeFindDistance;
private final int dropOff;
private final int tickDelay;
protected SimpleFluid(
Supplier<? extends FlowingFluid> source,
Supplier<? extends FlowingFluid> flowing,
Supplier<? extends Item> bucket,
String blockId,
int slopeFindDistance,
int dropOff,
int tickDelay
) {
this.source = source;
this.flowing = flowing;
this.bucket = bucket;
this.blockId = blockId;
this.slopeFindDistance = slopeFindDistance;
this.dropOff = dropOff;
this.tickDelay = tickDelay;
}
@Override
public Fluid getFlowing() {
return this.flowing.get();
}
@Override
public Fluid getSource() {
return this.source.get();
}
@Override
public Item getBucket() {
return this.bucket.get();
}
@Override
protected boolean canConvertToSource(Level level) {
return false;
}
@Override
protected void beforeDestroyingBlock(LevelAccessor level, BlockPos pos, BlockState state) {
BlockEntity blockEntity = state.hasBlockEntity() ? level.getBlockEntity(pos) : null;
net.minecraft.world.level.block.Block.dropResources(state, level, pos, blockEntity);
}
@Override
protected int getSlopeFindDistance(LevelReader level) {
return this.slopeFindDistance;
}
@Override
protected int getDropOff(LevelReader level) {
return this.dropOff;
}
@Override
public int getTickDelay(LevelReader level) {
return this.tickDelay;
}
@Override
protected float getExplosionResistance() {
return 100.0F;
}
@Override
public boolean isSame(Fluid fluid) {
return fluid == this.source.get() || fluid == this.flowing.get();
}
@Override
protected BlockState createLegacyBlock(FluidState state) {
LiquidBlock block = (LiquidBlock) BuiltInRegistries.BLOCK.get(ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, this.blockId));
return block.defaultBlockState().setValue(LiquidBlock.LEVEL, getLegacyLevel(state));
}
@Override
public Optional<SoundEvent> getPickupSound() {
return Optional.of(SoundEvents.BUCKET_FILL);
}
@Override
protected boolean canBeReplacedWith(FluidState state, BlockGetter level, BlockPos pos, Fluid fluid, Direction direction) {
return false;
}
public static final class Flowing extends SimpleFluid {
public Flowing(
Supplier<? extends FlowingFluid> source,
Supplier<? extends FlowingFluid> flowing,
Supplier<? extends Item> bucket,
String blockId,
int slopeFindDistance,
int dropOff,
int tickDelay
) {
super(source, flowing, bucket, blockId, slopeFindDistance, dropOff, tickDelay);
}
@Override
protected void createFluidStateDefinition(net.minecraft.world.level.block.state.StateDefinition.Builder<Fluid, FluidState> builder) {
super.createFluidStateDefinition(builder);
builder.add(LEVEL);
}
@Override
public int getAmount(FluidState state) {
return state.getValue(LEVEL);
}
@Override
public boolean isSource(FluidState state) {
return false;
}
}
public static final class Source extends SimpleFluid {
public Source(
Supplier<? extends FlowingFluid> source,
Supplier<? extends FlowingFluid> flowing,
Supplier<? extends Item> bucket,
String blockId,
int slopeFindDistance,
int dropOff,
int tickDelay
) {
super(source, flowing, bucket, blockId, slopeFindDistance, dropOff, tickDelay);
}
@Override
public int getAmount(FluidState state) {
return 8;
}
@Override
public boolean isSource(FluidState state) {
return true;
}
}
}
@@ -0,0 +1,11 @@
package com.craisinlord.antarchy.fabric.item;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ScaffoldingBlockItem;
import net.minecraft.world.level.block.Block;
public final class AntimetalScaffoldingItem extends ScaffoldingBlockItem {
public AntimetalScaffoldingItem(Block block, Item.Properties properties) {
super(block, properties);
}
}
@@ -0,0 +1,13 @@
package com.craisinlord.antarchy.fabric.item;
import java.util.function.Supplier;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.Mob;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.SpawnEggItem;
public final class DeferredSpawnEggItem extends SpawnEggItem {
public DeferredSpawnEggItem(Supplier<? extends EntityType<? extends Mob>> typeSupplier, int backgroundColor, int highlightColor, Item.Properties properties) {
super(typeSupplier.get(), backgroundColor, highlightColor, properties);
}
}
@@ -0,0 +1,81 @@
package com.craisinlord.antarchy.fabric.mixin;
import com.craisinlord.antarchy.content.AntarchyObjects;
import com.craisinlord.antarchy.content.gravity.AntarchyGravityApi;
import com.craisinlord.antarchy.content.gravity.AntarchyGravityDirection;
import com.craisinlord.antarchy.content.gravity.AntarchyGravityTransition;
import net.minecraft.core.BlockPos;
import net.minecraft.core.particles.DustParticleOptions;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.util.Mth;
import net.minecraft.world.effect.MobEffectInstance;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.phys.AABB;
import org.joml.Vector3f;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(LivingEntity.class)
public abstract class AntimetalInversionMixin {
private static final int ANTIMETAL_INVERTED_REFRESH_TICKS = 20;
@Inject(method = "tick", at = @At("TAIL"))
private void antarchy$tickOverheadInversion(CallbackInfo ci) {
LivingEntity entity = (LivingEntity) (Object) this;
if (entity.level().isClientSide()) return;
if (entity instanceof Player player && player.isSpectator()) return;
if (!antarchy$isDirectlyBelowAntimetal(entity)) return;
MobEffectInstance existing = entity.getEffect(AntarchyObjects.INVERTED_EFFECT.get());
if (existing == null || existing.getDuration() <= 5) {
entity.addEffect(new MobEffectInstance(AntarchyObjects.INVERTED_EFFECT.get(), ANTIMETAL_INVERTED_REFRESH_TICKS, 0, false, false, false));
antarchy$spawnInversionParticles(entity);
}
}
private static boolean antarchy$isDirectlyBelowAntimetal(LivingEntity entity) {
AABB bounds = entity.getBoundingBox().deflate(1.0E-3D);
int minX = Mth.floor(bounds.minX);
int maxX = Mth.floor(bounds.maxX);
int minY = Mth.floor(bounds.minY);
int maxY = Mth.floor(bounds.maxY);
int minZ = Mth.floor(bounds.minZ);
int maxZ = Mth.floor(bounds.maxZ);
for (int x = minX; x <= maxX; x++) {
for (int y = minY; y <= maxY; y++) {
for (int z = minZ; z <= maxZ; z++) {
BlockPos above = new BlockPos(x, y + 1, z);
if (entity.level().getBlockState(above).is(AntarchyObjects.ANTIMETAL.get())
|| entity.level().getBlockState(above).is(AntarchyObjects.POLISHED_ANTIMETAL.get())) {
return true;
}
}
}
}
return false;
}
@Inject(method = "onEffectRemoved", at = @At("HEAD"))
private void antarchy$onInvertedRemoved(MobEffectInstance effectInstance, CallbackInfo ci) {
LivingEntity entity = (LivingEntity) (Object) this;
if (entity.level().isClientSide()) return;
if (!effectInstance.is(AntarchyObjects.INVERTED_EFFECT.get())) return;
AntarchyGravityApi.setGravityDirection(entity, AntarchyGravityDirection.DOWN, new AntarchyGravityTransition(12));
}
private static void antarchy$spawnInversionParticles(LivingEntity entity) {
if (!(entity.level() instanceof ServerLevel serverLevel)) return;
double x = entity.getX();
double y = entity.getBoundingBox().maxY + 0.1D;
double z = entity.getZ();
serverLevel.sendParticles(
new DustParticleOptions(new Vector3f(1.0F, 0.1F, 0.1F), 1.0F),
x, y, z, 6, 0.2D, 0.05D, 0.2D, 0.0D
);
}
}
@@ -0,0 +1,108 @@
package com.craisinlord.antarchy.fabric.mixin;
import com.craisinlord.antarchy.fabric.AntarchyFabricContent;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.item.FallingBlockEntity;
import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelReader;
import net.minecraft.world.level.block.ScaffoldingBlock;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.util.RandomSource;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(ScaffoldingBlock.class)
public abstract class AntimetalScaffoldingMixin {
@Inject(method = "getStateForPlacement", at = @At("HEAD"), cancellable = true)
private void antarchy$antimetalPlacement(BlockPlaceContext context, CallbackInfoReturnable<BlockState> cir) {
if (!antarchy$isAntimetalScaffolding()) {
return;
}
BlockPos pos = context.getClickedPos();
Level level = context.getLevel();
int distance = antarchy$getDistance(level, pos);
BlockState state = ((ScaffoldingBlock) (Object) this).defaultBlockState()
.setValue(ScaffoldingBlock.WATERLOGGED, level.getFluidState(pos).getType().isSame(net.minecraft.world.level.material.Fluids.WATER))
.setValue(ScaffoldingBlock.DISTANCE, distance)
.setValue(ScaffoldingBlock.BOTTOM, antarchy$isBottom(level, pos, distance));
cir.setReturnValue(state);
}
@Inject(method = "canSurvive", at = @At("HEAD"), cancellable = true)
private void antarchy$antimetalCanSurvive(BlockState state, LevelReader level, BlockPos pos, CallbackInfoReturnable<Boolean> cir) {
if (!antarchy$isAntimetalScaffolding()) {
return;
}
cir.setReturnValue(antarchy$getDistance(level, pos) < 7);
}
@Inject(method = "tick", at = @At("HEAD"), cancellable = true)
private void antarchy$antimetalTick(BlockState state, ServerLevel level, BlockPos pos, RandomSource random, CallbackInfo ci) {
if (!antarchy$isAntimetalScaffolding()) {
return;
}
int distance = antarchy$getDistance(level, pos);
BlockState updated = state.setValue(ScaffoldingBlock.DISTANCE, distance)
.setValue(ScaffoldingBlock.BOTTOM, antarchy$isBottom(level, pos, distance));
if (updated.getValue(ScaffoldingBlock.DISTANCE) == 7) {
if (state.getValue(ScaffoldingBlock.DISTANCE) == 7) {
FallingBlockEntity.fall(level, pos, updated);
} else {
level.destroyBlock(pos, true);
}
} else if (state != updated) {
level.setBlock(pos, updated, 3);
}
ci.cancel();
}
private boolean antarchy$isAntimetalScaffolding() {
return ((ScaffoldingBlock) (Object) this).asItem() == AntarchyFabricContent.ANTIMETAL_SCAFFOLDING.get().asItem();
}
private int antarchy$getDistance(BlockGetter level, BlockPos pos) {
BlockPos.MutableBlockPos cursor = pos.mutable().move(Direction.DOWN);
BlockState below = level.getBlockState(cursor);
int distance = 7;
if (below.is((ScaffoldingBlock) (Object) this)) {
distance = below.getValue(ScaffoldingBlock.DISTANCE);
} else if (below.isFaceSturdy(level, cursor, Direction.UP)) {
return 0;
}
for (Direction direction : Direction.Plane.HORIZONTAL) {
BlockPos.MutableBlockPos neighborPos = pos.mutable().move(direction);
BlockState neighbor = level.getBlockState(neighborPos);
if (!neighbor.is((ScaffoldingBlock) (Object) this)) {
continue;
}
distance = Math.min(distance, neighbor.getValue(ScaffoldingBlock.DISTANCE) + 1);
if (distance == 1) {
break;
}
}
return distance;
}
private boolean antarchy$isBottom(BlockGetter level, BlockPos pos, int distance) {
if (distance <= 0) {
return false;
}
return !level.getBlockState(pos.below()).is((ScaffoldingBlock) (Object) this);
}
}
@@ -0,0 +1,47 @@
package com.craisinlord.antarchy.fabric.mixin;
import com.craisinlord.antarchy.content.block.PotentNyxiteBlock;
import net.minecraft.core.BlockPos;
import net.minecraft.world.entity.item.ItemEntity;
import net.minecraft.world.level.material.FluidState;
import net.minecraft.world.phys.Vec3;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(ItemEntity.class)
public abstract class AntiwaterItemEntityMixin {
@Inject(method = "tick", at = @At("TAIL"))
private void antarchy$applyAntiwaterItemMovement(CallbackInfo ci) {
ItemEntity entity = (ItemEntity) (Object) this;
if (!antarchy$isTouchingAntiwater(entity)) {
return;
}
Vec3 motion = entity.getDeltaMovement();
entity.setDeltaMovement(
motion.x * 0.99D,
motion.y + (motion.y < 0.06D ? 5.0E-4D : 0.0D),
motion.z * 0.99D
);
}
private boolean antarchy$isTouchingAntiwater(ItemEntity entity) {
BlockPos min = BlockPos.containing(entity.getBoundingBox().minX, entity.getBoundingBox().minY, entity.getBoundingBox().minZ);
BlockPos max = BlockPos.containing(entity.getBoundingBox().maxX, entity.getBoundingBox().maxY, entity.getBoundingBox().maxZ);
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);
FluidState fluidState = entity.level().getFluidState(cursor);
if (PotentNyxiteBlock.isAntiwater(fluidState)) {
return true;
}
}
}
}
return false;
}
}
@@ -0,0 +1,100 @@
package com.craisinlord.antarchy.fabric.mixin;
import com.craisinlord.antarchy.content.AntarchyObjects;
import com.craisinlord.antarchy.content.block.PotentNyxiteBlock;
import com.craisinlord.antarchy.content.gravity.AntarchyGravityApi;
import com.craisinlord.antarchy.content.gravity.AntarchyGravityDirection;
import com.craisinlord.antarchy.content.gravity.AntarchyGravityRotationUtil;
import net.minecraft.core.BlockPos;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.MoverType;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.material.FluidState;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(LivingEntity.class)
public abstract class AntiwaterLivingEntityMixin {
@Inject(method = "travel", at = @At("HEAD"), cancellable = true)
private void antarchy$moveInAntiwater(Vec3 travelVector, CallbackInfo ci) {
LivingEntity entity = (LivingEntity) (Object) this;
if (!antarchy$isTouchingAntiwaterLocal(entity)) {
return;
}
double prevY = entity.getY();
float swimSpeed = entity.isSprinting() ? 0.026F : 0.02F;
entity.moveRelative(swimSpeed, travelVector);
entity.move(MoverType.SELF, entity.getDeltaMovement());
Vec3 motion = entity.getDeltaMovement();
if (entity.horizontalCollision && entity.onClimbable()) {
motion = new Vec3(motion.x, 0.2D, motion.z);
}
AntarchyGravityDirection gravityDirection = antarchy$getEffectiveGravityDirection(entity);
Vec3 worldMotion = gravityDirection.isInverted()
? AntarchyGravityRotationUtil.vecPlayerToWorld(motion, gravityDirection)
: motion;
double drag = entity.isSwimming() ? 0.9D : 0.8D;
worldMotion = worldMotion.scale(drag);
boolean flying = entity instanceof Player player && player.getAbilities().flying;
if (!flying) {
worldMotion = worldMotion.add(0.0D, 0.005D, 0.0D);
}
entity.setDeltaMovement(
gravityDirection.isInverted()
? AntarchyGravityRotationUtil.vecWorldToPlayer(worldMotion, gravityDirection)
: worldMotion
);
if (entity.horizontalCollision && entity.isFree(
worldMotion.x,
worldMotion.y + 0.6D - entity.getY() + prevY,
worldMotion.z
)) {
Vec3 boostedWorldMotion = new Vec3(worldMotion.x, 0.3D, worldMotion.z);
entity.setDeltaMovement(
gravityDirection.isInverted()
? AntarchyGravityRotationUtil.vecWorldToPlayer(boostedWorldMotion, gravityDirection)
: boostedWorldMotion
);
}
ci.cancel();
}
private AntarchyGravityDirection antarchy$getEffectiveGravityDirection(LivingEntity entity) {
AntarchyGravityDirection gravityDirection = AntarchyGravityApi.getGravityDirection(entity);
if (gravityDirection.isInverted() || !entity.hasEffect(AntarchyObjects.INVERTED_EFFECT.get())) {
return gravityDirection;
}
return AntarchyGravityDirection.UP;
}
private boolean antarchy$isTouchingAntiwaterLocal(LivingEntity entity) {
AABB box = entity.getBoundingBox().inflate(0.05D);
BlockPos min = BlockPos.containing(box.minX, box.minY, box.minZ);
BlockPos max = BlockPos.containing(box.maxX, box.maxY, box.maxZ);
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);
FluidState fluidState = entity.level().getFluidState(cursor);
if (PotentNyxiteBlock.isAntiwater(fluidState)) {
return true;
}
}
}
}
return false;
}
}
@@ -0,0 +1,33 @@
package com.craisinlord.antarchy.fabric.mixin;
import com.craisinlord.antarchy.content.AntarchyObjects;
import com.craisinlord.antarchy.fabric.BloodglassManager;
import net.minecraft.world.effect.MobEffectInstance;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.player.Player;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(LivingEntity.class)
public abstract class BloodglassEffectMixin {
@Inject(method = "onEffectAdded", at = @At("TAIL"))
private void antarchy$onBloodglassWardAdded(MobEffectInstance effectInstance, @Nullable Entity source, CallbackInfo ci) {
LivingEntity entity = (LivingEntity) (Object) this;
if (!effectInstance.is(AntarchyObjects.BLOODGLASS_WARD.get())) return;
if (!(entity instanceof Player player)) return;
BloodglassManager.handleWardApplied(player, effectInstance.getAmplifier());
}
@Inject(method = "onEffectRemoved", at = @At("HEAD"))
private void antarchy$onBloodglassWardRemoved(MobEffectInstance effectInstance, CallbackInfo ci) {
LivingEntity entity = (LivingEntity) (Object) this;
if (!effectInstance.is(AntarchyObjects.BLOODGLASS_WARD.get())) return;
if (!(entity instanceof Player player)) return;
BloodglassManager.handleWardRemoved(player);
}
}
@@ -0,0 +1,23 @@
package com.craisinlord.antarchy.fabric.mixin;
import com.craisinlord.antarchy.fabric.BloodglassManager;
import net.minecraft.world.entity.EquipmentSlot;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(LivingEntity.class)
public abstract class BloodglassEquipMixin {
@Inject(method = "onEquipItem", at = @At("TAIL"))
private void antarchy$onBloodglassEquip(EquipmentSlot slot, ItemStack from, ItemStack to, CallbackInfo ci) {
LivingEntity entity = (LivingEntity) (Object) this;
if (entity instanceof Player player) {
BloodglassManager.handleArmorEquipChange(player, slot, from, to);
}
}
}
@@ -0,0 +1,17 @@
package com.craisinlord.antarchy.fabric.mixin;
import com.craisinlord.antarchy.fabric.BloodglassManager;
import net.minecraft.server.level.ServerPlayer;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(ServerPlayer.class)
public abstract class BloodglassRespawnMixin {
@Inject(method = "restoreFrom", at = @At("TAIL"))
private void antarchy$onRespawn(ServerPlayer oldPlayer, boolean keepEverything, CallbackInfo ci) {
BloodglassManager.handleRespawn((ServerPlayer) (Object) this);
}
}
@@ -0,0 +1,29 @@
package com.craisinlord.antarchy.fabric.mixin;
import com.craisinlord.antarchy.content.AntarchyObjects;
import com.craisinlord.antarchy.content.block.entity.DreamCampfireBlockEntity;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.entity.CampfireBlockEntity;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.ModifyArg;
@Mixin(CampfireBlockEntity.class)
public abstract class DreamCampfireBlockEntityMixin {
@ModifyArg(
method = "<init>",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/world/level/block/entity/BlockEntity;<init>(Lnet/minecraft/world/level/block/entity/BlockEntityType;Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/block/state/BlockState;)V"
),
index = 0
)
private static BlockEntityType<? extends BlockEntity> antarchy$useDreamCampfireType(
BlockEntityType<? extends BlockEntity> original
) {
return DreamCampfireBlockEntity.isConstructingDreamCampfire()
? AntarchyObjects.DREAM_CAMPFIRE_BLOCK_ENTITY.get()
: original;
}
}
@@ -0,0 +1,27 @@
package com.craisinlord.antarchy.fabric.mixin;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.item.ElytraItem;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
@Mixin(LivingEntity.class)
public abstract class ElytraFallFlyingMixin {
/**
* Redirect to allow any ElytraItem subclass to sustain fall flying.
*/
@Redirect(
method = "updateFallFlying",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/world/item/ItemStack;is(Lnet/minecraft/world/item/Item;)Z"
)
)
private boolean antarchy$allowCustomElytraFallFlying(ItemStack itemStack, Item item) {
return itemStack.getItem() instanceof ElytraItem;
}
}
@@ -0,0 +1,32 @@
package com.craisinlord.antarchy.fabric.mixin;
import net.minecraft.world.entity.EquipmentSlot;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ElytraItem;
import net.minecraft.world.item.ItemStack;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(Player.class)
public abstract class ElytraStartFlyingMixin {
@Shadow
public abstract void startFallFlying();
@Inject(method = "tryToStartFallFlying", at = @At("RETURN"), cancellable = true)
private void antarchy$allowCustomElytra(CallbackInfoReturnable<Boolean> cir) {
if (cir.getReturnValue()) return;
Player player = (Player) (Object) this;
if (!player.onGround() && !player.isFallFlying() && !player.isInWater()
&& !player.hasEffect(net.minecraft.world.effect.MobEffects.LEVITATION)) {
ItemStack chest = player.getItemBySlot(EquipmentSlot.CHEST);
if (chest.getItem() instanceof ElytraItem && ElytraItem.isFlyEnabled(chest)) {
this.startFallFlying();
cir.setReturnValue(true);
}
}
}
}
@@ -0,0 +1,49 @@
package com.craisinlord.antarchy.fabric.mixin.client;
import com.craisinlord.antarchy.content.block.PotentNyxiteBlock;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.shaders.FogShape;
import net.minecraft.client.Camera;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.renderer.FogRenderer;
import net.minecraft.core.BlockPos;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.material.FluidState;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(FogRenderer.class)
public abstract class AntiwaterFogMixin {
// @Inject(method = "setupColor", at = @At("TAIL"))
// private static void antarchy$overrideAntiwaterColor(Camera camera, float partialTick, ClientLevel level, int renderDistance, float darkenWorldAmount, CallbackInfo ci) {
// if (!antarchy$isInAntiwater(camera)) {
// return;
// }
//
// RenderSystem.clearColor(0.52F, 0.06F, 0.06F, 0.0F);
// }
//
// @Inject(method = "setupFog", at = @At("TAIL"))
// private static void antarchy$overrideAntiwaterFog(Camera camera, FogRenderer.FogMode mode, float renderDistance, boolean thickFog, float partialTick, CallbackInfo ci) {
// if (!antarchy$isInAntiwater(camera)) {
// return;
// }
//
// RenderSystem.setShaderFogStart(0.5F);
// RenderSystem.setShaderFogEnd(Math.min(renderDistance, 12.0F));
// RenderSystem.setShaderFogShape(FogShape.CYLINDER);
// }
//
// private static boolean antarchy$isInAntiwater(Camera camera) {
// Entity entity = camera.getEntity();
// if (entity == null || entity.level() == null) {
// return false;
// }
//
// BlockPos pos = BlockPos.containing(camera.getPosition());
// FluidState fluidState = entity.level().getFluidState(pos);
// return PotentNyxiteBlock.isAntiwater(fluidState);
// }
}
@@ -0,0 +1,35 @@
package com.craisinlord.antarchy.fabric.mixin.client;
import com.craisinlord.antarchy.content.item.GravityGunItem;
import com.craisinlord.antarchy.content.item.SizeRayItem;
import com.craisinlord.antarchy.content.item.SquidzookaItem;
import net.minecraft.client.model.HumanoidModel;
import net.minecraft.client.player.AbstractClientPlayer;
import net.minecraft.client.renderer.entity.player.PlayerRenderer;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(PlayerRenderer.class)
public class CrossbowArmPoseMixin {
@Inject(method = "getArmPose", at = @At("RETURN"), cancellable = true)
private static void antarchy$customArmPose(
AbstractClientPlayer player, InteractionHand hand,
CallbackInfoReturnable<HumanoidModel.ArmPose> cir) {
ItemStack stack = player.getItemInHand(hand);
Item item = stack.getItem();
if (item instanceof GravityGunItem || item instanceof SquidzookaItem) {
cir.setReturnValue(HumanoidModel.ArmPose.CROSSBOW_HOLD);
} else if (item instanceof SizeRayItem) {
if (player.isUsingItem() && player.getUsedItemHand() == hand) {
cir.setReturnValue(HumanoidModel.ArmPose.CROSSBOW_HOLD);
}
}
}
}
@@ -0,0 +1,30 @@
package com.craisinlord.antarchy.fabric.mixin.client;
import com.craisinlord.antarchy.content.block.DuctTapeBlock;
import net.minecraft.client.Minecraft;
import net.minecraft.client.player.Input;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(Input.class)
public abstract class DuctTapeInputMixin {
@Inject(method = "tick", at = @At("RETURN"))
private void blockInputInDuctTape(boolean isSneaking, float slowFactor, CallbackInfo ci) {
Minecraft mc = Minecraft.getInstance();
if (mc.player == null || mc.level == null) return;
if (!mc.player.isNoGravity() && !DuctTapeBlock.isTouchingTape(mc.level, mc.player)) return;
Input self = (Input) (Object) this;
self.left = false;
self.right = false;
self.up = false;
self.down = false;
self.leftImpulse = 0.0F;
self.forwardImpulse = 0.0F;
self.jumping = false;
self.shiftKeyDown = false;
}
}
@@ -0,0 +1,18 @@
package com.craisinlord.antarchy.fabric.mixin.client;
import com.craisinlord.antarchy.fabric.client.GravityGunClientHandler;
import net.minecraft.client.MouseHandler;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(MouseHandler.class)
public abstract class GravityGunMouseScrollMixin {
@Inject(method = "onScroll", at = @At("HEAD"), cancellable = true)
private void antarchy$handleGravityGunScroll(long window, double horizontalAmount, double verticalAmount, CallbackInfo ci) {
if (GravityGunClientHandler.onScroll(verticalAmount)) {
ci.cancel();
}
}
}
@@ -0,0 +1,63 @@
package com.craisinlord.antarchy.fabric.mixin.client;
import com.craisinlord.antarchy.content.entity.nightmare.NightmareEntity;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.math.Axis;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.GameRenderer;
import net.minecraft.util.Mth;
import net.minecraft.world.phys.Vec3;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
//TODO FIX ON FABRIC SIDE
@Mixin(GameRenderer.class)
public abstract class NightmareCameraShakeMixin {
// private static final double MAX_SHAKE_RANGE = 32.0D;
//
// @Shadow @Final private Minecraft minecraft;
//
// @Redirect(
// method = "renderLevel",
// at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/GameRenderer;bobView(Lcom/mojang/blaze3d/vertex/PoseStack;F)V")
// )
// private void applyNightmareCameraShake(GameRenderer instance, PoseStack poseStack, float partialTick) {
// instance.bobView(poseStack, partialTick);
// if (minecraft.level == null || minecraft.player == null) {
// return;
// }
//
// Vec3 cameraPos = minecraft.gameRenderer.getMainCamera().getPosition();
// float shakeStrength = 0.0F;
//
// for (NightmareEntity nightmare : minecraft.level.getEntitiesOfClass(
// NightmareEntity.class,
// minecraft.player.getBoundingBox().inflate(MAX_SHAKE_RANGE),
// e -> e.isAlive() && e.isRoaring()
// )) {
// double distance = Math.sqrt(cameraPos.distanceToSqr(nightmare.position().add(0.0D, nightmare.getBbHeight() * 0.5D, 0.0D)));
// if (distance > MAX_SHAKE_RANGE) {
// continue;
// }
// shakeStrength += (float) ((1.0D - distance / MAX_SHAKE_RANGE) * 1.35D);
// }
//
// if (shakeStrength <= 0.0F) {
// return;
// }
//
// float time = (float) (minecraft.player.tickCount + partialTick);
// float yawOffset = Mth.sin(time * 1.85F) * shakeStrength * 1.8F;
// float pitchOffset = Mth.cos(time * 2.15F) * shakeStrength * 1.45F;
// float rollOffset = Mth.sin(time * 2.65F) * shakeStrength * 1.05F;
//
// poseStack.mulPose(Axis.YP.rotationDegrees(yawOffset));
// poseStack.mulPose(Axis.XP.rotationDegrees(pitchOffset));
// poseStack.mulPose(Axis.ZP.rotationDegrees(rollOffset));
// }
}
@@ -0,0 +1,40 @@
package com.craisinlord.antarchy.fabric.mixin.infinity;
import com.craisinlord.antarchy.config.AntarchySettings;
import net.minecraft.core.BlockPos;
import net.minecraft.core.component.DataComponents;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.item.ItemEntity;
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.block.NetherPortalBlock;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(value = NetherPortalBlock.class, priority = 2000)
public abstract class PortalCreatorMixin {
@Inject(
method = "entityInside",
at = @At("HEAD"),
cancellable = true
)
private void antarchy$blockInfinityBookActivation(BlockState state, Level level, BlockPos pos, Entity entity, CallbackInfo ci) {
if (!AntarchySettings.disableInfinityBookPortalCreation()) {
return;
}
if (!(entity instanceof ItemEntity itemEntity) || itemEntity.isRemoved()) {
return;
}
ItemStack stack = itemEntity.getItem();
if (!stack.has(DataComponents.WRITABLE_BOOK_CONTENT) && !stack.has(DataComponents.WRITTEN_BOOK_CONTENT)) {
return;
}
ci.cancel();
}
}
@@ -0,0 +1,37 @@
package com.craisinlord.antarchy.fabric.mixin.infinity;
import net.lerariemann.infinity.dimensions.features.fluid_springs.RandomSpring;
import net.lerariemann.infinity.util.core.ConfigType;
import net.lerariemann.infinity.util.core.RandomProvider;
import net.minecraft.nbt.CompoundTag;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
import java.util.Random;
@Mixin(value = RandomSpring.class, remap = false)
/*
* Skips bad fluid picks for Infinity random springs on Fabric.
*/
public abstract class RandomSpringMixin {
@Redirect(
method = "feature",
at = @At(
value = "INVOKE",
target = "Lnet/lerariemann/infinity/util/core/RandomProvider;randomName(Ljava/util/Random;Lnet/lerariemann/infinity/util/core/ConfigType;)Ljava/lang/String;"
)
)
private String antarchy$useFluidIdForSpring(RandomProvider provider, Random random, ConfigType configType) {
if (configType != ConfigType.FLUIDS) {
return provider.randomName(random, configType);
}
CompoundTag fluidTag = provider.randomElement(random, configType);
String fluidId = fluidTag.getString("fluidName");
if (!fluidId.isEmpty()) {
return fluidId;
}
return fluidTag.getString("Name");
}
}
@@ -0,0 +1,38 @@
package com.craisinlord.antarchy.fabric.network;
import com.craisinlord.antarchy.content.client.BloodglassClientState;
import com.craisinlord.antarchy.content.client.BrutalflyElytraClientState;
import com.craisinlord.antarchy.content.client.ScorpionWhipTetherClientState;
import com.craisinlord.antarchy.content.client.ThoraxisWeatherClientState;
import com.craisinlord.antarchy.content.network.*;
import com.craisinlord.antarchy.content.weather.ThoraxisWeatherKind;
import com.craisinlord.antarchy.fabric.client.BloodCrystalKatanaTrailClientState;
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking;
import net.minecraft.resources.ResourceLocation;
public final class AntarchyFabricClientNetworking {
private AntarchyFabricClientNetworking() {
}
public static void register() {
ClientPlayNetworking.registerGlobalReceiver(GravityStatePayload.TYPE, (payload, context) ->
context.client().execute(() -> AntarchyFabricNetworking.handleGravityState(context.player(), payload)));
ClientPlayNetworking.registerGlobalReceiver(BloodglassStatePayload.TYPE, (payload, context) ->
context.client().execute(() -> BloodglassClientState.update(payload.shieldsActive(), payload.shieldsMax())));
ClientPlayNetworking.registerGlobalReceiver(BloodCrystalKatanaTrailPayload.TYPE, (payload, context) ->
context.client().execute(() -> BloodCrystalKatanaTrailClientState.trigger(payload.entityId(), payload.durationTicks())));
ClientPlayNetworking.registerGlobalReceiver(ScorpionWhipTetherPayload.TYPE, (payload, context) ->
context.client().execute(() -> ScorpionWhipTetherClientState.update(payload.playerId(), payload.targetId())));
ClientPlayNetworking.registerGlobalReceiver(BrutalflyElytraAnimationPayload.TYPE, (payload, context) ->
context.client().execute(() -> BrutalflyElytraClientState.trigger(payload.entityId(), payload.durationTicks(), payload.strength())));
ClientPlayNetworking.registerGlobalReceiver(ThoraxisWeatherPayload.TYPE, (payload, context) ->
context.client().execute(() -> ThoraxisWeatherClientState.apply(
ResourceLocation.parse(payload.dimensionId()),
ThoraxisWeatherKind.byId(payload.weatherId()),
payload.expiresAt(),
payload.anchorX(),
payload.anchorY(),
payload.anchorZ()
)));
}
}
@@ -0,0 +1,265 @@
package com.craisinlord.antarchy.fabric.network;
import com.craisinlord.antarchy.content.entity.DiamondMinecartEntity;
import com.craisinlord.antarchy.content.entity.multipart.MultipartEntityOwner;
import com.craisinlord.antarchy.content.entity.multipart.network.MultipartAttackPayload;
import com.craisinlord.antarchy.content.entity.multipart.network.MultipartInteractPayload;
import com.craisinlord.antarchy.content.gravity.AntarchyGravityApi;
import com.craisinlord.antarchy.content.item.BrutalflyElytraFlightHelper;
import com.craisinlord.antarchy.content.item.BrutalflyElytraItem;
import com.craisinlord.antarchy.content.item.GravityGunItem;
import com.craisinlord.antarchy.content.network.*;
import com.craisinlord.antarchy.content.weather.ThoraxisWeatherSnapshot;
import net.fabricmc.fabric.api.networking.v1.PayloadTypeRegistry;
import net.fabricmc.fabric.api.networking.v1.PlayerLookup;
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking;
import net.minecraft.network.protocol.game.ClientboundSetEntityMotionPacket;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.sounds.SoundSource;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EquipmentSlot;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.phys.Vec3;
public final class AntarchyFabricNetworking {
private AntarchyFabricNetworking() {
}
public static void register() {
registerPayloadTypes();
registerServerReceivers();
}
private static void registerPayloadTypes() {
PayloadTypeRegistry.playS2C().register(GravityStatePayload.TYPE, GravityStatePayload.STREAM_CODEC);
PayloadTypeRegistry.playS2C().register(BloodglassStatePayload.TYPE, BloodglassStatePayload.STREAM_CODEC);
PayloadTypeRegistry.playS2C().register(BloodCrystalKatanaTrailPayload.TYPE, BloodCrystalKatanaTrailPayload.STREAM_CODEC);
PayloadTypeRegistry.playS2C().register(ScorpionWhipTetherPayload.TYPE, ScorpionWhipTetherPayload.STREAM_CODEC);
PayloadTypeRegistry.playS2C().register(BrutalflyElytraAnimationPayload.TYPE, BrutalflyElytraAnimationPayload.STREAM_CODEC);
PayloadTypeRegistry.playS2C().register(ThoraxisWeatherPayload.TYPE, ThoraxisWeatherPayload.STREAM_CODEC);
PayloadTypeRegistry.playC2S().register(GravityGunPrimaryPayload.TYPE, GravityGunPrimaryPayload.STREAM_CODEC);
PayloadTypeRegistry.playC2S().register(GravityGunScrollPayload.TYPE, GravityGunScrollPayload.STREAM_CODEC);
PayloadTypeRegistry.playC2S().register(DiamondMinecartInputPayload.TYPE, DiamondMinecartInputPayload.STREAM_CODEC);
PayloadTypeRegistry.playC2S().register(BrutalflyElytraFlapPayload.TYPE, BrutalflyElytraFlapPayload.STREAM_CODEC);
PayloadTypeRegistry.playC2S().register(MultipartAttackPayload.TYPE, MultipartAttackPayload.STREAM_CODEC);
PayloadTypeRegistry.playC2S().register(MultipartInteractPayload.TYPE, MultipartInteractPayload.STREAM_CODEC);
}
private static void registerServerReceivers() {
ServerPlayNetworking.registerGlobalReceiver(GravityGunPrimaryPayload.TYPE, (payload, context) ->
context.server().execute(() -> handleGravityGunPrimary(context.player(), payload)));
ServerPlayNetworking.registerGlobalReceiver(GravityGunScrollPayload.TYPE, (payload, context) ->
context.server().execute(() -> handleGravityGunScroll(context.player(), payload)));
ServerPlayNetworking.registerGlobalReceiver(DiamondMinecartInputPayload.TYPE, (payload, context) ->
context.server().execute(() -> handleDiamondMinecartInput(context.player(), payload)));
ServerPlayNetworking.registerGlobalReceiver(BrutalflyElytraFlapPayload.TYPE, (payload, context) ->
context.server().execute(() -> handleBrutalflyFlap(context.player(), payload)));
ServerPlayNetworking.registerGlobalReceiver(MultipartAttackPayload.TYPE, (payload, context) ->
context.server().execute(() -> handleMultipartAttack(context.player(), payload)));
ServerPlayNetworking.registerGlobalReceiver(MultipartInteractPayload.TYPE, (payload, context) ->
context.server().execute(() -> handleMultipartInteract(context.player(), payload)));
}
public static void syncGravityToPlayer(ServerPlayer target, Entity entity) {
ServerPlayNetworking.send(target, new GravityStatePayload(
entity.getId(),
AntarchyGravityApi.getGravityDirection(entity),
AntarchyGravityApi.getPrevGravityDirection(entity),
AntarchyGravityApi.isGravityForced(entity),
AntarchyGravityApi.getTransitionDuration(entity),
AntarchyGravityApi.getTransitionRemaining(entity)
));
}
public static void syncGravityEntity(Entity entity) {
GravityStatePayload payload = new GravityStatePayload(
entity.getId(),
AntarchyGravityApi.getGravityDirection(entity),
AntarchyGravityApi.getPrevGravityDirection(entity),
AntarchyGravityApi.isGravityForced(entity),
AntarchyGravityApi.getTransitionDuration(entity),
AntarchyGravityApi.getTransitionRemaining(entity)
);
for (ServerPlayer player : PlayerLookup.tracking(entity)) {
ServerPlayNetworking.send(player, payload);
}
if (entity instanceof ServerPlayer player) {
ServerPlayNetworking.send(player, payload);
}
}
public static void syncBloodglass(ServerPlayer player, int shieldsActive, int shieldsMax) {
ServerPlayNetworking.send(player, new BloodglassStatePayload(shieldsActive, shieldsMax));
}
public static void syncKatanaTrail(Entity entity, int durationTicks) {
BloodCrystalKatanaTrailPayload payload = new BloodCrystalKatanaTrailPayload(entity.getId(), durationTicks);
for (ServerPlayer player : PlayerLookup.tracking(entity)) {
ServerPlayNetworking.send(player, payload);
}
if (entity instanceof ServerPlayer serverPlayer) {
ServerPlayNetworking.send(serverPlayer, payload);
}
}
public static void syncScorpionWhipTether(ServerPlayer player, int targetId) {
ScorpionWhipTetherPayload payload = new ScorpionWhipTetherPayload(player.getId(), targetId);
for (ServerPlayer tracking : PlayerLookup.tracking(player)) {
ServerPlayNetworking.send(tracking, payload);
}
ServerPlayNetworking.send(player, payload);
}
public static void syncThoraxisWeather(ServerLevel level, ThoraxisWeatherSnapshot snapshot) {
ThoraxisWeatherPayload payload = new ThoraxisWeatherPayload(
snapshot.dimension(),
snapshot.kind(),
snapshot.expiresAt(),
snapshot.anchor().getX(),
snapshot.anchor().getY(),
snapshot.anchor().getZ()
);
for (ServerPlayer player : level.players()) {
ServerPlayNetworking.send(player, payload);
}
}
public static void handleGravityState(Entity contextPlayer, GravityStatePayload payload) {
Entity entity = contextPlayer.level().getEntity(payload.entityId());
if (entity == null && contextPlayer.getId() == payload.entityId()) {
entity = contextPlayer;
}
if (entity == null) {
return;
}
AntarchyGravityApi.applySyncedState(
entity,
payload.direction(),
payload.previousDirection(),
payload.forced(),
payload.transitionDuration(),
payload.transitionRemaining()
);
}
private static void handleGravityGunPrimary(ServerPlayer player, GravityGunPrimaryPayload payload) {
if (!(player.getMainHandItem().getItem() instanceof GravityGunItem gravityGunItem)) {
return;
}
gravityGunItem.firePrimary(player.serverLevel(), player, player.getMainHandItem());
}
private static void handleGravityGunScroll(ServerPlayer player, GravityGunScrollPayload payload) {
if (!(player.getMainHandItem().getItem() instanceof GravityGunItem)) {
return;
}
GravityGunItem.adjustHeldDistance(player.getMainHandItem(), payload.distanceDelta());
}
private static void handleDiamondMinecartInput(ServerPlayer player, DiamondMinecartInputPayload payload) {
if (player.getVehicle() instanceof DiamondMinecartEntity cart) {
cart.onInputReceived(payload.inputFlags());
}
}
private static void handleBrutalflyFlap(ServerPlayer player, BrutalflyElytraFlapPayload payload) {
ItemStack chestStack = player.getItemBySlot(EquipmentSlot.CHEST);
if (!(chestStack.getItem() instanceof BrutalflyElytraItem)) {
return;
}
if (player.isSpectator() || !player.isFallFlying() || player.isPassenger()) {
return;
}
if (player.getCooldowns().isOnCooldown(chestStack.getItem())) {
return;
}
BrutalflyElytraFlightHelper.FlapTier tier = BrutalflyElytraFlightHelper.resolveFlapTier(payload.chargeTicks());
if (tier == BrutalflyElytraFlightHelper.FlapTier.FAIL) {
return;
}
float lift = BrutalflyElytraFlightHelper.liftFor(tier);
float forwardBoost = BrutalflyElytraFlightHelper.forwardBoostFor(tier);
Vec3 look = player.getLookAngle();
Vec3 boost = new Vec3(look.x * forwardBoost, lift, look.z * forwardBoost);
player.setDeltaMovement(player.getDeltaMovement().add(boost));
player.hasImpulse = true;
player.hurtMarked = true;
player.resetFallDistance();
player.connection.send(new ClientboundSetEntityMotionPacket(player));
chestStack.hurtAndBreak(1, player, EquipmentSlot.CHEST);
player.getCooldowns().addCooldown(chestStack.getItem(), BrutalflyElytraFlightHelper.FLAP_COOLDOWN_TICKS);
player.level().playSound(null, player.blockPosition(), SoundEvents.PHANTOM_FLAP, SoundSource.PLAYERS, 0.9F,
tier == BrutalflyElytraFlightHelper.FlapTier.PERFECT ? 0.8F : tier == BrutalflyElytraFlightHelper.FlapTier.GOOD ? 0.95F : 1.1F);
BrutalflyElytraAnimationPayload animationPayload = new BrutalflyElytraAnimationPayload(
player.getId(),
BrutalflyElytraFlightHelper.animationTicksFor(tier),
lift
);
for (ServerPlayer tracking : PlayerLookup.tracking(player)) {
ServerPlayNetworking.send(tracking, animationPayload);
}
ServerPlayNetworking.send(player, animationPayload);
}
private static void handleMultipartAttack(ServerPlayer serverPlayer, MultipartAttackPayload payload) {
Entity entity = serverPlayer.serverLevel().getEntity(payload.parentId());
if (!(entity instanceof MultipartEntityOwner owner)) {
return;
}
Entity[] parts = owner.antarchy$getMultipartParts();
if (parts == null) {
parts = owner.antarchy$createMultipartParts();
owner.antarchy$setMultipartParts(parts);
owner.antarchy$syncMultipartParts();
}
if (payload.partIndex() < 0 || payload.partIndex() >= parts.length) {
return;
}
Entity part = parts[payload.partIndex()];
if (part == null || part.isRemoved()) {
return;
}
if (serverPlayer.distanceToSqr(part) > 10000.0D) {
return;
}
DamageSource source = serverPlayer.level().damageSources().playerAttack(serverPlayer);
owner.antarchy$hurtMultipartPart(part, source, payload.damage());
}
private static void handleMultipartInteract(ServerPlayer serverPlayer, MultipartInteractPayload payload) {
Entity entity = serverPlayer.serverLevel().getEntity(payload.parentId());
if (!(entity instanceof MultipartEntityOwner owner)) {
return;
}
Entity[] parts = owner.antarchy$getMultipartParts();
if (parts == null) {
parts = owner.antarchy$createMultipartParts();
owner.antarchy$setMultipartParts(parts);
owner.antarchy$syncMultipartParts();
}
if (payload.partIndex() < 0 || payload.partIndex() >= parts.length) {
return;
}
Entity part = parts[payload.partIndex()];
if (part == null || part.isRemoved()) {
return;
}
InteractionHand hand = payload.handId() == 0 ? InteractionHand.MAIN_HAND : InteractionHand.OFF_HAND;
owner.antarchy$interactMultipartPart(part, serverPlayer, part.position(), hand);
}
}
@@ -0,0 +1,11 @@
package com.craisinlord.antarchy.fabric.registry;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.block.Block;
public final class DeferredBlock<T extends Block> extends DeferredHolder<Block, T> {
DeferredBlock(ResourceKey<?> registryKey, ResourceLocation id) {
super(registryKey, id);
}
}
@@ -0,0 +1,37 @@
package com.craisinlord.antarchy.fabric.registry;
import java.util.function.Supplier;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
public class DeferredHolder<R, T extends R> implements Supplier<T> {
private final ResourceKey<?> registryKey;
private final ResourceLocation id;
private T value;
DeferredHolder(ResourceKey<?> registryKey, ResourceLocation id) {
this.registryKey = registryKey;
this.id = id;
}
void bind(T value) {
this.value = value;
}
public ResourceLocation getId() {
return this.id;
}
@SuppressWarnings("unchecked")
public ResourceKey<T> getKey() {
return (ResourceKey<T>) ResourceKey.create((ResourceKey) this.registryKey, this.id);
}
@Override
public T get() {
if (this.value == null) {
throw new IllegalStateException("Registry object not yet bound: " + this.id);
}
return this.value;
}
}
@@ -0,0 +1,11 @@
package com.craisinlord.antarchy.fabric.registry;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.Item;
public final class DeferredItem<T extends Item> extends DeferredHolder<Item, T> {
DeferredItem(ResourceKey<?> registryKey, ResourceLocation id) {
super(registryKey, id);
}
}
@@ -0,0 +1,117 @@
package com.craisinlord.antarchy.fabric.registry;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.Supplier;
import net.minecraft.core.Registry;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.BlockItem;
import net.minecraft.world.item.Item;
import net.minecraft.world.level.block.Block;
public class DeferredRegister<T> {
public static final class Blocks extends DeferredRegister<Block> {
private Blocks(String modId) {
super(net.minecraft.core.registries.Registries.BLOCK, modId);
}
public <I extends Block> DeferredBlock<I> register(String name, Supplier<? extends I> supplier) {
return this.registerTyped(name, supplier, DeferredBlock::new);
}
}
public static final class Items extends DeferredRegister<Item> {
private Items(String modId) {
super(net.minecraft.core.registries.Registries.ITEM, modId);
}
public <I extends Item> DeferredItem<I> register(String name, Supplier<? extends I> supplier) {
return this.registerTyped(name, supplier, DeferredItem::new);
}
public DeferredItem<Item> registerSimpleItem(String name) {
return this.registerSimpleItem(name, new Item.Properties());
}
public DeferredItem<Item> registerSimpleItem(String name, Item.Properties properties) {
return this.register(name, () -> new Item(properties));
}
public DeferredItem<BlockItem> registerSimpleBlockItem(DeferredBlock<? extends Block> block) {
return this.register(block.getId().getPath(), () -> new BlockItem(block.get(), new Item.Properties()));
}
}
@FunctionalInterface
protected interface HolderFactory<R, T extends R, H extends DeferredHolder<R, T>> {
H create(ResourceKey<?> registryKey, ResourceLocation id);
}
private record PendingEntry<T>(ResourceLocation id, Supplier<? extends T> supplier, DeferredHolder<T, ? extends T> holder) {
}
private final ResourceKey<? extends Registry<T>> registryKey;
private final String modId;
private final List<PendingEntry<T>> entries = new ArrayList<>();
protected DeferredRegister(ResourceKey<? extends Registry<T>> registryKey, String modId) {
this.registryKey = registryKey;
this.modId = modId;
}
public static Blocks createBlocks(String modId) {
return new Blocks(modId);
}
public static Items createItems(String modId) {
return new Items(modId);
}
public static <T> DeferredRegister<T> create(ResourceKey<? extends Registry<T>> registryKey, String modId) {
return new DeferredRegister<>(registryKey, modId);
}
public <I extends T> DeferredHolder<T, I> register(String name, Supplier<? extends I> supplier) {
return this.registerTyped(name, supplier, DeferredHolder::new);
}
protected <I extends T, H extends DeferredHolder<T, I>> H registerTyped(String name, Supplier<? extends I> supplier, HolderFactory<T, I, H> factory) {
ResourceLocation id = ResourceLocation.fromNamespaceAndPath(this.modId, name);
H holder = factory.create(this.registryKey, id);
this.entries.add(new PendingEntry<>(id, supplier, holder));
return holder;
}
public List<DeferredHolder<T, ? extends T>> getEntries() {
List<DeferredHolder<T, ? extends T>> holders = new ArrayList<>(this.entries.size());
for (PendingEntry<T> entry : this.entries) {
holders.add(entry.holder());
}
return Collections.unmodifiableList(holders);
}
public void register() {
Registry<T> registry = resolveRegistry(this.registryKey);
for (PendingEntry<T> entry : this.entries) {
T value = Registry.register(registry, entry.id(), entry.supplier().get());
bind(entry.holder(), value);
}
}
@SuppressWarnings({"unchecked", "rawtypes"})
private static <T> void bind(DeferredHolder<T, ? extends T> holder, T value) {
((DeferredHolder) holder).bind(value);
}
@SuppressWarnings("unchecked")
private static <T> Registry<T> resolveRegistry(ResourceKey<? extends Registry<T>> registryKey) {
Registry<?> registry = BuiltInRegistries.REGISTRY.get(registryKey.location());
if (registry == null) {
throw new IllegalStateException("Could not resolve registry: " + registryKey.location());
}
return (Registry<T>) registry;
}
}
@@ -0,0 +1,31 @@
{
"required": true,
"package": "com.craisinlord.antarchy.fabric.mixin",
"refmap": "antarchy.refmap.json",
"compatibilityLevel": "JAVA_21",
"mixins": [
"AntiwaterLivingEntityMixin",
"AntiwaterItemEntityMixin",
"AntimetalInversionMixin",
"AntimetalScaffoldingMixin",
"ElytraStartFlyingMixin",
"ElytraFallFlyingMixin",
"BloodglassEquipMixin",
"BloodglassEffectMixin",
"BloodglassRespawnMixin",
"DreamCampfireBlockEntityMixin",
"infinity.RandomSpringMixin",
"infinity.PortalCreatorMixin"
],
"client": [
"client.NightmareCameraShakeMixin",
"client.DuctTapeInputMixin",
"client.AntiwaterFogMixin",
"client.GravityGunMouseScrollMixin",
"client.CrossbowArmPoseMixin"
],
"injectors": {
"defaultRequire": 1
},
"minVersion": "0.8"
}
@@ -0,0 +1,6 @@
{
"parent": "minecraft:item/generated",
"textures": {
"layer0": "antarchy:item/antiwater_bucket"
}
}
@@ -0,0 +1,3 @@
{
"parent": "antarchy:item/battle_axe_held"
}
@@ -0,0 +1,3 @@
{
"parent": "antarchy:item/big_bertha_held"
}
@@ -0,0 +1,3 @@
{
"parent": "antarchy:item/gravity_gun_held_geo"
}
@@ -0,0 +1,3 @@
{
"parent": "antarchy:item/grow_ray_held_geo"
}
@@ -0,0 +1,6 @@
{
"parent": "minecraft:item/generated",
"textures": {
"layer0": "antarchy:item/ichor_bucket"
}
}
@@ -0,0 +1,3 @@
{
"parent": "antarchy:item/shrink_ray_held_geo"
}
@@ -0,0 +1,3 @@
{
"parent": "antarchy:item/squidzooka_held_geo"
}
+38
View File
@@ -0,0 +1,38 @@
{
"schemaVersion": 1,
"id": "${mod_id}",
"version": "${version}",
"name": "${mod_name}",
"description": "${mod_description}",
"authors": [
"${mod_author}"
],
"contact": {
"homepage": "${homepage_url}",
"sources": "${sources_url}",
"issues": "${issues_url}"
},
"license": "${license}",
"environment": "*",
"icon": "${logo_fabric}",
"entrypoints": {
"main": [
"com.craisinlord.antarchy.fabric.AntarchyFabric"
],
"client": [
"com.craisinlord.antarchy.fabric.client.AntarchyFabricClient"
]
},
"mixins": [
"antarchy-common.mixins.json",
"antarchy-fabric.mixins.json"
],
"depends": {
"fabricloader": ">=${fabric_loader_version}",
"minecraft": "~${minecraft_version}",
"java": ">=21",
"fabric-api": "*",
"integrated_api": "*",
"geckolib": "*"
}
}