anthology split

This commit is contained in:
craisinlord committed 2026-10-12 19:55:08 -05:00
1 parent bba2a56671
commit f7e7bebac3
244 files changed
+452 -11595

No files matched your search

@@ -1,7 +1,7 @@
package com.craisinlord.antarchy;
import com.craisinlord.antarchy.content.computer.AntarchyArchiveLocators;
import com.craisinlord.antarchy.content.movement.AntarchyGravityHooks;
import com.craisinlord.antarchy.content.AntarchyAnthologyHooks;
import com.craisinlord.antarchy.content.util.AttributeRangeInit;
import com.craisinlord.antarchy.content.worldgen.VillagePoolAdditions;
import com.craisinlord.integrated_api.events.lifecycle.ServerGoingToStartEvent;
@@ -18,7 +18,7 @@ public class Antarchy {
public static void init() {
AttributeRangeInit.apply();
AntarchyArchiveLocators.register();
AntarchyGravityHooks.register();
AntarchyAnthologyHooks.register();
ServerGoingToStartEvent.EVENT.addListener(Antarchy::serverAboutToStart);
}
private static void serverAboutToStart(final ServerGoingToStartEvent event) {
@@ -156,7 +156,6 @@ public final class AntarchyConfigCatalog {
|| name.equals("experimentalSettingsPopupDisabled")
|| name.equals("unlockAllArchives")
|| name.equals("entitySpecificFireOverlayEnabled")
|| name.equals("dreamSandEnabled")
|| name.startsWith("dreamSand")
|| name.equals("ichorWitherEnabled")
|| name.startsWith("diamondMinecart")
@@ -175,11 +175,7 @@ public final class AntarchySettings {
private static double americanBonusSaturation = 1.0D;
private static int americanRegenerationDurationTicks = 60;
private static int americanRegenerationAmplifier = 0;
private static boolean dreamSandEnabled = true;
private static boolean entitySpecificFireOverlayEnabled = true;
private static double dreamSandJumpVelocityMultiplier = 1.3D;
private static double dreamSandGravityMultiplier = 0.45D;
private static double dreamSandEffectDurationSeconds = 7.0D;
private static double dreamSandFallingBlockGravityMultiplier = 0.4D;
private static boolean ichorWitherEnabled = true;
private static int nightmareHelmetArmorValue = 4;
@@ -1926,46 +1922,14 @@ public final class AntarchySettings {
americanRegenerationAmplifier = value;
}
public static boolean dreamSandEnabled() {
return dreamSandEnabled;
}
public static boolean entitySpecificFireOverlayEnabled() {
return entitySpecificFireOverlayEnabled;
}
public static void setDreamSandEnabled(boolean value) {
dreamSandEnabled = value;
}
public static void setEntitySpecificFireOverlayEnabled(boolean value) {
entitySpecificFireOverlayEnabled = value;
}
public static double dreamSandJumpVelocityMultiplier() {
return dreamSandJumpVelocityMultiplier;
}
public static void setDreamSandJumpVelocityMultiplier(double value) {
dreamSandJumpVelocityMultiplier = value;
}
public static double dreamSandGravityMultiplier() {
return dreamSandGravityMultiplier;
}
public static void setDreamSandGravityMultiplier(double value) {
dreamSandGravityMultiplier = value;
}
public static double dreamSandEffectDurationSeconds() {
return dreamSandEffectDurationSeconds;
}
public static void setDreamSandEffectDurationSeconds(double value) {
dreamSandEffectDurationSeconds = value;
}
public static double dreamSandFallingBlockGravityMultiplier() {
return dreamSandFallingBlockGravityMultiplier;
}
@@ -0,0 +1,199 @@
package com.craisinlord.antarchy.content;
import net.minecraft.world.item.ItemStack;
import net.minecraft.client.player.LocalPlayer;
import java.util.UUID;
import com.craisinlord.anthology.portal.client.PortalClientHooks;
import com.craisinlord.anthology.portal.PortalSettings;
import com.craisinlord.anthology.portal.PortalHooks;
import com.craisinlord.anthology.portal.PortalEntity;
import com.craisinlord.antarchy.content.portalgun.PortalGunProjectileEntity;
import com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity;
import com.craisinlord.antarchy.content.item.PortalGunItem;
import com.craisinlord.antarchy.content.client.renderer.PortalGunMoonViewRenderer;
import com.craisinlord.antarchy.config.AntarchySettings;
import com.craisinlord.antarchy.content.item.BigBerthaItem;
import com.craisinlord.antarchy.content.client.HerculesBeetleImpactShakeClientState;
import com.craisinlord.anthology.shake.client.CameraShake;
import com.craisinlord.antarchy.Antarchy;
import com.craisinlord.antarchy.content.effect.RoyalEffectEligibility;
import com.craisinlord.antarchy.content.enchantment.AntarchyEnchantments;
import com.craisinlord.antarchy.content.entity.BedBugEntity;
import com.craisinlord.antarchy.content.entity.portal.DimensionalTearEntity;
import com.craisinlord.antarchy.content.entity.royal.QueenEntity;
import com.craisinlord.antarchy.content.entity.royal.RoyalBlackHoleEntity;
import com.craisinlord.antarchy.content.fluid.AntarchyFluidChecks;
import com.craisinlord.antarchy.content.item.TemporalTunerAccess;
import com.craisinlord.antarchy.content.item.TemporalTunerItem;
import com.craisinlord.antarchy.content.worldgen.thoraxis.ThoraxisUndersideManager;
import com.craisinlord.anthology.effect.AnthologyEffects;
import com.craisinlord.anthology.gravity.GravityApi;
import com.craisinlord.anthology.gravity.GravityHooks;
import com.craisinlord.anthology.time.TimeDilationApi;
import com.craisinlord.anthology.time.TimeDilationFieldEntity;
import com.craisinlord.anthology.time.TimeDilationHooks;
import com.craisinlord.anthology.time.TimeDilationMath;
import com.craisinlord.anthology.time.client.ContractionAfterimages;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.effect.MobEffectInstance;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.Mob;
import net.minecraft.world.entity.player.Player;
public final class AntarchyAnthologyHooks {
private AntarchyAnthologyHooks() {
}
public static void register() {
GravityHooks.addInvertedRegion(ThoraxisUndersideManager::isInUnderside);
GravityHooks.addInvertedFluid(AntarchyFluidChecks::isAntiwater);
GravityHooks.addLoadListener(AntarchyAnthologyHooks::clearStaleUndersideInversion);
TimeDilationHooks.addTrackedPredicate(entity -> entity instanceof Player player && TemporalTunerItem.isAvailable(player));
TimeDilationHooks.addRateSource(AntarchyAnthologyHooks::temporalTunerRate);
TimeDilationHooks.addFieldExclusion(AntarchyAnthologyHooks::excludedFromField);
TimeDilationHooks.addChronosphereLevelSource(AntarchyEnchantments::chronosphereLevel);
PortalHooks.setSettings(new AntarchyPortalSettings());
PortalHooks.addTransitExclusion(entity -> entity instanceof PortalGunProjectileEntity);
}
public static void registerClient() {
PortalClientHooks.registerCustomView(portal -> portal instanceof PortalGunPortalEntity gunPortal && gunPortal.isMoonPortal(), PortalGunMoonViewRenderer::render);
PortalClientHooks.setOutlinePolicy(AntarchyAnthologyHooks::outlinesPortal);
CameraShake.addStrengthSource(cameraPos -> HerculesBeetleImpactShakeClientState.getTicks() / 25.0F * 2.0F);
CameraShake.addStrengthSource(cameraPos -> BigBerthaItem.clientShakeTicks / 25.0F * 2.0F);
ContractionAfterimages.addActivator(living -> living instanceof Player player
&& TemporalTunerItem.isAvailable(player)
&& TimeDilationApi.getRate(living) > 1.0D);
}
private static boolean outlinesPortal(PortalEntity portal, LocalPlayer viewer) {
if (!(portal instanceof PortalGunPortalEntity gunPortal) || gunPortal.getOwnerId() == null) {
return false;
}
UUID owner = gunPortal.getOwnerId();
return owner.equals(viewer.getUUID())
|| owner.equals(heldGunOwner(viewer.getMainHandItem(), viewer.getUUID()))
|| owner.equals(heldGunOwner(viewer.getOffhandItem(), viewer.getUUID()));
}
private static UUID heldGunOwner(ItemStack stack, UUID fallback) {
return stack.getItem() instanceof PortalGunItem ? PortalGunItem.getPortalOwnerId(stack, fallback) : null;
}
private static final class AntarchyPortalSettings implements PortalSettings {
@Override
public boolean seeThroughEnabled() {
return AntarchySettings.portalGunSeeThroughPortals();
}
@Override
public int maxLiveViews() {
return AntarchySettings.portalGunMaxLiveViews();
}
@Override
public double minFloorExitSpeed() {
return AntarchySettings.portalGunMinFloorExitSpeed();
}
@Override
public double maxExitSpeed() {
return AntarchySettings.portalGunMaxExitSpeed();
}
@Override
public boolean fancySurfaces() {
return AntarchySettings.portalGunFancyPortals();
}
}
private static double temporalTunerRate(Entity entity) {
if (!(entity instanceof Player player) || !(player instanceof TemporalTunerAccess access)) {
return TimeDilationMath.NORMAL_RATE;
}
if (TemporalTunerItem.isAvailable(player)) {
return TemporalTunerItem.getRate(player);
}
access.antarchy$setTemporalTunerRate(TimeDilationMath.NORMAL_RATE);
TemporalTunerItem.syncTooltipRate(player, TimeDilationMath.NORMAL_RATE);
return TimeDilationMath.NORMAL_RATE;
}
private static boolean excludedFromField(TimeDilationFieldEntity field, Entity entity) {
if (!field.isOwnerAnchored() && entity instanceof RoyalBlackHoleEntity) {
return true;
}
if (!(field.owner() instanceof QueenEntity)) {
return false;
}
if (entity instanceof LivingEntity living && !RoyalEffectEligibility.canApplyDilated(living)) {
return true;
}
return !field.isOwnerAnchored() && entity.getType().is(AntarchyTags.Entities.QUEEN_DOES_NOT_ATTACK);
}
private static void clearStaleUndersideInversion(Entity entity) {
if (!(entity instanceof Mob mob)
|| !GravityApi.isGravityInverted(mob)
|| !GravityApi.isGravityForced(mob)) {
return;
}
MobEffectInstance invertedEffect = mob.getEffect(AnthologyEffects.inverted());
boolean aboveUndersideExit = ThoraxisUndersideManager.isAboveUndersideExit(mob);
int nearbyDimensionalTears = countNearbyDimensionalTears(mob);
if (invertedEffect != null
&& invertedEffect.isAmbient()
&& !invertedEffect.isVisible()
&& aboveUndersideExit) {
clearInversion(mob);
Antarchy.LOGGER.warn(
"[antarchy-gravity] cleared stale underside inversion from loaded mob type={} uuid={} pos=({}, {}, {}) dim={} effectDuration={} nearbyDimensionalTears={}",
mob.getType(), mob.getUUID(), mob.getX(), mob.getY(), mob.getZ(),
mob.level() != null ? mob.level().dimension().location() : "?",
invertedEffect.getDuration(), nearbyDimensionalTears
);
return;
}
if (invertedEffect != null
&& mob instanceof BedBugEntity
&& aboveUndersideExit
&& ThoraxisUndersideManager.isThoraxis(mob.level())) {
clearInversion(mob);
Antarchy.LOGGER.warn(
"[antarchy-gravity] cleared loaded inverted bed bug above underside exit type={} uuid={} pos=({}, {}, {}) dim={} effectDuration={} effectAmbient={} effectVisible={} nearbyDimensionalTears={}",
mob.getType(), mob.getUUID(), mob.getX(), mob.getY(), mob.getZ(),
mob.level() != null ? mob.level().dimension().location() : "?",
invertedEffect.getDuration(), invertedEffect.isAmbient(), invertedEffect.isVisible(), nearbyDimensionalTears
);
return;
}
Antarchy.LOGGER.info(
"[antarchy-gravity] loaded inverted mob type={} uuid={} pos=({}, {}, {}) dim={} hasInvertedEffect={} effectDuration={} effectAmbient={} effectVisible={} aboveUndersideExit={} nearbyDimensionalTears={}",
mob.getType(), mob.getUUID(), mob.getX(), mob.getY(), mob.getZ(),
mob.level() != null ? mob.level().dimension().location() : "?",
invertedEffect != null,
invertedEffect != null ? invertedEffect.getDuration() : -1,
invertedEffect != null && invertedEffect.isAmbient(),
invertedEffect != null && invertedEffect.isVisible(),
aboveUndersideExit,
nearbyDimensionalTears
);
}
private static void clearInversion(Mob mob) {
mob.removeEffect(AnthologyEffects.inverted());
GravityApi.resetGravityState(mob);
}
private static int countNearbyDimensionalTears(Mob mob) {
if (!(mob.level() instanceof ServerLevel serverLevel)) {
return -1;
}
return serverLevel.getEntitiesOfClass(
DimensionalTearEntity.class,
mob.getBoundingBox().inflate(48.0D),
Entity::isAlive
).size();
}
}
@@ -32,7 +32,6 @@ import com.craisinlord.antarchy.content.entity.lucid.LucidEntity;
import com.craisinlord.antarchy.content.entity.vortex.VortexChargeProjectileEntity;
import com.craisinlord.antarchy.content.entity.vortex.VortexEntity;
import com.craisinlord.antarchy.content.entity.vortex.WindVortexEntity;
import com.craisinlord.antarchy.content.time.TimeDilationFieldEntity;
import com.craisinlord.antarchy.content.entity.nightmare.NightmareEntity;
import com.craisinlord.antarchy.content.entity.nightmare.NightmareBiteEntity;
import com.craisinlord.antarchy.content.entity.nightmare.NightmarePortalEntity;
@@ -97,8 +96,6 @@ public final class AntarchyObjects {
@SuppressWarnings("unchecked")
public static Supplier<EntityType<VortexChargeProjectileEntity>> VORTEX_CHARGE_PROJECTILE = (Supplier<EntityType<VortexChargeProjectileEntity>>) UNBOUND;
@SuppressWarnings("unchecked")
public static Supplier<EntityType<TimeDilationFieldEntity>> TIME_DILATION_FIELD = (Supplier<EntityType<TimeDilationFieldEntity>>) UNBOUND;
@SuppressWarnings("unchecked")
public static Supplier<EntityType<com.craisinlord.antarchy.content.entity.royal.RoyalBlackHoleEntity>> ROYAL_BLACK_HOLE = (Supplier<EntityType<com.craisinlord.antarchy.content.entity.royal.RoyalBlackHoleEntity>>) UNBOUND;
@SuppressWarnings("unchecked")
public static Supplier<EntityType<com.craisinlord.antarchy.content.entity.royal.RoyalAssailantBlackHoleEntity>> ROYAL_ASSAILANT_BLACK_HOLE = (Supplier<EntityType<com.craisinlord.antarchy.content.entity.royal.RoyalAssailantBlackHoleEntity>>) UNBOUND;
@@ -698,10 +695,6 @@ public final class AntarchyObjects {
VORTEX_CHARGE_PROJECTILE = supplier;
}
public static void setTimeDilationField(Supplier<EntityType<TimeDilationFieldEntity>> supplier) {
TIME_DILATION_FIELD = supplier;
}
public static void setRoyalBlackHole(Supplier<EntityType<com.craisinlord.antarchy.content.entity.royal.RoyalBlackHoleEntity>> supplier) {
ROYAL_BLACK_HOLE = supplier;
}
@@ -1,5 +1,6 @@
package com.craisinlord.antarchy.content;
import com.craisinlord.anthology.time.TimeDilationTags;
import com.craisinlord.antarchy.Antarchy;
import net.minecraft.core.registries.Registries;
import net.minecraft.resources.ResourceLocation;
@@ -84,7 +85,7 @@ public final class AntarchyTags {
public static final TagKey<EntityType<?>> LUCID_ANCHOR_IMMUNE = TagKey.create(Registries.ENTITY_TYPE, id("lucid_anchor_immune"));
public static final TagKey<EntityType<?>> DIMENSIONAL_TEAR_COMMON_SPAWNS = TagKey.create(Registries.ENTITY_TYPE, id("dimensional_tear_common_spawns"));
public static final TagKey<EntityType<?>> DIMENSIONAL_TEAR_RARE_SPAWNS = TagKey.create(Registries.ENTITY_TYPE, id("dimensional_tear_rare_spawns"));
public static final TagKey<EntityType<?>> TIME_DILATION_IMMUNE = TagKey.create(Registries.ENTITY_TYPE, id("time_dilation_immune"));
public static final TagKey<EntityType<?>> TIME_DILATION_IMMUNE = TimeDilationTags.TIME_DILATION_IMMUNE;
public static final TagKey<EntityType<?>> QUEEN_DOES_NOT_ATTACK = TagKey.create(Registries.ENTITY_TYPE, id("queen_does_not_attack"));
public static final TagKey<EntityType<?>> COMMANDED_BLACKLIST = TagKey.create(Registries.ENTITY_TYPE, id("commanded_blacklist"));
public static final TagKey<EntityType<?>> DILATED_BLACKLIST = TagKey.create(Registries.ENTITY_TYPE, id("dilated_blacklist"));
@@ -3,6 +3,7 @@ package com.craisinlord.antarchy.content.block.entity;
import com.craisinlord.antarchy.content.AntarchyObjects;
import com.craisinlord.antarchy.content.block.UndertrialSpawnerBlock;
import com.craisinlord.antarchy.Antarchy;
import com.craisinlord.anthology.effect.AnthologyEffects;
import net.minecraft.core.BlockPos;
import net.minecraft.core.particles.SimpleParticleType;
import net.minecraft.core.Direction;
@@ -116,7 +117,7 @@ public final class UndertrialSpawnerBlockEntity extends BlockEntity implements T
mob.addEffect(new MobEffectInstance(AntarchyObjects.INVERTED_EFFECT.get(), EFFECT_DURATION, 0, false, true, true));
int roll = mob.getRandom().nextInt(100);
if (roll < 30) {
mob.addEffect(new MobEffectInstance(effect("contracted"), EFFECT_DURATION, 0, false, true, true));
mob.addEffect(new MobEffectInstance(AnthologyEffects.contracted(), EFFECT_DURATION, 0, false, true, true));
} else if (roll < 50) {
mob.addEffect(new MobEffectInstance(effect("shrinking"), EFFECT_DURATION, 0, false, true, true));
} else if (roll < 65) {
@@ -1,92 +0,0 @@
package com.craisinlord.antarchy.content.client;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.function.BooleanSupplier;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.phys.Vec3;
public final class CameraShakeClientState {
public static final double TORETERROR_RANGE = 48.0D;
public static final float TORETERROR_STRENGTH = 1.0F;
public static final double NIGHTMARE_RANGE = 32.0D;
public static final float NIGHTMARE_STRENGTH = 1.35F;
private record Source(double range, float strength, BooleanSupplier active) {
}
private record Impact(Vec3 position, float intensity, int remainingTicks, int totalTicks, float radius) {
private Impact ticked() {
return new Impact(position, intensity, remainingTicks - 1, totalTicks, radius);
}
}
private static final Map<LivingEntity, Source> SOURCES = new HashMap<>();
private static final java.util.List<Impact> IMPACTS = new java.util.ArrayList<>();
private CameraShakeClientState() {
}
public static void register(LivingEntity entity, double range, float strength, BooleanSupplier active) {
SOURCES.putIfAbsent(entity, new Source(range, strength, active));
}
public static float getStrength(Vec3 cameraPos) {
float total = 0.0F;
Iterator<Map.Entry<LivingEntity, Source>> iterator = SOURCES.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<LivingEntity, Source> entry = iterator.next();
LivingEntity entity = entry.getKey();
Source source = entry.getValue();
if (entity.isRemoved() || !entity.isAlive() || !source.active().getAsBoolean()) {
iterator.remove();
continue;
}
double distance = Math.sqrt(cameraPos.distanceToSqr(entity.position().add(0.0D, entity.getBbHeight() * 0.5D, 0.0D)));
if (distance > source.range()) {
continue;
}
total += (float) ((1.0D - distance / source.range()) * source.strength());
}
Iterator<Impact> impactIterator = IMPACTS.iterator();
while (impactIterator.hasNext()) {
Impact impact = impactIterator.next();
if (impact.remainingTicks() <= 0) {
impactIterator.remove();
continue;
}
double distance = Math.sqrt(cameraPos.distanceToSqr(impact.position()));
if (distance > impact.radius()) {
continue;
}
total += (float) ((1.0D - distance / impact.radius()) * impact.intensity() * (impact.remainingTicks() / (float) Math.max(1, impact.totalTicks())));
}
return total;
}
public static void triggerImpact(Vec3 position, float intensity, int durationTicks, float radius) {
int ticks = Math.max(1, durationTicks);
IMPACTS.add(new Impact(position, intensity, ticks, ticks, Math.max(1.0F, radius)));
}
public static void tick() {
for (int i = IMPACTS.size() - 1; i >= 0; i--) {
Impact impact = IMPACTS.get(i);
if (impact.remainingTicks() <= 1) {
IMPACTS.remove(i);
} else {
IMPACTS.set(i, impact.ticked());
}
}
}
public static void clear() {
SOURCES.clear();
IMPACTS.clear();
}
}
@@ -1,171 +0,0 @@
package com.craisinlord.antarchy.content.client;
import com.craisinlord.antarchy.content.time.TimeDilationFieldSnapshot;
import com.craisinlord.antarchy.content.time.TimeDilationFieldSnapshotSampler;
import com.craisinlord.antarchy.content.time.TimeDilationMath;
import java.util.List;
import java.util.Map;
import java.util.WeakHashMap;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.item.ItemEntity;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.util.Mth;
public final class ClientTimeDilationTicker {
private static volatile List<TimeDilationFieldSnapshot> activeFields = List.of();
private static ClientLevel activeLevel;
private static final Map<ItemEntity, ItemSpinState> ITEM_SPIN_STATES = new WeakHashMap<>();
private static final Map<LivingEntity, AttackAnimationState> ATTACK_ANIMATION_STATES = new WeakHashMap<>();
private static final Map<LivingEntity, AnimationClockState> WALK_ANIMATION_STATES = new WeakHashMap<>();
private static final Map<Entity, AnimationClockState> ANIMATION_CLOCKS = new WeakHashMap<>();
private ClientTimeDilationTicker() {
}
public static void tick(ClientLevel level) {
if (activeLevel != level) {
activeLevel = level;
activeFields = List.of();
ITEM_SPIN_STATES.clear();
ATTACK_ANIMATION_STATES.clear();
WALK_ANIMATION_STATES.clear();
ANIMATION_CLOCKS.clear();
ContractionAfterimages.clear();
com.craisinlord.antarchy.content.time.TimeDilationApi.clearSyncedClientRates();
}
ContractionAfterimages.tick(level);
}
public static float dilateAnimationTime(Entity entity, float vanillaTime) {
double rate = com.craisinlord.antarchy.content.time.TimeDilationApi.getRate(entity);
if (rate >= TimeDilationMath.NORMAL_RATE) {
ANIMATION_CLOCKS.remove(entity);
return vanillaTime;
}
AnimationClockState state = ANIMATION_CLOCKS.computeIfAbsent(entity,
ignored -> new AnimationClockState(vanillaTime));
float delta = vanillaTime - state.lastVanillaTime;
if (delta >= 0.0F) {
state.temporalTime += delta * (float) rate;
}
state.lastVanillaTime = vanillaTime;
return state.temporalTime;
}
public static float dilateAttackAnimation(LivingEntity entity, float partialTick, float vanillaAnimation) {
double rate = com.craisinlord.antarchy.content.time.TimeDilationApi.getRate(entity);
if (rate >= TimeDilationMath.NORMAL_RATE) {
ATTACK_ANIMATION_STATES.remove(entity);
return vanillaAnimation;
}
AttackAnimationState state = ATTACK_ANIMATION_STATES.computeIfAbsent(
entity, ignored -> new AttackAnimationState(entity.tickCount, vanillaAnimation)
);
if (state.lastTick != entity.tickCount) {
state.previous = state.current;
state.current += (vanillaAnimation - state.current) * (float) rate;
state.lastTick = entity.tickCount;
}
return state.previous + (state.current - state.previous) * partialTick;
}
public static float dilateRotation(LivingEntity entity, float previous, float current, float partialTick) {
double rate = com.craisinlord.antarchy.content.time.TimeDilationApi.getRate(entity);
if (rate >= TimeDilationMath.NORMAL_RATE) {
return Mth.rotLerp(partialTick, previous, current);
}
return previous + Mth.wrapDegrees(current - previous) * partialTick * (float) rate;
}
public static float dilatePitch(LivingEntity entity, float previous, float current, float partialTick) {
double rate = com.craisinlord.antarchy.content.time.TimeDilationApi.getRate(entity);
if (rate >= TimeDilationMath.NORMAL_RATE) {
return Mth.lerp(partialTick, previous, current);
}
return previous + (current - previous) * partialTick * (float) rate;
}
public static float dilateWalkAmount(LivingEntity entity, float amount) {
double rate = com.craisinlord.antarchy.content.time.TimeDilationApi.getRate(entity);
return rate >= TimeDilationMath.NORMAL_RATE ? amount : amount * (float) rate;
}
public static float dilateWalkPosition(LivingEntity entity, float vanillaPosition) {
double rate = com.craisinlord.antarchy.content.time.TimeDilationApi.getRate(entity);
if (Math.abs(rate - TimeDilationMath.NORMAL_RATE) < 0.001D) {
WALK_ANIMATION_STATES.remove(entity);
return vanillaPosition;
}
AnimationClockState state = WALK_ANIMATION_STATES.computeIfAbsent(entity,
ignored -> new AnimationClockState(vanillaPosition));
float delta = vanillaPosition - state.lastVanillaTime;
if (delta >= 0.0F) {
state.temporalTime += delta * (float) rate;
}
state.lastVanillaTime = vanillaPosition;
return state.temporalTime;
}
public static float dilateItemSpin(ItemEntity item, float vanillaSpin) {
double rate = com.craisinlord.antarchy.content.time.TimeDilationApi.getRate(item);
if (rate >= TimeDilationMath.NORMAL_RATE) {
ITEM_SPIN_STATES.remove(item);
return vanillaSpin;
}
ItemSpinState state = ITEM_SPIN_STATES.computeIfAbsent(item, ignored -> new ItemSpinState(vanillaSpin));
float rawDelta = vanillaSpin - state.lastVanillaSpin;
state.lastVanillaSpin = vanillaSpin;
state.temporalSpin += rawDelta * (float) rate;
return state.temporalSpin;
}
private static final class ItemSpinState {
private float lastVanillaSpin;
private float temporalSpin;
private ItemSpinState(float initialSpin) {
this.lastVanillaSpin = initialSpin;
this.temporalSpin = initialSpin;
}
}
private static final class AttackAnimationState {
private int lastTick;
private float previous;
private float current;
private AttackAnimationState(int tick, float initial) {
this.lastTick = tick;
this.previous = initial;
this.current = initial;
}
}
private static final class AnimationClockState {
private float lastVanillaTime;
private float temporalTime;
private AnimationClockState(float initialTime) {
this.lastVanillaTime = initialTime;
this.temporalTime = initialTime;
}
}
public static double rateAt(double x, double y, double z) {
List<TimeDilationFieldSnapshot> fields = activeFields;
if (fields.isEmpty()) {
return TimeDilationMath.NORMAL_RATE;
}
return TimeDilationFieldSnapshotSampler.sample(fields, x, y, z);
}
public static void applyFields(List<TimeDilationFieldSnapshot> fields) {
activeFields = List.copyOf(fields);
}
public static boolean hasActiveDilation() {
return !activeFields.isEmpty()
|| com.craisinlord.antarchy.content.time.TimeDilationApi.hasSyncedClientRates();
}
}
@@ -1,136 +0,0 @@
package com.craisinlord.antarchy.content.client;
import com.craisinlord.antarchy.content.effect.RoyalEffectHooks;
import com.craisinlord.antarchy.content.item.TemporalTunerItem;
import com.craisinlord.antarchy.content.time.TimeDilationApi;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.Iterator;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.function.Function;
import java.util.function.Predicate;
import net.minecraft.core.Holder;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.world.effect.MobEffect;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.LivingEntity;
public final class ContractionAfterimages {
public static final int MAX_SAMPLES = 10;
public static final int SAMPLE_LIFETIME_TICKS = 13;
public static final int DEFAULT_COLOR = 0xFFD35A;
private static final List<Predicate<LivingEntity>> ACTIVATORS = new CopyOnWriteArrayList<>();
private static final List<Function<LivingEntity, Integer>> COLOR_PROVIDERS = new CopyOnWriteArrayList<>();
private static final Map<LivingEntity, Deque<Sample>> HISTORY = new WeakHashMap<>();
private ContractionAfterimages() {
}
public static void tick(ClientLevel level) {
int now = (int) (level.getGameTime() & 0x7fffffffL);
Iterator<Map.Entry<LivingEntity, Deque<Sample>>> iterator = HISTORY.entrySet().iterator();
while (iterator.hasNext()) {
LivingEntity tracked = iterator.next().getKey();
if (tracked == null || tracked.isRemoved() || !tracked.isAlive() || !isActive(tracked)) {
iterator.remove();
}
}
}
public static void observe(LivingEntity living) {
if (!isActive(living)) {
return;
}
int now = (int) (living.level().getGameTime() & 0x7fffffffL);
Deque<Sample> samples = HISTORY.computeIfAbsent(living, ignored -> new ArrayDeque<>());
samples.addFirst(new Sample(living, now));
while (samples.size() > MAX_SAMPLES) {
samples.removeLast();
}
}
public static Deque<Sample> samples(LivingEntity entity) {
return HISTORY.get(entity);
}
public static void addActivator(Predicate<LivingEntity> activator) {
ACTIVATORS.add(activator);
}
public static void addColorProvider(Function<LivingEntity, Integer> provider) {
COLOR_PROVIDERS.add(provider);
}
public static int colorFor(Entity entity) {
if (!(entity instanceof LivingEntity living)) {
return DEFAULT_COLOR;
}
for (Function<LivingEntity, Integer> provider : COLOR_PROVIDERS) {
Integer color = provider.apply(living);
if (color != null) {
return color & 0xFFFFFF;
}
}
return DEFAULT_COLOR;
}
public static boolean isActive(LivingEntity living) {
Holder<MobEffect> holder = RoyalEffectHooks.contractedHolder();
boolean contracted = holder != null && living.hasEffect(holder);
boolean tuned = living instanceof net.minecraft.world.entity.player.Player player
&& TemporalTunerItem.isAvailable(player)
&& TimeDilationApi.getRate(living) > 1.0D;
if (living.isInvisible()) {
return false;
}
if (contracted || tuned) {
return true;
}
for (Predicate<LivingEntity> activator : ACTIVATORS) {
if (activator.test(living)) {
return true;
}
}
return false;
}
public static float fade(Sample sample, int now) {
float elapsed = now - sample.recordedTick;
return Math.max(0.0F, 1.0F - elapsed / SAMPLE_LIFETIME_TICKS);
}
public static void clear() {
HISTORY.clear();
}
public static final class Sample {
public final double x;
public final double y;
public final double z;
public final float bodyYaw;
public final float headYaw;
public final float xRot;
public final float limbPos;
public final float limbSpeed;
public final float ageInTicks;
public final int recordedTick;
Sample(LivingEntity entity, int recordedTick) {
this.x = entity.getX();
this.y = entity.getY();
this.z = entity.getZ();
this.bodyYaw = entity.yBodyRot;
this.headYaw = entity.getYHeadRot();
this.xRot = entity.getXRot();
this.limbPos = entity.walkAnimation.position();
this.limbSpeed = Math.min(entity.walkAnimation.speed(), 1.0F);
this.ageInTicks = entity.tickCount;
this.recordedTick = recordedTick;
}
}
}
@@ -1,90 +0,0 @@
package com.craisinlord.antarchy.content.client;
import com.craisinlord.antarchy.content.network.PortalGunTransitPayload;
import com.craisinlord.anthology.gravity.GravityApi;
import com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity;
import com.craisinlord.antarchy.content.portalgun.PortalGunTransformUtil;
import com.craisinlord.antarchy.content.portalgun.PortalGunTransitMath;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ClientPacketListener;
import net.minecraft.client.player.LocalPlayer;
import net.minecraft.network.protocol.common.ServerboundCustomPayloadPacket;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
public final class PortalGunClientTransit {
private static long cooldownUntil;
private PortalGunClientTransit() {
}
public static void afterLocalMove(LocalPlayer player, AABB startBox, Vec3 velocity) {
if (player.level() == null || !player.isAlive() || player.isPassenger() || player.isSpectator()) {
return;
}
long gameTime = player.level().getGameTime();
if (gameTime < cooldownUntil && cooldownUntil - gameTime <= PortalGunPortalEntity.TELEPORT_COOLDOWN_TICKS) {
return;
}
AABB currentBox = player.getBoundingBox();
if (velocity.lengthSqr() <= 1.0E-12D && startBox.getCenter().distanceToSqr(currentBox.getCenter()) <= 1.0E-10D) {
return;
}
ClientPacketListener connection = Minecraft.getInstance().getConnection();
if (connection == null) {
return;
}
Vec3 crossingVelocity = GravityApi.isGravityInverted(player)
? PortalGunTransitMath.entityVelocityToWorld(player, velocity)
: velocity;
for (PortalGunPortalEntity portal : PortalGunPortalEntity.findPortalsNearBounds(player.level(), startBox.minmax(currentBox).expandTowards(crossingVelocity).inflate(1.0D))) {
PortalGunPortalEntity linked = portal.getLinkedPortal();
if (linked == null || !linked.isAlive()) {
continue;
}
if (portal.resolveCrossing(player, startBox, currentBox, crossingVelocity) == null) {
continue;
}
Vec3 look = player.getLookAngle();
connection.send(new ServerboundCustomPayloadPacket(new PortalGunTransitPayload(
portal.getId(), PortalGunTransitMath.entityPosition(startBox, player), player.position(), velocity)));
PortalGunPortalEntity.PortalTransit transit = portal.computeTransit(player, linked, currentBox, velocity, look);
Vec3 rollLook = GravityApi.isGravityInverted(player)
? PortalGunTransitMath.entityVelocityToWorld(player, look)
: look;
PortalGunRollClientState.onTransit(portal, linked, rollLook);
apply(player, transit);
cooldownUntil = gameTime + PortalGunPortalEntity.TELEPORT_COOLDOWN_TICKS;
return;
}
}
private static void apply(LocalPlayer player, PortalGunPortalEntity.PortalTransit transit) {
Vec3 position = transit.position();
float yaw = PortalGunTransformUtil.yawFromLook(transit.look());
float pitch = PortalGunTransformUtil.pitchFromLook(transit.look());
player.setPos(position.x, position.y, position.z);
player.xo = position.x;
player.yo = position.y;
player.zo = position.z;
player.xOld = position.x;
player.yOld = position.y;
player.zOld = position.z;
player.setYRot(yaw);
player.setXRot(pitch);
player.yRotO = yaw;
player.xRotO = pitch;
player.setYHeadRot(yaw);
player.yHeadRotO = yaw;
player.setYBodyRot(yaw);
player.yBodyRotO = yaw;
player.setDeltaMovement(transit.velocity());
Vec3 fallVelocity = PortalGunTransitMath.entityVelocityToWorld(player, transit.velocity());
player.fallDistance = PortalGunTransitMath.exitFallDistance(player.fallDistance, fallVelocity, player);
player.setOnGround(false);
player.horizontalCollision = false;
player.verticalCollision = false;
player.verticalCollisionBelow = false;
player.setPortalCooldown(PortalGunPortalEntity.TELEPORT_COOLDOWN_TICKS);
}
}
@@ -79,8 +79,8 @@ public final class PortalGunCrosshairRenderer {
long gameTime = level.getGameTime();
if (cachedLevel != level || cachedGameTime != gameTime || !gunId.equals(cachedGunId)) {
Set<PortalGunPortalEntity.PortalSide> portals = EnumSet.noneOf(PortalGunPortalEntity.PortalSide.class);
for (PortalGunPortalEntity portal : com.craisinlord.antarchy.content.portalgun.PortalGunPortalRegistry.all(level)) {
if (portal.isAlive() && gunId.equals(portal.getGunId())) {
for (com.craisinlord.anthology.portal.PortalEntity entry : com.craisinlord.anthology.portal.PortalRegistry.all(level)) {
if (entry instanceof PortalGunPortalEntity portal && portal.isAlive() && gunId.equals(portal.getGunId())) {
portals.add(portal.getPortalSide());
}
}
@@ -1,120 +0,0 @@
package com.craisinlord.antarchy.content.client;
import com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity;
import com.craisinlord.antarchy.content.portalgun.PortalGunTransformUtil;
import java.util.ArrayDeque;
import java.util.Deque;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.phys.Vec3;
public final class PortalGunEntityTransformationStack {
private final Entity entity;
private final Deque<EntityTransformation> stack = new ArrayDeque<>();
public PortalGunEntityTransformationStack(Entity entity) {
this.entity = entity;
}
public void push() {
this.stack.push(EntityTransformation.capture(this.entity));
}
public void pop() {
if (!this.stack.isEmpty()) {
this.stack.pop().restore(this.entity);
}
}
public Vec3 moveEntity(PortalGunPortalEntity sourcePortal, PortalGunPortalEntity destinationPortal, float partialTick) {
double eyeHeight = this.entity.getEyeHeight();
Vec3 interpolatedEye = new Vec3(
net.minecraft.util.Mth.lerp(partialTick, this.entity.xo, this.entity.getX()),
net.minecraft.util.Mth.lerp(partialTick, this.entity.yo, this.entity.getY()) + eyeHeight,
net.minecraft.util.Mth.lerp(partialTick, this.entity.zo, this.entity.getZ())
);
Vec3 previousEye = new Vec3(this.entity.xo, this.entity.yo + eyeHeight, this.entity.zo);
Vec3 transformedEye = destinationPortal.position().add(PortalGunTransformUtil.transformPosition(sourcePortal, destinationPortal, interpolatedEye.subtract(sourcePortal.position())));
Vec3 transformedPreviousEye = destinationPortal.position().add(PortalGunTransformUtil.transformPosition(sourcePortal, destinationPortal, previousEye.subtract(sourcePortal.position())));
Vec3 transformed = transformedEye.subtract(0.0D, eyeHeight, 0.0D);
Vec3 transformedPrevious = transformedPreviousEye.subtract(0.0D, eyeHeight, 0.0D);
Vec3 look = PortalGunTransformUtil.transformVector(sourcePortal, destinationPortal, this.entity.getLookAngle()).normalize();
Vec3 previousLook = PortalGunTransformUtil.transformVector(
sourcePortal,
destinationPortal,
Vec3.directionFromRotation(this.entity.xRotO, this.entity.yRotO)
).normalize();
Vec3 velocity = PortalGunTransformUtil.transformVector(sourcePortal, destinationPortal, this.entity.getDeltaMovement());
float yaw = PortalGunTransformUtil.yawFromLook(look);
float pitch = PortalGunTransformUtil.pitchFromLook(look);
float previousYaw = PortalGunTransformUtil.yawFromLook(previousLook);
float previousPitch = PortalGunTransformUtil.pitchFromLook(previousLook);
float yawDelta = net.minecraft.util.Mth.wrapDegrees(yaw - this.entity.getYRot());
this.entity.setPos(transformed.x, transformed.y, transformed.z);
this.entity.xo = transformedPrevious.x;
this.entity.yo = transformedPrevious.y;
this.entity.zo = transformedPrevious.z;
this.entity.setYRot(yaw);
this.entity.setXRot(pitch);
this.entity.yRotO = previousYaw;
this.entity.xRotO = previousPitch;
this.entity.setDeltaMovement(velocity);
if (this.entity instanceof LivingEntity living) {
living.setYHeadRot(living.getYHeadRot() + yawDelta);
living.setYBodyRot(living.yBodyRot + yawDelta);
living.yHeadRotO += yawDelta;
living.yBodyRotO += yawDelta;
}
return transformed;
}
public void reset() {
while (!this.stack.isEmpty()) {
this.pop();
}
}
private record EntityTransformation(
double x,
double y,
double z,
double xo,
double yo,
double zo,
float yRot,
float xRot,
float yRotO,
float xRotO,
float yHeadRot,
float yHeadRotO,
float yBodyRot,
float yBodyRotO,
Vec3 deltaMovement
) {
private static EntityTransformation capture(Entity entity) {
float yHeadRot = entity instanceof LivingEntity living ? living.getYHeadRot() : entity.getYRot();
float yHeadRotO = entity instanceof LivingEntity living ? living.yHeadRotO : entity.yRotO;
float yBodyRot = entity instanceof LivingEntity living ? living.yBodyRot : entity.getYRot();
float yBodyRotO = entity instanceof LivingEntity living ? living.yBodyRotO : entity.yRotO;
return new EntityTransformation(entity.getX(), entity.getY(), entity.getZ(), entity.xo, entity.yo, entity.zo, entity.getYRot(), entity.getXRot(), entity.yRotO, entity.xRotO, yHeadRot, yHeadRotO, yBodyRot, yBodyRotO, entity.getDeltaMovement());
}
private void restore(Entity entity) {
entity.setPos(this.x, this.y, this.z);
entity.xo = this.xo;
entity.yo = this.yo;
entity.zo = this.zo;
entity.setYRot(this.yRot);
entity.setXRot(this.xRot);
entity.yRotO = this.yRotO;
entity.xRotO = this.xRotO;
entity.setDeltaMovement(this.deltaMovement);
if (entity instanceof LivingEntity living) {
living.setYHeadRot(this.yHeadRot);
living.yHeadRotO = this.yHeadRotO;
living.setYBodyRot(this.yBodyRot);
living.yBodyRotO = this.yBodyRotO;
}
}
}
}
@@ -1,152 +0,0 @@
package com.craisinlord.antarchy.content.client;
import com.craisinlord.antarchy.content.portalgun.PortalGunWorldPortalShape;
import com.craisinlord.antarchy.content.portalgun.PortalGunApertureCuller;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.UUID;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
import org.joml.Matrix4f;
public final class PortalGunPortalRenderState {
private static final ThreadLocal<Deque<PortalGunWorldPortalShape>> ACTIVE_DESTINATION_SHAPES = ThreadLocal.withInitial(ArrayDeque::new);
private static final ThreadLocal<Deque<PortalGunWorldPortalShape>> ACTIVE_SOURCE_SHAPES = ThreadLocal.withInitial(ArrayDeque::new);
private static final ThreadLocal<Deque<PortalRenderContext>> ACTIVE_CONTEXTS = ThreadLocal.withInitial(ArrayDeque::new);
private static final ThreadLocal<Deque<PortalGunApertureCuller>> ACTIVE_CULLERS = ThreadLocal.withInitial(ArrayDeque::new);
private static final ThreadLocal<Integer> RENDER_ALL_DEPTH = ThreadLocal.withInitial(() -> 0);
private PortalGunPortalRenderState() {
}
public static void pushPortalView(PortalGunWorldPortalShape sourceShape, PortalGunWorldPortalShape destinationShape, boolean renderAll) {
ACTIVE_SOURCE_SHAPES.get().push(sourceShape);
ACTIVE_DESTINATION_SHAPES.get().push(destinationShape);
if (renderAll) {
RENDER_ALL_DEPTH.set(RENDER_ALL_DEPTH.get() + 1);
}
}
public static void pushPortalView(PortalRenderContext context) {
ACTIVE_CONTEXTS.get().push(context);
ACTIVE_CULLERS.get().push(PortalGunApertureCuller.create(context.destinationShape(), context.cameraPos()));
pushPortalView(context.sourceShape(), context.destinationShape(), context.renderAll());
}
public static void popPortalView(boolean renderAll) {
Deque<PortalGunWorldPortalShape> sourceShapes = ACTIVE_SOURCE_SHAPES.get();
if (!sourceShapes.isEmpty()) {
sourceShapes.pop();
}
if (sourceShapes.isEmpty()) {
ACTIVE_SOURCE_SHAPES.remove();
}
Deque<PortalGunWorldPortalShape> destinationShapes = ACTIVE_DESTINATION_SHAPES.get();
if (!destinationShapes.isEmpty()) {
destinationShapes.pop();
}
if (destinationShapes.isEmpty()) {
ACTIVE_DESTINATION_SHAPES.remove();
}
if (renderAll) {
int depth = Math.max(0, RENDER_ALL_DEPTH.get() - 1);
if (depth == 0) {
RENDER_ALL_DEPTH.remove();
} else {
RENDER_ALL_DEPTH.set(depth);
}
}
Deque<PortalRenderContext> contexts = ACTIVE_CONTEXTS.get();
if (!contexts.isEmpty()) {
contexts.pop();
}
if (contexts.isEmpty()) {
ACTIVE_CONTEXTS.remove();
}
Deque<PortalGunApertureCuller> cullers = ACTIVE_CULLERS.get();
if (!cullers.isEmpty()) {
cullers.pop();
}
if (cullers.isEmpty()) {
ACTIVE_CULLERS.remove();
}
}
public static void pushDestinationShape(PortalGunWorldPortalShape shape) {
ACTIVE_DESTINATION_SHAPES.get().push(shape);
}
public static void popDestinationShape() {
Deque<PortalGunWorldPortalShape> shapes = ACTIVE_DESTINATION_SHAPES.get();
if (!shapes.isEmpty()) {
shapes.pop();
}
if (shapes.isEmpty()) {
ACTIVE_DESTINATION_SHAPES.remove();
}
}
public static PortalGunWorldPortalShape getDestinationShape() {
Deque<PortalGunWorldPortalShape> shapes = ACTIVE_DESTINATION_SHAPES.get();
return shapes.isEmpty() ? null : shapes.peek();
}
public static PortalGunWorldPortalShape getSourceShape() {
Deque<PortalGunWorldPortalShape> shapes = ACTIVE_SOURCE_SHAPES.get();
return shapes.isEmpty() ? null : shapes.peek();
}
public static PortalRenderContext getContext() {
Deque<PortalRenderContext> contexts = ACTIVE_CONTEXTS.get();
return contexts.isEmpty() ? null : contexts.peek();
}
public static int getRenderLevel() {
return ACTIVE_CONTEXTS.get().size();
}
public static boolean renderAll() {
return RENDER_ALL_DEPTH.get() > 0;
}
public static boolean shouldRenderBounds(AABB bounds) {
if (renderAll()) {
return true;
}
PortalRenderContext context = getContext();
if (context == null) {
PortalGunWorldPortalShape destinationShape = getDestinationShape();
PortalGunWorldPortalShape sourceShape = getSourceShape();
return destinationShape == null || shouldRenderBounds(bounds, sourceShape, destinationShape, false);
}
if (!context.destinationShape().intersectsFront(bounds, 0.125D)) {
return false;
}
Deque<PortalGunApertureCuller> cullers = ACTIVE_CULLERS.get();
return cullers.isEmpty() || cullers.peek().intersects(bounds);
}
private static boolean shouldRenderBounds(AABB bounds, PortalGunWorldPortalShape sourceShape, PortalGunWorldPortalShape destinationShape, boolean renderAll) {
if (renderAll) {
return true;
}
if (!destinationShape.intersectsFront(bounds, 0.125D)) {
return false;
}
return true;
}
public record PortalRenderContext(
PortalGunWorldPortalShape sourceShape,
PortalGunWorldPortalShape destinationShape,
Vec3 cameraPos,
Vec3 look,
Vec3 up,
Matrix4f viewMatrix,
Matrix4f projectionMatrix,
int renderLevel,
boolean renderAll,
UUID destinationId
) {
}
}
@@ -1,67 +0,0 @@
package com.craisinlord.antarchy.content.client;
import com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity;
import com.craisinlord.antarchy.content.portalgun.PortalGunTransformUtil;
import net.minecraft.client.Minecraft;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.phys.Vec3;
public final class PortalGunRollClientState {
private static final double PORTAL_QUERY_RADIUS = 12.0D;
private static final float ROLL_DECAY = 0.85F;
private static final float ROLL_EPSILON = 0.05F;
private static float previousRollDegrees;
private static float rollDegrees;
private PortalGunRollClientState() {
}
public static float getRollDegrees() {
return rollDegrees;
}
public static float getRollDegrees(float partialTick) {
return previousRollDegrees + (rollDegrees - previousRollDegrees) * partialTick;
}
public static boolean isEntityInsidePortal(Minecraft minecraft, Entity entity) {
if (minecraft.level == null || entity == null) {
return false;
}
for (PortalGunPortalEntity portal : com.craisinlord.antarchy.content.portalgun.PortalGunPortalRegistry.near(minecraft.level, entity.getBoundingBox().inflate(PORTAL_QUERY_RADIUS))) {
if (portal.intersectsEntityBounds(entity)) {
return true;
}
}
return false;
}
public static boolean isCameraInsidePortal(Minecraft minecraft) {
Entity cameraEntity = minecraft.getCameraEntity();
return cameraEntity != null && isEntityInsidePortal(minecraft, cameraEntity);
}
public static void clear() {
previousRollDegrees = 0.0F;
rollDegrees = 0.0F;
}
public static void tick(Minecraft minecraft) {
if (minecraft.level == null || minecraft.player == null) {
clear();
return;
}
previousRollDegrees = rollDegrees;
rollDegrees *= ROLL_DECAY;
if (Math.abs(rollDegrees) < ROLL_EPSILON) {
rollDegrees = 0.0F;
}
}
public static void onTransit(PortalGunPortalEntity sourcePortal, PortalGunPortalEntity destinationPortal, Vec3 look) {
Vec3 transformedLook = PortalGunTransformUtil.transformVector(sourcePortal, destinationPortal, look).normalize();
Vec3 transformedUp = PortalGunTransformUtil.transformVector(sourcePortal, destinationPortal, PortalGunTransformUtil.upVectorFromLookAndRoll(look, rollDegrees)).normalize();
rollDegrees = PortalGunTransformUtil.rollFromOrientation(transformedLook, transformedUp);
previousRollDegrees = rollDegrees;
}
}
@@ -1,26 +0,0 @@
package com.craisinlord.antarchy.content.client.model;
import com.craisinlord.antarchy.Antarchy;
import com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity;
import net.minecraft.resources.ResourceLocation;
import software.bernie.geckolib.model.GeoModel;
public class PortalGunPortalModel extends GeoModel<PortalGunPortalEntity> {
private static final ResourceLocation BLUE_TEXTURE = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "textures/vfx/portal_gun_portal_blue.png");
private static final ResourceLocation ORANGE_TEXTURE = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "textures/vfx/portal_gun_portal_orange.png");
@Override
public ResourceLocation getModelResource(PortalGunPortalEntity animatable) {
return ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "geo/portal_gun_portal.geo.json");
}
@Override
public ResourceLocation getTextureResource(PortalGunPortalEntity animatable) {
return animatable.getPortalSide() == PortalGunPortalEntity.PortalSide.BLUE ? BLUE_TEXTURE : ORANGE_TEXTURE;
}
@Override
public ResourceLocation getAnimationResource(PortalGunPortalEntity animatable) {
return ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "animations/portal_gun_portal.animation.json");
}
}
@@ -1,61 +0,0 @@
package com.craisinlord.antarchy.content.client.renderer;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import net.minecraft.client.renderer.LevelRenderer;
final class IrisPortalSceneCompat {
private static final Field PIPELINE_FIELD;
private static final Method GET_PIPELINE_MANAGER;
private static final Method GET_PIPELINE;
static {
Field field = null;
Method manager = null;
Method pipeline = null;
try {
Class<?> iris = Class.forName("net.irisshaders.iris.Iris");
manager = iris.getMethod("getPipelineManager");
pipeline = manager.getReturnType().getMethod("getPipelineNullable");
field = LevelRenderer.class.getDeclaredField("pipeline");
field.setAccessible(true);
} catch (ReflectiveOperationException | RuntimeException | LinkageError ignored) {
field = null;
}
PIPELINE_FIELD = field;
GET_PIPELINE_MANAGER = field == null ? null : manager;
GET_PIPELINE = field == null ? null : pipeline;
}
private IrisPortalSceneCompat() {
}
static boolean begin(LevelRenderer renderer) {
if (PIPELINE_FIELD == null) {
return false;
}
try {
if (PIPELINE_FIELD.get(renderer) != null) {
return false;
}
Object pipeline = GET_PIPELINE.invoke(GET_PIPELINE_MANAGER.invoke(null));
if (pipeline == null) {
return false;
}
PIPELINE_FIELD.set(renderer, pipeline);
return true;
} catch (ReflectiveOperationException | RuntimeException ignored) {
return false;
}
}
static void end(LevelRenderer renderer, boolean installed) {
if (!installed) {
return;
}
try {
PIPELINE_FIELD.set(renderer, null);
} catch (ReflectiveOperationException | RuntimeException ignored) {
}
}
}
@@ -1,8 +1,8 @@
package com.craisinlord.antarchy.content.client.renderer;
import com.craisinlord.antarchy.Antarchy;
import com.craisinlord.antarchy.content.portalgun.PortalGunTransformUtil;
import com.craisinlord.antarchy.content.portalgun.PortalGunWorldPortalShape;
import com.craisinlord.anthology.portal.PortalTransforms;
import com.craisinlord.anthology.portal.PortalShape;
import com.mojang.blaze3d.platform.GlStateManager;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.BufferBuilder;
@@ -37,13 +37,13 @@ public final class PortalGunMoonViewRenderer {
private PortalGunMoonViewRenderer() {
}
public static void render(Minecraft minecraft, PortalGunWorldPortalShape source, Vec3 cameraPos, Vec3 look, Vec3 up,
public static void render(Minecraft minecraft, PortalShape source, Vec3 cameraPos, Vec3 look, Vec3 up,
Matrix4f rootProjection, float partialTick) {
PortalGunWorldPortalShape exit = new PortalGunWorldPortalShape(Vec3.ZERO, EXIT_NORMAL, EXIT_UP, EXIT_NORMAL.cross(EXIT_UP),
PortalShape exit = new PortalShape(Vec3.ZERO, EXIT_NORMAL, EXIT_UP, EXIT_NORMAL.cross(EXIT_UP),
source.halfWidth(), source.halfHeight(), source.halfDepth());
Vec3 moonLook = PortalGunTransformUtil.transformVector(source, exit, look).normalize();
Vec3 moonUp = PortalGunTransformUtil.transformVector(source, exit, up).normalize();
Matrix4f view = new Matrix4f().rotation(PortalGunTransformUtil.orientationQuaternion(moonLook, moonUp).conjugate(new Quaternionf()));
Vec3 moonLook = PortalTransforms.transformVector(source, exit, look).normalize();
Vec3 moonUp = PortalTransforms.transformVector(source, exit, up).normalize();
Matrix4f view = new Matrix4f().rotation(PortalTransforms.orientationQuaternion(moonLook, moonUp).conjugate(new Quaternionf()));
Vec3 earthDirection = new Vec3(0.0D, Math.sin(EARTH_ELEVATION), Math.cos(EARTH_ELEVATION));
Matrix4fStack modelViewStack = RenderSystem.getModelViewStack();
@@ -1,108 +1,11 @@
package com.craisinlord.antarchy.content.client.renderer;
import com.craisinlord.antarchy.Antarchy;
import com.craisinlord.antarchy.config.AntarchySettings;
import com.craisinlord.antarchy.content.client.model.PortalGunPortalModel;
import com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import net.minecraft.client.GraphicsStatus;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.LightTexture;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.RenderType;
import com.craisinlord.anthology.portal.client.PortalEntityRenderer;
import net.minecraft.client.renderer.entity.EntityRendererProvider;
import net.minecraft.client.renderer.texture.OverlayTexture;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.Nullable;
import org.joml.Matrix4f;
import software.bernie.geckolib.renderer.GeoEntityRenderer;
public class PortalGunPortalRenderer extends GeoEntityRenderer<PortalGunPortalEntity> {
public class PortalGunPortalRenderer extends PortalEntityRenderer<PortalGunPortalEntity> {
public PortalGunPortalRenderer(EntityRendererProvider.Context context) {
super(context, new PortalGunPortalModel());
this.shadowRadius = 0.0F;
}
@Override
public void render(PortalGunPortalEntity entity, float entityYaw, float partialTick, PoseStack poseStack, MultiBufferSource bufferSource, int packedLight) {
poseStack.pushPose();
Matrix4f pose = poseStack.last().pose();
float portalScale = entity.getPortalVisualScale(partialTick);
float halfWidth = entity.getPortalWidth() * 0.5F;
float halfHeight = entity.getPortalHeight() * 0.5F;
ResourceLocation texture = portalTexture(entity);
VertexConsumer consumer = bufferSource.getBuffer(RenderType.entityTranslucent(texture));
if (isFancyPortalsEnabled()) {
float frameV = Math.floorMod((int) ((entity.level().getGameTime() + partialTick) / 5.0F), 4) * 0.25F;
putAnimatedPortalVertex(consumer, pose, entity, portalScale, frameV, -halfWidth, -halfHeight, 0.0F, 1.0F);
putAnimatedPortalVertex(consumer, pose, entity, portalScale, frameV, -halfWidth, halfHeight, 0.0F, 0.0F);
putAnimatedPortalVertex(consumer, pose, entity, portalScale, frameV, halfWidth, halfHeight, 1.0F, 0.0F);
putAnimatedPortalVertex(consumer, pose, entity, portalScale, frameV, halfWidth, -halfHeight, 1.0F, 1.0F);
} else {
putPortalVertex(consumer, pose, entity, portalScale, -halfWidth, -halfHeight, 0.0F, 1.0F, true);
putPortalVertex(consumer, pose, entity, portalScale, -halfWidth, halfHeight, 0.0F, 0.0F, true);
putPortalVertex(consumer, pose, entity, portalScale, halfWidth, halfHeight, 1.0F, 0.0F, true);
putPortalVertex(consumer, pose, entity, portalScale, halfWidth, -halfHeight, 1.0F, 1.0F, true);
}
VertexConsumer edge = bufferSource.getBuffer(RenderType.entityTranslucent(PortalGunPortalRim.texture(entity)));
float rimWidth = halfWidth + PortalGunPortalRim.EXTRA_WIDTH;
float rimHeight = halfHeight + PortalGunPortalRim.EXTRA_HEIGHT;
putPortalVertex(edge, pose, entity, portalScale, -rimWidth, -rimHeight, 0.0F, 1.0F, true);
putPortalVertex(edge, pose, entity, portalScale, -rimWidth, rimHeight, 0.0F, 0.0F, true);
putPortalVertex(edge, pose, entity, portalScale, rimWidth, rimHeight, 1.0F, 0.0F, true);
putPortalVertex(edge, pose, entity, portalScale, rimWidth, -rimHeight, 1.0F, 1.0F, true);
poseStack.popPose();
}
private static ResourceLocation portalTexture(PortalGunPortalEntity entity) {
return isFancyPortalsEnabled()
? entity.getVariant().vortexTexture(entity.getPortalSide())
: PortalGunPortalRim.texture(entity);
}
private static boolean isFancyPortalsEnabled() {
Minecraft minecraft = Minecraft.getInstance();
return AntarchySettings.portalGunFancyPortals()
&& minecraft.options.graphicsMode().get() == GraphicsStatus.FANCY;
}
private static void putAnimatedPortalVertex(VertexConsumer consumer, Matrix4f pose, PortalGunPortalEntity entity, float portalScale, float frameV, float horizontal, float vertical, float u, float v) {
Vec3 position = entity.getWidthVec().normalize().scale(horizontal * portalScale)
.add(entity.getUpVec().normalize().scale(vertical * portalScale))
.add(entity.getNormalVec().normalize().scale(0.03125D));
consumer.addVertex(pose, (float) position.x, (float) position.y, (float) position.z)
.setColor(255, 255, 255, 210)
.setUv(u, frameV + v * 0.25F)
.setOverlay(OverlayTexture.NO_OVERLAY)
.setLight(LightTexture.FULL_BRIGHT)
.setNormal((float) entity.getNormalVec().x, (float) entity.getNormalVec().y, (float) entity.getNormalVec().z);
}
private static void putPortalVertex(VertexConsumer consumer, Matrix4f pose, PortalGunPortalEntity entity, float portalScale, float horizontal, float vertical, float u, float v, boolean channelTint) {
Vec3 position = entity.getWidthVec().normalize().scale(horizontal * portalScale)
.add(entity.getUpVec().normalize().scale(vertical * portalScale))
.add(entity.getNormalVec().normalize().scale(0.03125D));
int color = entity.getPortalColor();
int red = channelTint ? color >> 16 & 0xFF : 255;
int green = channelTint ? color >> 8 & 0xFF : 255;
int blue = channelTint ? color & 0xFF : 255;
int alpha = channelTint ? PortalGunPortalRim.ENTITY_ALPHA : 210;
consumer.addVertex(pose, (float) position.x, (float) position.y, (float) position.z)
.setColor(red, green, blue, alpha)
.setUv(u, v)
.setOverlay(OverlayTexture.NO_OVERLAY)
.setLight(LightTexture.FULL_BRIGHT)
.setNormal((float) entity.getNormalVec().x, (float) entity.getNormalVec().y, (float) entity.getNormalVec().z);
}
@Override
protected void applyRotations(PortalGunPortalEntity animatable, PoseStack poseStack, float ageInTicks, float rotationYaw, float partialTick, float nativeScale) {
}
@Override
public @Nullable RenderType getRenderType(PortalGunPortalEntity animatable, ResourceLocation texture, @Nullable MultiBufferSource bufferSource, float partialTick) {
return RenderType.entityTranslucent(portalTexture(animatable));
super(context);
}
}
@@ -1,186 +0,0 @@
package com.craisinlord.antarchy.content.client.renderer;
import com.craisinlord.antarchy.Antarchy;
import com.craisinlord.antarchy.content.item.PortalGunItem;
import com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity;
import com.mojang.blaze3d.platform.GlStateManager;
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 com.mojang.blaze3d.vertex.VertexSorting;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.GameRenderer;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.phys.Vec3;
import org.joml.Matrix4f;
import org.joml.Matrix4fStack;
import org.lwjgl.opengl.GL11;
public final class PortalGunPortalRim {
public static final float EXTRA_WIDTH = 0.0F;
public static final float EXTRA_HEIGHT = 0.0F;
public static final int ENTITY_ALPHA = 150;
private static final double OVERLAY_OFFSET = 0.05D;
private static final int HIDDEN_RIM_ALPHA = 170;
private static final ResourceLocation BLUE_TEXTURE = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "textures/vfx/portal_gun_portal_blue.png");
private static final ResourceLocation ORANGE_TEXTURE = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "textures/vfx/portal_gun_portal_orange.png");
private PortalGunPortalRim() {
}
public static ResourceLocation texture(PortalGunPortalEntity portal) {
return portal.getPortalSide() == PortalGunPortalEntity.PortalSide.BLUE ? BLUE_TEXTURE : ORANGE_TEXTURE;
}
static void drawOverView(PortalGunPortalEntity portal, Vec3 cameraPos, Matrix4f viewMatrix, Matrix4f projectionMatrix, float partialTick) {
withCameraMatrices(viewMatrix, projectionMatrix, () -> {
RenderSystem.disableDepthTest();
RenderSystem.depthMask(false);
RenderSystem.defaultBlendFunc();
drawRims(List.of(portal), cameraPos, partialTick, ENTITY_ALPHA);
});
}
static void drawHiddenOutlines(Minecraft minecraft, Vec3 cameraPos, Matrix4f viewMatrix, Matrix4f projectionMatrix, float partialTick) {
drawHiddenOutlines(minecraft, cameraPos, viewMatrix, projectionMatrix, partialTick, null);
}
static void drawHiddenOutlines(Minecraft minecraft, Vec3 cameraPos, Matrix4f viewMatrix, Matrix4f projectionMatrix, float partialTick,
PortalGunPortalEntity excluded) {
if (minecraft.level == null || minecraft.player == null) {
return;
}
List<PortalGunPortalEntity> ownedPortals = collectOwnedPortals(minecraft);
if (excluded != null) {
ownedPortals.remove(excluded);
}
if (ownedPortals.isEmpty()) {
return;
}
withCameraMatrices(viewMatrix, projectionMatrix, () -> {
RenderSystem.enableDepthTest();
RenderSystem.depthMask(false);
RenderSystem.depthFunc(GL11.GL_GREATER);
RenderSystem.blendFunc(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE);
drawRims(ownedPortals, cameraPos, partialTick, HIDDEN_RIM_ALPHA);
});
}
private static void withCameraMatrices(Matrix4f viewMatrix, Matrix4f projectionMatrix, Runnable draw) {
Matrix4fStack modelViewStack = RenderSystem.getModelViewStack();
Matrix4f previousProjection = new Matrix4f(RenderSystem.getProjectionMatrix());
VertexSorting previousSorting = RenderSystem.getVertexSorting();
modelViewStack.pushMatrix();
try {
modelViewStack.set(viewMatrix);
RenderSystem.applyModelViewMatrix();
RenderSystem.setProjectionMatrix(new Matrix4f(projectionMatrix), VertexSorting.DISTANCE_TO_ORIGIN);
RenderSystem.setShader(GameRenderer::getPositionTexColorShader);
RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F);
RenderSystem.disableCull();
RenderSystem.enableBlend();
draw.run();
} finally {
RenderSystem.depthFunc(GL11.GL_LEQUAL);
RenderSystem.enableDepthTest();
RenderSystem.depthMask(true);
RenderSystem.defaultBlendFunc();
RenderSystem.disableBlend();
RenderSystem.enableCull();
RenderSystem.setProjectionMatrix(previousProjection, previousSorting);
modelViewStack.popMatrix();
RenderSystem.applyModelViewMatrix();
}
}
private static List<PortalGunPortalEntity> collectOwnedPortals(Minecraft minecraft) {
Set<UUID> owners = new HashSet<>();
Player player = minecraft.player;
owners.add(player.getUUID());
addGunOwner(owners, player.getMainHandItem(), player.getUUID());
addGunOwner(owners, player.getOffhandItem(), player.getUUID());
List<PortalGunPortalEntity> portals = new ArrayList<>();
for (PortalGunPortalEntity portal : com.craisinlord.antarchy.content.portalgun.PortalGunPortalRegistry.all(minecraft.level)) {
if (portal.isAlive() && owners.contains(portal.getOwnerId())) {
portals.add(portal);
}
}
return portals;
}
private static void addGunOwner(Set<UUID> owners, ItemStack stack, UUID fallback) {
if (stack.getItem() instanceof PortalGunItem) {
UUID owner = PortalGunItem.getPortalOwnerId(stack, fallback);
if (owner != null) {
owners.add(owner);
}
}
}
private static void drawRims(List<PortalGunPortalEntity> portals, Vec3 cameraPos, float partialTick, int alpha) {
drawRimsWithTexture(portals, cameraPos, partialTick, alpha, BLUE_TEXTURE, PortalGunPortalEntity.PortalSide.BLUE);
drawRimsWithTexture(portals, cameraPos, partialTick, alpha, ORANGE_TEXTURE, PortalGunPortalEntity.PortalSide.ORANGE);
}
private static void drawRimsWithTexture(List<PortalGunPortalEntity> portals, Vec3 cameraPos, float partialTick, int alpha,
ResourceLocation texture, PortalGunPortalEntity.PortalSide side) {
BufferBuilder buffer = null;
for (PortalGunPortalEntity portal : portals) {
if (portal.getPortalSide() != side) {
continue;
}
if (buffer == null) {
buffer = Tesselator.getInstance().begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_TEX_COLOR);
}
addRim(buffer, portal, cameraPos, partialTick, alpha);
}
if (buffer == null) {
return;
}
MeshData mesh = buffer.build();
if (mesh == null) {
return;
}
RenderSystem.setShaderTexture(0, texture);
BufferUploader.drawWithShader(mesh);
}
private static void addRim(BufferBuilder buffer, PortalGunPortalEntity portal, Vec3 cameraPos, float partialTick, int alpha) {
float scale = portal.getPortalVisualScale(partialTick);
if (scale <= 0.0F) {
return;
}
Vec3 right = portal.getWidthVec().normalize();
Vec3 up = portal.getUpVec().normalize();
Vec3 center = portal.position().add(portal.getNormalVec().normalize().scale(OVERLAY_OFFSET)).subtract(cameraPos);
double halfWidth = (portal.getPortalWidth() * 0.5D + EXTRA_WIDTH) * scale;
double halfHeight = (portal.getPortalHeight() * 0.5D + EXTRA_HEIGHT) * scale;
int color = portal.getPortalColor();
int red = color >> 16 & 0xFF;
int green = color >> 8 & 0xFF;
int blue = color & 0xFF;
addVertex(buffer, center, right, up, -halfWidth, -halfHeight, 0.0F, 1.0F, red, green, blue, alpha);
addVertex(buffer, center, right, up, -halfWidth, halfHeight, 0.0F, 0.0F, red, green, blue, alpha);
addVertex(buffer, center, right, up, halfWidth, halfHeight, 1.0F, 0.0F, red, green, blue, alpha);
addVertex(buffer, center, right, up, halfWidth, -halfHeight, 1.0F, 1.0F, red, green, blue, alpha);
}
private static void addVertex(BufferBuilder buffer, Vec3 center, Vec3 right, Vec3 up, double horizontal, double vertical,
float u, float v, int red, int green, int blue, int alpha) {
Vec3 position = center.add(right.scale(horizontal)).add(up.scale(vertical));
buffer.addVertex((float) position.x, (float) position.y, (float) position.z)
.setUv(u, v)
.setColor(red, green, blue, alpha);
}
}
@@ -1,242 +0,0 @@
package com.craisinlord.antarchy.content.client.renderer;
import com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity;
import com.craisinlord.antarchy.mixins.client.LevelRendererPortalSceneInvoker;
import com.craisinlord.antarchy.mixins.client.LevelRendererPortalViewAreaAccessor;
import com.mojang.blaze3d.platform.Lighting;
import com.mojang.blaze3d.shaders.FogShape;
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.PoseStack;
import com.mojang.blaze3d.vertex.Tesselator;
import com.mojang.blaze3d.vertex.VertexFormat;
import com.mojang.blaze3d.vertex.VertexSorting;
import java.util.UUID;
import java.util.function.Consumer;
import net.minecraft.client.Camera;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.FogRenderer;
import net.minecraft.client.renderer.GameRenderer;
import net.minecraft.client.renderer.LevelRenderer;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.RenderBuffers;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.culling.Frustum;
import net.minecraft.core.BlockPos;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.phys.Vec3;
import org.joml.Matrix4f;
import org.joml.Matrix4fStack;
import org.lwjgl.opengl.GL11;
public final class PortalGunPortalSceneRenderer {
private static RenderBuffers portalBuffers;
private static boolean renderingScene;
private PortalGunPortalSceneRenderer() {
}
public static MultiBufferSource.BufferSource bufferSource() {
if (portalBuffers == null) {
portalBuffers = new RenderBuffers(1);
}
return portalBuffers.bufferSource();
}
public static boolean isRenderingScene() {
return renderingScene;
}
public static void render(Minecraft minecraft, Camera camera, float partialTick, Matrix4f viewMatrix,
Matrix4f projection, Matrix4f skyProjection, Frustum frustum,
PortalTerrain terrain, Consumer<PoseStack> crossingEntities,
UUID sourceId, UUID destinationId, int viewLevel) {
LevelRenderer renderer = minecraft.levelRenderer;
LevelRendererPortalViewAreaAccessor state = (LevelRendererPortalViewAreaAccessor) renderer;
LevelRendererPortalSceneInvoker scene = (LevelRendererPortalSceneInvoker) renderer;
Vec3 cameraPosition = camera.getPosition();
double cameraX = cameraPosition.x;
double cameraY = cameraPosition.y;
double cameraZ = cameraPosition.z;
int previousRainSoundTime = state.antarchy$getRainSoundTime();
Matrix4fStack modelViewStack = RenderSystem.getModelViewStack();
Matrix4f previousProjection = new Matrix4f(RenderSystem.getProjectionMatrix());
VertexSorting previousSorting = RenderSystem.getVertexSorting();
float previousFogStart = RenderSystem.getShaderFogStart();
float previousFogEnd = RenderSystem.getShaderFogEnd();
float[] previousFogColor = RenderSystem.getShaderFogColor().clone();
FogShape previousFogShape = RenderSystem.getShaderFogShape();
Camera previousBlockEntityCamera = minecraft.getBlockEntityRenderDispatcher().camera;
modelViewStack.pushMatrix();
modelViewStack.identity();
RenderSystem.applyModelViewMatrix();
renderingScene = true;
boolean irisPipelineInstalled = IrisPortalSceneCompat.begin(renderer);
terrain.begin(minecraft);
try {
float renderDistance = minecraft.gameRenderer.getRenderDistance();
BlockPos cameraBlockPos = BlockPos.containing(cameraX, cameraY, cameraZ);
boolean foggy = minecraft.level.effects().isFoggyAt(cameraBlockPos.getX(), cameraBlockPos.getY())
|| minecraft.gui.getBossOverlay().shouldCreateWorldFog();
{
RenderSystem.setShaderGameTime(minecraft.level.getGameTime(), partialTick);
minecraft.getBlockEntityRenderDispatcher().prepare(minecraft.level, camera, minecraft.hitResult);
minecraft.getEntityRenderDispatcher().prepare(minecraft.level, camera, minecraft.crosshairPickEntity);
FogRenderer.setupColor(camera, partialTick, minecraft.level, minecraft.options.getEffectiveRenderDistance(),
minecraft.gameRenderer.getDarkenWorldAmount(partialTick));
FogRenderer.levelFogColor();
fillBackground(RenderSystem.getShaderFogColor());
RenderSystem.setProjectionMatrix(skyProjection, VertexSorting.DISTANCE_TO_ORIGIN);
FogRenderer.setupFog(camera, FogRenderer.FogMode.FOG_SKY, renderDistance, foggy, partialTick);
RenderSystem.setShader(GameRenderer::getPositionShader);
if (irisPipelineInstalled || PortalGunPortalSkyQueries.shouldRenderSky(sourceId, destinationId, viewLevel)) {
renderer.renderSky(viewMatrix, skyProjection, partialTick, camera, foggy,
() -> FogRenderer.setupFog(camera, FogRenderer.FogMode.FOG_SKY, renderDistance, foggy, partialTick));
}
}
RenderSystem.setProjectionMatrix(projection, VertexSorting.DISTANCE_TO_ORIGIN);
FogRenderer.setupFog(camera, FogRenderer.FogMode.FOG_TERRAIN, Math.max(renderDistance, 32.0F), foggy, partialTick);
{
RenderSystem.enableDepthTest();
RenderSystem.depthMask(true);
terrain.drawLayer(minecraft, RenderType.solid(), cameraPosition, viewMatrix, projection);
terrain.drawLayer(minecraft, RenderType.cutoutMipped(), cameraPosition, viewMatrix, projection);
terrain.drawLayer(minecraft, RenderType.cutout(), cameraPosition, viewMatrix, projection);
}
if (minecraft.level.effects().constantAmbientLight()) {
Lighting.setupNetherLevel();
} else {
Lighting.setupLevel();
}
modelViewStack.pushMatrix();
modelViewStack.mul(viewMatrix);
RenderSystem.applyModelViewMatrix();
PoseStack poseStack = new PoseStack();
MultiBufferSource.BufferSource buffers = bufferSource();
try {
{
for (Entity entity : minecraft.level.entitiesForRendering()) {
if (!minecraft.getEntityRenderDispatcher().shouldRender(entity, frustum, cameraX, cameraY, cameraZ)
&& !entity.hasIndirectPassenger(minecraft.player)) {
continue;
}
if (entity instanceof PortalGunPortalEntity portal && !portal.shouldRenderFront(cameraPosition)) {
continue;
}
if (entity == camera.getEntity()
&& entity.getBoundingBox().inflate(0.1D).contains(cameraPosition)
&& (!(entity instanceof LivingEntity living) || !living.isSleeping())) {
continue;
}
scene.antarchy$renderPortalEntity(entity, cameraX, cameraY, cameraZ, partialTick, poseStack, buffers);
}
crossingEntities.accept(poseStack);
}
buffers.endBatch();
{
terrain.forEachBlockEntity(minecraft, frustum, blockEntity ->
renderBlockEntity(minecraft, blockEntity, cameraX, cameraY, cameraZ, partialTick, poseStack, buffers));
}
buffers.endBatch();
if (!irisPipelineInstalled) {
PortalGunPortalSkyQueries.querySkyVisibility(sourceId, destinationId, viewLevel);
}
{
terrain.drawLayer(minecraft, RenderType.translucent(), cameraPosition, viewMatrix, projection);
buffers.endBatch();
terrain.drawLayer(minecraft, RenderType.tripwire(), cameraPosition, viewMatrix, projection);
minecraft.particleEngine.render(minecraft.gameRenderer.lightTexture(), camera, partialTick);
}
{
RenderSystem.depthMask(false);
scene.antarchy$renderPortalWeather(minecraft.gameRenderer.lightTexture(), partialTick, cameraX, cameraY, cameraZ);
scene.antarchy$renderPortalWorldBorder(camera);
}
} finally {
try {
buffers.endBatch();
} finally {
modelViewStack.popMatrix();
RenderSystem.applyModelViewMatrix();
}
}
} finally {
renderingScene = false;
IrisPortalSceneCompat.end(renderer, irisPipelineInstalled);
terrain.end(minecraft);
state.antarchy$setRainSoundTime(previousRainSoundTime);
modelViewStack.popMatrix();
RenderSystem.applyModelViewMatrix();
RenderSystem.setProjectionMatrix(previousProjection, previousSorting);
RenderSystem.setShaderFogStart(previousFogStart);
RenderSystem.setShaderFogEnd(previousFogEnd);
RenderSystem.setShaderFogColor(previousFogColor[0], previousFogColor[1], previousFogColor[2], previousFogColor[3]);
RenderSystem.setShaderFogShape(previousFogShape);
if (previousBlockEntityCamera != null) {
minecraft.getBlockEntityRenderDispatcher().prepare(minecraft.level, previousBlockEntityCamera, minecraft.hitResult);
}
RenderSystem.depthMask(true);
RenderSystem.enableDepthTest();
RenderSystem.enableCull();
RenderSystem.disableBlend();
RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F);
}
}
private static void fillBackground(float[] fogColor) {
int previousDepthFunction = GL11.glGetInteger(GL11.GL_DEPTH_FUNC);
Matrix4f previousProjection = new Matrix4f(RenderSystem.getProjectionMatrix());
VertexSorting previousSorting = RenderSystem.getVertexSorting();
BufferBuilder buffer = Tesselator.getInstance().begin(VertexFormat.Mode.TRIANGLES, DefaultVertexFormat.POSITION);
buffer.addVertex(-1.0F, -1.0F, 0.99999F);
buffer.addVertex(3.0F, -1.0F, 0.99999F);
buffer.addVertex(-1.0F, 3.0F, 0.99999F);
MeshData mesh = buffer.build();
try {
RenderSystem.enableDepthTest();
RenderSystem.depthMask(true);
RenderSystem.colorMask(true, true, true, true);
RenderSystem.disableBlend();
RenderSystem.disableCull();
GL11.glDepthFunc(GL11.GL_ALWAYS);
RenderSystem.setProjectionMatrix(new Matrix4f(), previousSorting);
RenderSystem.setShader(GameRenderer::getPositionShader);
RenderSystem.setShaderColor(fogColor[0], fogColor[1], fogColor[2], 1.0F);
if (mesh != null) {
BufferUploader.drawWithShader(mesh);
}
} finally {
GL11.glDepthFunc(previousDepthFunction);
RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F);
RenderSystem.setProjectionMatrix(previousProjection, previousSorting);
RenderSystem.enableCull();
}
}
private static void renderBlockEntity(Minecraft minecraft, BlockEntity blockEntity, double cameraX, double cameraY, double cameraZ,
float partialTick, PoseStack poseStack, MultiBufferSource.BufferSource buffers) {
if (!blockEntity.hasLevel() || !blockEntity.getType().isValid(blockEntity.getBlockState())) {
return;
}
BlockPos pos = blockEntity.getBlockPos();
poseStack.pushPose();
try {
poseStack.translate(pos.getX() - cameraX, pos.getY() - cameraY, pos.getZ() - cameraZ);
minecraft.getBlockEntityRenderDispatcher().render(blockEntity, partialTick, poseStack, buffers);
} finally {
poseStack.popPose();
}
}
}
@@ -1,165 +0,0 @@
package com.craisinlord.antarchy.content.client.renderer;
import com.craisinlord.antarchy.mixins.client.LevelRendererPortalViewAreaAccessor;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import net.minecraft.client.Camera;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.SectionOcclusionGraph;
import net.minecraft.client.renderer.ViewArea;
import net.minecraft.client.renderer.chunk.SectionRenderDispatcher;
import net.minecraft.client.renderer.culling.Frustum;
import net.minecraft.core.SectionPos;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.phys.Vec3;
import org.joml.Matrix4f;
public final class PortalGunPortalSectionViews {
private static final int MAX_VIEWS = 8;
private static final double SEED_OFFSET = 0.5D;
private static final LinkedHashMap<UUID, View> VIEWS = new LinkedHashMap<>(8, 0.75F, true);
private static volatile SectionOcclusionGraph[] graphs = new SectionOcclusionGraph[0];
private static ViewArea ownerArea;
private static int mainSectionX = Integer.MIN_VALUE;
private static int mainSectionY = Integer.MIN_VALUE;
private static int mainSectionZ = Integer.MIN_VALUE;
private PortalGunPortalSectionViews() {
}
public static boolean canView(Minecraft minecraft, Vec3 exitCenter) {
ViewArea area = ((LevelRendererPortalViewAreaAccessor) minecraft.levelRenderer).antarchy$getViewArea();
if (area == null) {
return false;
}
LevelRendererPortalViewAreaAccessor accessor = (LevelRendererPortalViewAreaAccessor) minecraft.levelRenderer;
int limit = area.getViewDistance() - 1;
return Math.abs(SectionPos.posToSectionCoord(exitCenter.x) - accessor.antarchy$getLastCameraSectionX()) <= limit
&& Math.abs(SectionPos.posToSectionCoord(exitCenter.z) - accessor.antarchy$getLastCameraSectionZ()) <= limit;
}
public static View prepare(Minecraft minecraft, UUID exitId, Vec3 exitCenter, Vec3 exitNormal, Frustum frustum) {
LevelRendererPortalViewAreaAccessor accessor = (LevelRendererPortalViewAreaAccessor) minecraft.levelRenderer;
ViewArea area = accessor.antarchy$getViewArea();
if (area != ownerArea) {
clear();
ownerArea = area;
}
int sectionX = accessor.antarchy$getLastCameraSectionX();
int sectionY = accessor.antarchy$getLastCameraSectionY();
int sectionZ = accessor.antarchy$getLastCameraSectionZ();
if (sectionX != mainSectionX || sectionY != mainSectionY || sectionZ != mainSectionZ) {
mainSectionX = sectionX;
mainSectionY = sectionY;
mainSectionZ = sectionZ;
for (View view : VIEWS.values()) {
view.graph.invalidate();
}
}
View view = VIEWS.get(exitId);
if (view == null) {
view = new View(area);
VIEWS.put(exitId, view);
trim();
publishGraphs();
}
Vec3 seed = exitCenter.add(exitNormal.normalize().scale(SEED_OFFSET));
if (!seed.equals(view.seed)) {
view.seed = seed;
view.seedCamera = new SeedCamera(seed);
view.updateFrustum = centredFrustum(seed);
view.graph.invalidate();
}
view.partialUpdates.clear();
view.graph.update(minecraft.smartCull, view.seedCamera, view.updateFrustum(), view.partialUpdates);
view.visibleSections.clear();
view.graph.addSectionsInFrustum(frustum, view.visibleSections);
return view;
}
public static void onSectionCompiled(SectionRenderDispatcher.RenderSection section) {
for (SectionOcclusionGraph graph : graphs) {
graph.onSectionCompiled(section);
}
}
public static void onChunkLoaded(ChunkPos pos) {
for (SectionOcclusionGraph graph : graphs) {
graph.onChunkLoaded(pos);
}
}
public static void onChunkDropped(ChunkPos pos) {
for (SectionOcclusionGraph graph : graphs) {
graph.invalidate();
}
}
public static void clear() {
for (View view : VIEWS.values()) {
view.release();
}
VIEWS.clear();
publishGraphs();
ownerArea = null;
mainSectionX = Integer.MIN_VALUE;
mainSectionY = Integer.MIN_VALUE;
mainSectionZ = Integer.MIN_VALUE;
}
private static void publishGraphs() {
graphs = VIEWS.values().stream().map(view -> view.graph).toArray(SectionOcclusionGraph[]::new);
}
private static void trim() {
Iterator<Map.Entry<UUID, View>> iterator = VIEWS.entrySet().iterator();
while (VIEWS.size() > MAX_VIEWS && iterator.hasNext()) {
View view = iterator.next().getValue();
iterator.remove();
view.release();
}
}
public static final class View {
private final SectionOcclusionGraph graph = new SectionOcclusionGraph();
private final List<SectionRenderDispatcher.RenderSection> partialUpdates = new ArrayList<>();
private final ObjectArrayList<SectionRenderDispatcher.RenderSection> visibleSections = new ObjectArrayList<>();
private Vec3 seed;
private Camera seedCamera;
private Frustum updateFrustum;
private View(ViewArea area) {
((PortalSectionGraphMarker) this.graph).antarchy$markPortalGraph();
this.graph.waitAndReset(area);
}
public ObjectArrayList<SectionRenderDispatcher.RenderSection> visibleSections() {
return this.visibleSections;
}
private Frustum updateFrustum() {
return this.updateFrustum;
}
private void release() {
this.graph.waitAndReset(null);
}
}
private static Frustum centredFrustum(Vec3 position) {
Frustum frustum = new Frustum(new Matrix4f(), new Matrix4f().setPerspective((float) Math.toRadians(90.0D), 1.0F, 0.05F, 1024.0F));
frustum.prepare(position.x, position.y, position.z);
return frustum;
}
private static final class SeedCamera extends Camera {
private SeedCamera(Vec3 position) {
this.setPosition(position);
}
}
}
@@ -1,129 +0,0 @@
package com.craisinlord.antarchy.content.client.renderer;
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 com.mojang.blaze3d.vertex.VertexSorting;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.UUID;
import net.minecraft.client.renderer.GameRenderer;
import org.joml.Matrix4f;
import org.joml.Matrix4fStack;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL15;
public final class PortalGunPortalSkyQueries {
private static final int MAX_ENTRIES = 128;
private static final LinkedHashMap<Key, QueryState> QUERIES = new LinkedHashMap<>(16, 0.75F, true);
private PortalGunPortalSkyQueries() {
}
public static boolean shouldRenderSky(UUID source, UUID destination, int level) {
QueryState state = QUERIES.get(new Key(source, destination, level));
if (state == null) {
return true;
}
poll(state);
return !state.hasResult || state.visible;
}
public static void querySkyVisibility(UUID source, UUID destination, int level) {
Key key = new Key(source, destination, level);
QueryState state = QUERIES.computeIfAbsent(key, ignored -> new QueryState(GL15.glGenQueries()));
poll(state);
if (state.inFlight) {
trim();
return;
}
MeshData mesh = buildFarPlane();
if (mesh == null) {
trim();
return;
}
Matrix4fStack modelViewStack = RenderSystem.getModelViewStack();
Matrix4f previousProjection = new Matrix4f(RenderSystem.getProjectionMatrix());
VertexSorting previousSorting = RenderSystem.getVertexSorting();
int previousDepthFunction = GL11.glGetInteger(GL11.GL_DEPTH_FUNC);
modelViewStack.pushMatrix();
GL15.glBeginQuery(GL15.GL_SAMPLES_PASSED, state.query);
try {
RenderSystem.enableDepthTest();
RenderSystem.depthMask(false);
RenderSystem.colorMask(false, false, false, false);
RenderSystem.disableBlend();
RenderSystem.disableCull();
GL11.glDepthFunc(GL11.GL_LEQUAL);
modelViewStack.identity();
RenderSystem.applyModelViewMatrix();
RenderSystem.setProjectionMatrix(new Matrix4f(), previousSorting);
RenderSystem.setShader(GameRenderer::getPositionShader);
BufferUploader.drawWithShader(mesh);
} finally {
GL15.glEndQuery(GL15.GL_SAMPLES_PASSED);
state.inFlight = true;
GL11.glDepthFunc(previousDepthFunction);
RenderSystem.colorMask(true, true, true, true);
RenderSystem.depthMask(true);
RenderSystem.enableCull();
RenderSystem.setProjectionMatrix(previousProjection, previousSorting);
modelViewStack.popMatrix();
RenderSystem.applyModelViewMatrix();
}
trim();
}
public static void clear() {
for (QueryState state : QUERIES.values()) {
GL15.glDeleteQueries(state.query);
}
QUERIES.clear();
}
private static void poll(QueryState state) {
if (state.inFlight && GL15.glGetQueryObjecti(state.query, GL15.GL_QUERY_RESULT_AVAILABLE) != 0) {
state.visible = GL15.glGetQueryObjecti(state.query, GL15.GL_QUERY_RESULT) != 0;
state.hasResult = true;
state.inFlight = false;
}
}
private static MeshData buildFarPlane() {
BufferBuilder buffer = Tesselator.getInstance().begin(VertexFormat.Mode.TRIANGLES, DefaultVertexFormat.POSITION);
buffer.addVertex(-1.0F, -1.0F, 0.99999F);
buffer.addVertex(3.0F, -1.0F, 0.99999F);
buffer.addVertex(-1.0F, 3.0F, 0.99999F);
return buffer.build();
}
private static void trim() {
Iterator<Map.Entry<Key, QueryState>> iterator = QUERIES.entrySet().iterator();
while (QUERIES.size() > MAX_ENTRIES && iterator.hasNext()) {
QueryState state = iterator.next().getValue();
if (!state.inFlight) {
GL15.glDeleteQueries(state.query);
iterator.remove();
}
}
}
private record Key(UUID source, UUID destination, int level) {
}
private static final class QueryState {
private final int query;
private boolean inFlight;
private boolean hasResult;
private boolean visible = true;
private QueryState(int query) {
this.query = query;
}
}
}
@@ -1,755 +0,0 @@
package com.craisinlord.antarchy.content.client.renderer;
import com.craisinlord.antarchy.Antarchy;
import com.craisinlord.antarchy.config.AntarchySettings;
import com.craisinlord.antarchy.content.client.PortalGunEntityTransformationStack;
import com.craisinlord.antarchy.content.client.PortalGunPortalRenderState;
import com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity;
import com.craisinlord.antarchy.content.portalgun.PortalGunProjectionUtil;
import com.craisinlord.antarchy.content.portalgun.PortalGunTransformUtil;
import com.craisinlord.antarchy.content.portalgun.PortalGunWorldPortalShape;
import com.craisinlord.antarchy.mixins.client.CameraBasisAccessor;
import com.craisinlord.antarchy.mixins.client.MinecraftMainRenderTargetAccessor;
import com.mojang.blaze3d.pipeline.RenderTarget;
import com.mojang.blaze3d.pipeline.TextureTarget;
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.PoseStack;
import com.mojang.blaze3d.vertex.Tesselator;
import com.mojang.blaze3d.vertex.VertexFormat;
import com.mojang.blaze3d.vertex.VertexSorting;
import java.util.ArrayList;
import java.util.Collections;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Set;
import net.minecraft.client.Camera;
import net.minecraft.client.DeltaTracker;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.GameRenderer;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.ShaderInstance;
import net.minecraft.client.renderer.culling.Frustum;
import net.minecraft.util.Mth;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
import org.joml.Matrix4f;
import org.joml.Matrix4fStack;
import org.joml.Quaternionf;
import org.joml.Vector3f;
import org.joml.Vector4f;
import org.lwjgl.opengl.GL11;
public final class PortalGunPortalViewRenderer {
private static final int MAX_VIEW_LEVELS = 2;
private static final double PORTAL_SEARCH_RADIUS = 128.0D;
private static final int PORTAL_TARGET_FILTER = 9729;
private static final double SURFACE_OFFSET = 0.04D;
private static final double EXIT_CLIP_OFFSET = 0.01D;
private static final int APERTURE_SEGMENTS = 32;
private static final double APERTURE_RIM_FRACTION = 0.92D;
private static TextureTarget offscreenTarget;
private static ShaderInstance portalViewShader;
private static boolean renderingPortalView;
private static boolean loggedMissingPortalViewShader;
private static long lastRenderFailureTick = Long.MIN_VALUE;
private PortalGunPortalViewRenderer() {
}
public static void setPortalViewShader(ShaderInstance shader) {
portalViewShader = shader;
}
public static void clearPortalViewShader() {
portalViewShader = null;
}
public static boolean isEnabled() {
return AntarchySettings.portalGunSeeThroughPortals();
}
public static void releaseTargets() {
PortalGunPortalVisibilityQueries.clear();
PortalGunPortalSkyQueries.clear();
PortalGunPortalSectionViews.clear();
if (offscreenTarget != null) {
offscreenTarget.destroyBuffers();
offscreenTarget = null;
}
}
public static void render(Camera camera, Matrix4f poseMatrix, Matrix4f projectionMatrix, DeltaTracker tickCounter) {
if (renderingPortalView) {
return;
}
renderHiddenOutlines(camera, poseMatrix, projectionMatrix, tickCounter);
if (isEnabled()) {
renderViews(camera, poseMatrix, projectionMatrix, tickCounter);
}
}
private static void renderHiddenOutlines(Camera camera, Matrix4f poseMatrix, Matrix4f projectionMatrix, DeltaTracker tickCounter) {
Minecraft minecraft = Minecraft.getInstance();
if (minecraft.level == null || camera == null || poseMatrix == null || projectionMatrix == null || tickCounter == null) {
return;
}
try {
minecraft.getMainRenderTarget().bindWrite(true);
PortalGunPortalRim.drawHiddenOutlines(minecraft, camera.getPosition(), new Matrix4f(poseMatrix), new Matrix4f(projectionMatrix),
tickCounter.getGameTimeDeltaPartialTick(true));
} catch (RuntimeException exception) {
logRenderFailure(minecraft, exception);
}
}
private static void renderViews(Camera camera, Matrix4f poseMatrix, Matrix4f projectionMatrix, DeltaTracker tickCounter) {
PortalRenderWatchdog.frame();
Minecraft minecraft = Minecraft.getInstance();
if (minecraft.level == null || minecraft.gameRenderer == null || camera == null || poseMatrix == null || projectionMatrix == null || tickCounter == null) {
return;
}
Entity cameraEntity = minecraft.getCameraEntity();
if (cameraEntity == null) {
return;
}
Vec3 cameraPos = camera.getPosition();
Matrix4f rootViewMatrix = new Matrix4f(poseMatrix);
Matrix4f rootProjection = new Matrix4f(projectionMatrix);
float partialTick = tickCounter.getGameTimeDeltaPartialTick(true);
RenderTarget mainTarget = minecraft.getMainRenderTarget();
int width = mainTarget.width;
int height = mainTarget.height;
List<PortalGunPortalEntity> portals = collectPortals(minecraft, cameraPos);
if (portals.isEmpty()) {
return;
}
List<RootCandidate> candidates = new ArrayList<>();
for (PortalGunPortalEntity portal : portals) {
boolean moon = portal.isMoonPortal();
PortalGunPortalEntity linked = moon ? null : portal.getLinkedPortal();
if (!moon && (linked == null || !linked.isAlive()) || !portal.shouldRenderFront(cameraPos)) {
continue;
}
ScreenClip clip = computeScreenClip(cameraPos, rootViewMatrix, rootProjection, portal, partialTick, width, height);
if (clip != null) {
candidates.add(new RootCandidate(portal, linked, clip));
}
}
candidates.sort((left, right) -> Integer.compare(right.clip().area(), left.clip().area()));
int maxRootViews = AntarchySettings.portalGunMaxLiveViews();
if (candidates.size() > maxRootViews) {
candidates = candidates.subList(0, maxRootViews);
}
if (candidates.isEmpty()) {
return;
}
boolean directStencil = PortalStencilTarget.supportsDirect(mainTarget);
if (!directStencil && portalViewShader == null) {
if (!loggedMissingPortalViewShader) {
loggedMissingPortalViewShader = true;
Antarchy.LOGGER.error("Portal gun destination view shader is unavailable; skipping offscreen portal rendering");
}
return;
}
Matrix4f cameraToWorld = new Matrix4f(rootViewMatrix).invert();
Vector3f rootLook = cameraToWorld.transformDirection(new Vector3f(0.0F, 0.0F, -1.0F)).normalize();
Vector3f rootUp = cameraToWorld.transformDirection(new Vector3f(0.0F, 1.0F, 0.0F)).normalize();
RenderContext rootContext = new RenderContext(cameraPos, new Vec3(rootLook), new Vec3(rootUp), rootViewMatrix);
ViewBudget budget = new ViewBudget(maxRootViews * 2);
PortalRenderWatchdog.portalViewDrawn();
renderingPortalView = true;
RenderSystem.backupProjectionMatrix();
try {
if (directStencil) {
PortalStencilTarget.beginFrame(mainTarget);
}
for (RootCandidate candidate : candidates) {
if (!budget.tryReserve()) {
break;
}
try {
if (candidate.linked() == null) {
if (directStencil) {
renderMoonViewDirect(minecraft, partialTick, rootContext, rootProjection, candidate.portal(), candidate.clip());
} else {
renderMoonViewOffscreen(minecraft, partialTick, rootContext, rootProjection, candidate.portal(), candidate.clip());
}
} else if (directStencil) {
renderViewDirect(minecraft, cameraEntity, partialTick, rootContext, rootProjection, rootProjection,
candidate.portal(), candidate.linked(), 0, candidate.clip(), null, budget);
} else {
renderViewOffscreen(minecraft, cameraEntity, partialTick, rootContext, rootProjection,
candidate.portal(), candidate.linked(), candidate.clip());
}
} catch (RuntimeException exception) {
logRenderFailure(minecraft, exception);
if (directStencil) {
PortalStencilTarget.beginFrame(mainTarget);
}
}
}
} catch (RuntimeException exception) {
logRenderFailure(minecraft, exception);
} finally {
try {
if (directStencil) {
PortalStencilTarget.endFrame();
}
restorePortalDrawState();
mainTarget.bindWrite(true);
minecraft.getEntityRenderDispatcher().prepare(minecraft.level, camera, minecraft.crosshairPickEntity);
minecraft.getBlockEntityRenderDispatcher().prepare(minecraft.level, camera, minecraft.hitResult);
} catch (RuntimeException exception) {
logRenderFailure(minecraft, exception);
} finally {
RenderSystem.restoreProjectionMatrix();
renderingPortalView = false;
}
}
}
private static void renderViewDirect(Minecraft minecraft, Entity cameraEntity, float partialTick, RenderContext context,
Matrix4f rootProjection, Matrix4f parentProjection,
PortalGunPortalEntity source, PortalGunPortalEntity destination,
int parentLevel, ScreenClip clip, ScreenClip parentClip, ViewBudget budget) {
PortalGunWorldPortalShape exit = destination.getWorldPortalShape();
if (!PortalTerrain.canView(minecraft, exit.center())) {
return;
}
boolean root = parentLevel == 0;
boolean visible = !root || PortalGunPortalVisibilityQueries.shouldRender(source.getUUID(), destination.getUUID());
RenderTarget target = minecraft.getMainRenderTarget();
PortalStencilTarget.pushAperture(parentLevel, () -> {
boolean queryStarted = root && PortalGunPortalVisibilityQueries.begin(source.getUUID(), destination.getUUID());
try {
drawPortalMask(context.cameraPos(), context.viewMatrix(), parentProjection, source, partialTick);
} finally {
if (root) {
PortalGunPortalVisibilityQueries.end(source.getUUID(), destination.getUUID(), queryStarted);
}
}
});
try {
if (!visible) {
return;
}
RenderContext transformed = transformContext(context, source, destination);
Matrix4f clippedProjection = applyPortalClipPlane(rootProjection, transformed, destination);
RenderSystem.enableScissor(clip.x(), clip.y(), clip.width(), clip.height());
renderScene(minecraft, cameraEntity, partialTick, transformed, rootProjection, clippedProjection,
source, destination, clip, target.width, target.height, parentLevel + 1);
PortalGunPortalRim.drawHiddenOutlines(minecraft, transformed.cameraPos(), transformed.viewMatrix(), clippedProjection,
partialTick, destination);
if (parentLevel + 1 < MAX_VIEW_LEVELS) {
renderNestedViews(minecraft, cameraEntity, partialTick, transformed, rootProjection, clippedProjection,
destination, parentLevel + 1, clip, budget, target.width, target.height);
}
} finally {
try {
if (visible) {
PortalStencilTarget.drawInside(parentLevel + 1);
PortalGunPortalRim.drawOverView(source, context.cameraPos(), context.viewMatrix(), parentProjection, partialTick);
}
} finally {
PortalStencilTarget.popAperture(parentLevel,
() -> restorePortalApertureDepth(context.cameraPos(), context.viewMatrix(), parentProjection, source, partialTick));
}
if (parentClip == null) {
RenderSystem.disableScissor();
} else {
RenderSystem.enableScissor(parentClip.x(), parentClip.y(), parentClip.width(), parentClip.height());
}
}
}
private static void renderMoonViewDirect(Minecraft minecraft, float partialTick, RenderContext context,
Matrix4f rootProjection, PortalGunPortalEntity source, ScreenClip clip) {
PortalStencilTarget.pushAperture(0, () -> drawPortalMask(context.cameraPos(), context.viewMatrix(), rootProjection, source, partialTick));
try {
RenderSystem.enableScissor(clip.x(), clip.y(), clip.width(), clip.height());
PortalGunMoonViewRenderer.render(minecraft, source.getWorldPortalShape(), context.cameraPos(), context.look(), context.up(),
rootProjection, partialTick);
} finally {
try {
PortalStencilTarget.drawInside(1);
PortalGunPortalRim.drawOverView(source, context.cameraPos(), context.viewMatrix(), rootProjection, partialTick);
} finally {
PortalStencilTarget.popAperture(0,
() -> restorePortalApertureDepth(context.cameraPos(), context.viewMatrix(), rootProjection, source, partialTick));
RenderSystem.disableScissor();
}
}
}
private static void renderMoonViewOffscreen(Minecraft minecraft, float partialTick, RenderContext context,
Matrix4f rootProjection, PortalGunPortalEntity source, ScreenClip clip) {
RenderTarget mainTarget = minecraft.getMainRenderTarget();
TextureTarget target = ensureOffscreenTarget(mainTarget.width, mainTarget.height);
MinecraftMainRenderTargetAccessor targetAccessor = (MinecraftMainRenderTargetAccessor) minecraft;
targetAccessor.antarchy$setMainRenderTarget(target);
try {
target.bindWrite(true);
RenderSystem.disableScissor();
target.clear(Minecraft.ON_OSX);
target.bindWrite(true);
RenderSystem.enableScissor(clip.x(), clip.y(), clip.width(), clip.height());
PortalGunMoonViewRenderer.render(minecraft, source.getWorldPortalShape(), context.cameraPos(), context.look(), context.up(),
rootProjection, partialTick);
} finally {
RenderSystem.disableScissor();
targetAccessor.antarchy$setMainRenderTarget(mainTarget);
mainTarget.bindWrite(true);
}
RenderSystem.setProjectionMatrix(rootProjection, VertexSorting.DISTANCE_TO_ORIGIN);
drawPortalComposite(context.cameraPos(), context.viewMatrix(), rootProjection, source, partialTick, target.getColorTextureId());
}
private static void renderNestedViews(Minecraft minecraft, Entity cameraEntity, float partialTick, RenderContext viewContext,
Matrix4f rootProjection, Matrix4f viewProjection, PortalGunPortalEntity exit,
int level, ScreenClip viewClip, ViewBudget budget, int width, int height) {
PortalGunWorldPortalShape exitShape = exit.getWorldPortalShape();
for (PortalGunPortalEntity child : collectPortals(minecraft, viewContext.cameraPos())) {
if (!budget.hasRemaining()) {
return;
}
PortalGunPortalEntity childLinked = child.getLinkedPortal();
if (child == exit || childLinked == null || !childLinked.isAlive()
|| !child.shouldRenderFront(viewContext.cameraPos())
|| !exitShape.intersectsFront(child.getBoundingBox(), 0.0D)) {
continue;
}
ScreenClip childClip = computeScreenClip(viewContext.cameraPos(), viewContext.viewMatrix(), viewProjection,
child, partialTick, width, height);
childClip = childClip == null ? null : childClip.intersect(viewClip);
if (childClip == null || !budget.tryReserve()) {
continue;
}
renderViewDirect(minecraft, cameraEntity, partialTick, viewContext, rootProjection, viewProjection,
child, childLinked, level, childClip, viewClip, budget);
}
}
private static void renderViewOffscreen(Minecraft minecraft, Entity cameraEntity, float partialTick, RenderContext context,
Matrix4f rootProjection, PortalGunPortalEntity source,
PortalGunPortalEntity destination, ScreenClip clip) {
if (!PortalTerrain.canView(minecraft, destination.getWorldPortalShape().center())) {
return;
}
RenderTarget mainTarget = minecraft.getMainRenderTarget();
TextureTarget target = ensureOffscreenTarget(mainTarget.width, mainTarget.height);
RenderContext transformed = transformContext(context, source, destination);
Matrix4f clippedProjection = applyPortalClipPlane(rootProjection, transformed, destination);
MinecraftMainRenderTargetAccessor targetAccessor = (MinecraftMainRenderTargetAccessor) minecraft;
targetAccessor.antarchy$setMainRenderTarget(target);
try {
target.bindWrite(true);
RenderSystem.disableScissor();
target.clear(Minecraft.ON_OSX);
target.bindWrite(true);
RenderSystem.enableScissor(clip.x(), clip.y(), clip.width(), clip.height());
renderScene(minecraft, cameraEntity, partialTick, transformed, rootProjection, clippedProjection,
source, destination, clip, target.width, target.height, 1);
PortalGunPortalRim.drawHiddenOutlines(minecraft, transformed.cameraPos(), transformed.viewMatrix(), clippedProjection,
partialTick, destination);
} finally {
RenderSystem.disableScissor();
targetAccessor.antarchy$setMainRenderTarget(mainTarget);
mainTarget.bindWrite(true);
}
RenderSystem.setProjectionMatrix(rootProjection, VertexSorting.DISTANCE_TO_ORIGIN);
drawPortalComposite(context.cameraPos(), context.viewMatrix(), rootProjection, source, partialTick, target.getColorTextureId());
}
private static void renderScene(Minecraft minecraft, Entity cameraEntity, float partialTick, RenderContext transformed,
Matrix4f rootProjection, Matrix4f clippedProjection,
PortalGunPortalEntity source, PortalGunPortalEntity destination,
ScreenClip clip, int width, int height, int level) {
PortalGunWorldPortalShape exit = destination.getWorldPortalShape();
PortalCamera portalCamera = new PortalCamera();
portalCamera.setup(minecraft.level, cameraEntity, partialTick, transformed.cameraPos(), transformed.look(), transformed.up());
Frustum frustum = new Frustum(transformed.viewMatrix(), cropProjectionToScreenClip(rootProjection, clip, width, height));
frustum.prepare(transformed.cameraPos().x, transformed.cameraPos().y, transformed.cameraPos().z);
PortalGunPortalRenderState.pushPortalView(new PortalGunPortalRenderState.PortalRenderContext(
source.getWorldPortalShape(), exit, transformed.cameraPos(), transformed.look(), transformed.up(),
transformed.viewMatrix(), clippedProjection, level, false, destination.getUUID()));
try {
PortalTerrain terrain = PortalTerrain.prepare(minecraft, destination.getUUID(), exit.center(), exit.normal(), frustum);
PortalGunPortalSceneRenderer.render(minecraft, portalCamera, partialTick, transformed.viewMatrix(), clippedProjection,
rootProjection, frustum, terrain,
poseStack -> renderCrossingEntities(minecraft, transformed.cameraPos(), partialTick, source, destination, poseStack),
source.getUUID(), destination.getUUID(), level);
} finally {
PortalGunPortalRenderState.popPortalView(false);
}
}
private static List<PortalGunPortalEntity> collectPortals(Minecraft minecraft, Vec3 cameraPos) {
List<PortalGunPortalEntity> portals = new ArrayList<>();
for (PortalGunPortalEntity portal : com.craisinlord.antarchy.content.portalgun.PortalGunPortalRegistry.all(minecraft.level)) {
if (portal.isAlive() && portal.distanceToSqr(cameraPos) <= PORTAL_SEARCH_RADIUS * PORTAL_SEARCH_RADIUS) {
portals.add(portal);
}
}
portals.sort((left, right) -> Double.compare(left.distanceToSqr(cameraPos), right.distanceToSqr(cameraPos)));
return portals;
}
private static void logRenderFailure(Minecraft minecraft, RuntimeException exception) {
long gameTime = minecraft.level == null ? Long.MIN_VALUE : minecraft.level.getGameTime();
if (lastRenderFailureTick == Long.MIN_VALUE || gameTime == Long.MIN_VALUE || gameTime - lastRenderFailureTick >= 100L) {
lastRenderFailureTick = gameTime;
Antarchy.LOGGER.error("Portal gun portal view render failed", exception);
}
}
private static TextureTarget ensureOffscreenTarget(int width, int height) {
if (offscreenTarget == null) {
offscreenTarget = new TextureTarget(width, height, true, Minecraft.ON_OSX);
offscreenTarget.setClearColor(0.0F, 0.0F, 0.0F, 1.0F);
} else if (offscreenTarget.width != width || offscreenTarget.height != height) {
offscreenTarget.resize(width, height, Minecraft.ON_OSX);
}
offscreenTarget.setFilterMode(PORTAL_TARGET_FILTER);
return offscreenTarget;
}
private static RenderContext transformContext(RenderContext context, PortalGunPortalEntity source, PortalGunPortalEntity destination) {
PortalGunWorldPortalShape sourceShape = source.getWorldPortalShape();
PortalGunWorldPortalShape destinationShape = destination.getWorldPortalShape();
Vec3 relativeEye = context.cameraPos().subtract(sourceShape.center());
Vec3 destinationEye = destinationShape.center().add(PortalGunTransformUtil.transformPosition(sourceShape, destinationShape, relativeEye));
Vec3 look = PortalGunTransformUtil.transformVector(sourceShape, destinationShape, context.look()).normalize();
Vec3 up = PortalGunTransformUtil.transformVector(sourceShape, destinationShape, context.up()).normalize();
Matrix4f viewMatrix = new Matrix4f().rotation(PortalGunTransformUtil.orientationQuaternion(look, up).conjugate(new Quaternionf()));
return new RenderContext(destinationEye, look, up, viewMatrix);
}
private static Matrix4f applyPortalClipPlane(Matrix4f projection, RenderContext context, PortalGunPortalEntity destination) {
PortalGunWorldPortalShape exit = destination.getWorldPortalShape();
return PortalGunProjectionUtil.clipAtExit(projection, context.cameraPos(), context.look(), context.up(),
exit.center().add(exit.normal().scale(EXIT_CLIP_OFFSET)), exit.normal());
}
private static ScreenClip computeScreenClip(Vec3 cameraPos, Matrix4f viewMatrix, Matrix4f projectionMatrix,
PortalGunPortalEntity portal, float partialTick, int width, int height) {
List<Vector4f> clipVertices = projectPortalPolygon(new Matrix4f(projectionMatrix).mul(viewMatrix), cameraPos, portal, partialTick);
if (clipVertices.size() < 3) {
return null;
}
float minX = Float.POSITIVE_INFINITY;
float minY = Float.POSITIVE_INFINITY;
float maxX = Float.NEGATIVE_INFINITY;
float maxY = Float.NEGATIVE_INFINITY;
for (Vector4f projected : clipVertices) {
float invW = 1.0F / projected.w;
float screenX = (projected.x * invW * 0.5F + 0.5F) * width;
float screenY = (projected.y * invW * 0.5F + 0.5F) * height;
minX = Math.min(minX, screenX);
minY = Math.min(minY, screenY);
maxX = Math.max(maxX, screenX);
maxY = Math.max(maxY, screenY);
}
if (maxX <= 0.0F || minX >= width || maxY <= 0.0F || minY >= height) {
return null;
}
int x = Mth.clamp((int) Math.floor(minX), 0, width - 1);
int maxClipX = Mth.clamp((int) Math.ceil(maxX), x + 1, width);
int y = Mth.clamp((int) Math.floor(minY), 0, height - 1);
int maxClipY = Mth.clamp((int) Math.ceil(maxY), y + 1, height);
return new ScreenClip(x, y, maxClipX - x, maxClipY - y);
}
private static Matrix4f cropProjectionToScreenClip(Matrix4f projectionMatrix, ScreenClip clip, int width, int height) {
float left = (float) (clip.x() * 2.0D / width - 1.0D);
float right = (float) ((clip.x() + clip.width()) * 2.0D / width - 1.0D);
float bottom = (float) (clip.y() * 2.0D / height - 1.0D);
float top = (float) ((clip.y() + clip.height()) * 2.0D / height - 1.0D);
float widthNdc = Math.max(1.0E-5F, right - left);
float heightNdc = Math.max(1.0E-5F, top - bottom);
return new Matrix4f()
.translate(-(right + left) / widthNdc, -(top + bottom) / heightNdc, 0.0F)
.scale(2.0F / widthNdc, 2.0F / heightNdc, 1.0F)
.mul(projectionMatrix);
}
private static List<Vector4f> clipPolygon(List<Vector4f> vertices, boolean nearPlane) {
if (vertices.isEmpty()) {
return vertices;
}
List<Vector4f> clipped = new ArrayList<>(vertices.size() + 2);
Vector4f previous = vertices.get(vertices.size() - 1);
float previousDistance = clipPlaneDistance(previous, nearPlane);
for (Vector4f current : vertices) {
float currentDistance = clipPlaneDistance(current, nearPlane);
boolean previousInside = previousDistance >= 0.0F;
boolean currentInside = currentDistance >= 0.0F;
if (previousInside != currentInside) {
float amount = previousDistance / (previousDistance - currentDistance);
clipped.add(new Vector4f(
previous.x + (current.x - previous.x) * amount,
previous.y + (current.y - previous.y) * amount,
previous.z + (current.z - previous.z) * amount,
previous.w + (current.w - previous.w) * amount
));
}
if (currentInside) {
clipped.add(new Vector4f(current));
}
previous = current;
previousDistance = currentDistance;
}
return clipped;
}
private static float clipPlaneDistance(Vector4f vertex, boolean nearPlane) {
return nearPlane ? vertex.z + vertex.w : vertex.w - 0.001F;
}
private static List<Vector4f> projectPortalPolygon(Matrix4f combined, Vec3 cameraPos, PortalGunPortalEntity portal, float partialTick) {
Vec3[] corners = apertureOutline(portal, partialTick);
List<Vector4f> polygon = new ArrayList<>(corners.length);
for (Vec3 corner : corners) {
Vec3 relative = corner.subtract(cameraPos);
polygon.add(new Vector4f((float) relative.x, (float) relative.y, (float) relative.z, 1.0F).mul(combined));
}
polygon = clipPolygon(polygon, false);
return clipPolygon(polygon, true);
}
static Vec3[] apertureOutline(PortalGunPortalEntity portal, float partialTick) {
float scale = portal.getPortalVisualScale(partialTick);
PortalGunWorldPortalShape shape = portal.getWorldPortalShape().scaleAperture(scale);
double radiusHorizontal = Math.min(shape.halfWidth(), (shape.halfWidth() + PortalGunPortalRim.EXTRA_WIDTH * scale) * APERTURE_RIM_FRACTION);
double radiusVertical = Math.min(shape.halfHeight(), (shape.halfHeight() + PortalGunPortalRim.EXTRA_HEIGHT * scale) * APERTURE_RIM_FRACTION);
return shape.getOutline(SURFACE_OFFSET, APERTURE_SEGMENTS, radiusHorizontal, radiusVertical);
}
private static MeshData buildAperture(Vec3 cameraPos, Matrix4f viewMatrix, Matrix4f projectionMatrix,
PortalGunPortalEntity portal, float partialTick, boolean withScreenUv) {
List<Vector4f> polygon = projectPortalPolygon(new Matrix4f(projectionMatrix).mul(viewMatrix), cameraPos, portal, partialTick);
if (polygon.size() < 3) {
return null;
}
BufferBuilder buffer = Tesselator.getInstance().begin(VertexFormat.Mode.TRIANGLES,
withScreenUv ? DefaultVertexFormat.POSITION_TEX : DefaultVertexFormat.POSITION);
for (int index = 1; index + 1 < polygon.size(); index++) {
addClipVertex(buffer, polygon.get(0), withScreenUv);
addClipVertex(buffer, polygon.get(index), withScreenUv);
addClipVertex(buffer, polygon.get(index + 1), withScreenUv);
}
return buffer.build();
}
private static void addClipVertex(BufferBuilder buffer, Vector4f clipPosition, boolean withScreenUv) {
float inverseW = 1.0F / clipPosition.w();
float ndcX = clipPosition.x() * inverseW;
float ndcY = clipPosition.y() * inverseW;
float ndcZ = clipPosition.z() * inverseW;
if (withScreenUv) {
buffer.addVertex(ndcX, ndcY, ndcZ).setUv(ndcX * 0.5F + 0.5F, ndcY * 0.5F + 0.5F);
} else {
buffer.addVertex(ndcX, ndcY, ndcZ);
}
}
private static void drawPortalMask(Vec3 cameraPos, Matrix4f viewMatrix, Matrix4f projectionMatrix, PortalGunPortalEntity portal, float partialTick) {
drawClipSpaceMesh(buildAperture(cameraPos, viewMatrix, projectionMatrix, portal, partialTick, false));
}
private static void restorePortalApertureDepth(Vec3 cameraPos, Matrix4f viewMatrix, Matrix4f projectionMatrix,
PortalGunPortalEntity portal, float partialTick) {
MeshData mesh = buildAperture(cameraPos, viewMatrix, projectionMatrix, portal, partialTick, false);
int previousDepthFunction = GL11.glGetInteger(GL11.GL_DEPTH_FUNC);
GL11.glDepthFunc(GL11.GL_ALWAYS);
try {
drawClipSpaceMesh(mesh);
} finally {
GL11.glDepthFunc(previousDepthFunction);
}
}
private static void drawClipSpaceMesh(MeshData mesh) {
if (mesh == null) {
return;
}
Matrix4fStack modelViewStack = RenderSystem.getModelViewStack();
Matrix4f previousProjection = new Matrix4f(RenderSystem.getProjectionMatrix());
VertexSorting previousSorting = RenderSystem.getVertexSorting();
modelViewStack.pushMatrix();
try {
RenderSystem.disableCull();
RenderSystem.disableBlend();
modelViewStack.identity();
RenderSystem.applyModelViewMatrix();
RenderSystem.setProjectionMatrix(new Matrix4f(), previousSorting);
RenderSystem.setShader(GameRenderer::getPositionShader);
RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F);
BufferUploader.drawWithShader(mesh);
} finally {
RenderSystem.setProjectionMatrix(previousProjection, previousSorting);
modelViewStack.popMatrix();
RenderSystem.applyModelViewMatrix();
RenderSystem.enableCull();
}
}
private static void drawPortalComposite(Vec3 cameraPos, Matrix4f viewMatrix, Matrix4f projectionMatrix,
PortalGunPortalEntity portal, float partialTick, int textureId) {
MeshData mesh = buildAperture(cameraPos, viewMatrix, projectionMatrix, portal, partialTick, true);
if (mesh == null || portalViewShader == null) {
if (mesh != null) {
mesh.close();
}
return;
}
Matrix4fStack modelViewStack = RenderSystem.getModelViewStack();
Matrix4f previousProjection = new Matrix4f(RenderSystem.getProjectionMatrix());
VertexSorting previousSorting = RenderSystem.getVertexSorting();
modelViewStack.pushMatrix();
try {
RenderSystem.disableBlend();
RenderSystem.depthMask(false);
RenderSystem.enableDepthTest();
RenderSystem.disableCull();
modelViewStack.identity();
RenderSystem.applyModelViewMatrix();
RenderSystem.setProjectionMatrix(new Matrix4f(), previousSorting);
portalViewShader.setSampler("Sampler0", textureId);
RenderSystem.setShader(() -> portalViewShader);
RenderSystem.setShaderTexture(0, textureId);
RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F);
BufferUploader.drawWithShader(mesh);
} finally {
RenderSystem.setProjectionMatrix(previousProjection, previousSorting);
modelViewStack.popMatrix();
RenderSystem.applyModelViewMatrix();
restorePortalDrawState();
}
}
private static void renderCrossingEntities(Minecraft minecraft, Vec3 cameraPos, float partialTick,
PortalGunPortalEntity sourcePortal, PortalGunPortalEntity destinationPortal, PoseStack poseStack) {
MultiBufferSource.BufferSource bufferSource = PortalGunPortalSceneRenderer.bufferSource();
List<Entity> entities = minecraft.level.getEntities((Entity) null, sourcePortal.getWorldPortalShape().getBoundsForCulling(),
entity -> isCrossing(entity, sourcePortal, destinationPortal));
Set<Entity> rendered = Collections.newSetFromMap(new IdentityHashMap<>());
try {
for (Entity entity : entities) {
renderCrossingEntityTree(minecraft, cameraPos, partialTick, sourcePortal, destinationPortal, entity, poseStack, bufferSource, rendered);
}
} finally {
bufferSource.endBatch();
}
}
private static void renderCrossingEntityTree(Minecraft minecraft, Vec3 cameraPos, float partialTick, PortalGunPortalEntity sourcePortal,
PortalGunPortalEntity destinationPortal, Entity entity, PoseStack poseStack,
MultiBufferSource.BufferSource bufferSource, Set<Entity> rendered) {
if (!isCrossing(entity, sourcePortal, destinationPortal) || !rendered.add(entity)) {
return;
}
PortalGunEntityTransformationStack transformationStack = new PortalGunEntityTransformationStack(entity);
transformationStack.push();
try {
Vec3 position = transformationStack.moveEntity(sourcePortal, destinationPortal, partialTick);
float yaw = Mth.rotLerp(partialTick, entity.yRotO, entity.getYRot());
int light = minecraft.getEntityRenderDispatcher().getPackedLightCoords(entity, partialTick);
PortalGunPortalRenderState.pushDestinationShape(destinationPortal.getWorldPortalShape());
try {
minecraft.getEntityRenderDispatcher().render(entity, position.x - cameraPos.x, position.y - cameraPos.y,
position.z - cameraPos.z, yaw, partialTick, poseStack, bufferSource, light);
} finally {
PortalGunPortalRenderState.popDestinationShape();
}
} finally {
transformationStack.pop();
}
for (Entity passenger : entity.getPassengers()) {
renderCrossingEntityTree(minecraft, cameraPos, partialTick, sourcePortal, destinationPortal, passenger, poseStack, bufferSource, rendered);
}
}
private static boolean isCrossing(Entity entity, PortalGunPortalEntity sourcePortal, PortalGunPortalEntity destinationPortal) {
return entity != null
&& entity.isAlive()
&& !(entity instanceof PortalGunPortalEntity)
&& entity != sourcePortal
&& entity != destinationPortal
&& entity != Minecraft.getInstance().getCameraEntity()
&& sourcePortal.intersectsEntityBounds(entity);
}
private static void restorePortalDrawState() {
RenderSystem.disableScissor();
RenderSystem.enableCull();
RenderSystem.enableDepthTest();
RenderSystem.depthMask(true);
RenderSystem.colorMask(true, true, true, true);
RenderSystem.defaultBlendFunc();
RenderSystem.disableBlend();
RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F);
}
private static final class PortalCamera extends Camera {
private void setup(net.minecraft.world.level.BlockGetter level, Entity cameraEntity, float partialTick, Vec3 position, Vec3 look, Vec3 up) {
super.setup(level, cameraEntity, true, false, partialTick);
Vec3 normalizedLook = look.normalize();
Vec3 normalizedUp = up.subtract(normalizedLook.scale(up.dot(normalizedLook))).normalize();
this.setPosition(position);
this.setRotation(PortalGunTransformUtil.yawFromLook(normalizedLook), PortalGunTransformUtil.pitchFromLook(normalizedLook));
Quaternionf orientation = PortalGunTransformUtil.orientationQuaternion(normalizedLook, normalizedUp);
this.rotation().set(orientation);
CameraBasisAccessor basis = (CameraBasisAccessor) (Object) this;
basis.antarchy$getForwards().set(0.0F, 0.0F, -1.0F).rotate(orientation);
basis.antarchy$getUp().set(0.0F, 1.0F, 0.0F).rotate(orientation);
basis.antarchy$getLeft().set(-1.0F, 0.0F, 0.0F).rotate(orientation);
}
}
private record RenderContext(Vec3 cameraPos, Vec3 look, Vec3 up, Matrix4f viewMatrix) {
}
private record RootCandidate(PortalGunPortalEntity portal, PortalGunPortalEntity linked, ScreenClip clip) {
}
private static final class ViewBudget {
private int remaining;
private ViewBudget(int maximumViews) {
this.remaining = maximumViews;
}
private boolean tryReserve() {
if (this.remaining <= 0) {
return false;
}
this.remaining--;
return true;
}
private boolean hasRemaining() {
return this.remaining > 0;
}
}
private record ScreenClip(int x, int y, int width, int height) {
private int area() {
return this.width * this.height;
}
private ScreenClip intersect(ScreenClip other) {
int minX = Math.max(this.x, other.x);
int minY = Math.max(this.y, other.y);
int maxX = Math.min(this.x + this.width, other.x + other.width);
int maxY = Math.min(this.y + this.height, other.y + other.height);
return maxX <= minX || maxY <= minY ? null : new ScreenClip(minX, minY, maxX - minX, maxY - minY);
}
}
}
@@ -1,92 +0,0 @@
package com.craisinlord.antarchy.content.client.renderer;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.UUID;
import org.lwjgl.opengl.GL15;
public final class PortalGunPortalVisibilityQueries {
private static final int MAX_ENTRIES = 128;
private static final LinkedHashMap<Pair, QueryState> QUERIES = new LinkedHashMap<>(16, 0.75F, true);
private PortalGunPortalVisibilityQueries() {
}
public static boolean shouldRender(UUID source, UUID destination) {
QueryState state = QUERIES.get(new Pair(source, destination));
if (state == null) {
return true;
}
if (state.inFlight && GL15.glGetQueryObjecti(state.query, GL15.GL_QUERY_RESULT_AVAILABLE) != 0) {
state.visible = GL15.glGetQueryObjecti(state.query, GL15.GL_QUERY_RESULT) != 0;
state.hasResult = true;
state.inFlight = false;
}
return !state.hasResult || state.visible;
}
public static boolean begin(UUID source, UUID destination) {
Pair pair = new Pair(source, destination);
QueryState state = QUERIES.computeIfAbsent(pair, key -> new QueryState(GL15.glGenQueries()));
if (state.inFlight || state.active) {
trim();
return false;
}
state.active = true;
try {
GL15.glBeginQuery(GL15.GL_SAMPLES_PASSED, state.query);
} catch (RuntimeException | Error exception) {
state.active = false;
throw exception;
}
trim();
return true;
}
public static void end(UUID source, UUID destination, boolean started) {
if (!started) {
return;
}
QueryState state = QUERIES.get(new Pair(source, destination));
if (state == null || !state.active) {
return;
}
GL15.glEndQuery(GL15.GL_SAMPLES_PASSED);
state.active = false;
state.inFlight = true;
}
public static void clear() {
for (QueryState state : QUERIES.values()) {
GL15.glDeleteQueries(state.query);
}
QUERIES.clear();
}
private static void trim() {
Iterator<Map.Entry<Pair, QueryState>> iterator = QUERIES.entrySet().iterator();
while (QUERIES.size() > MAX_ENTRIES && iterator.hasNext()) {
QueryState state = iterator.next().getValue();
if (!state.active) {
GL15.glDeleteQueries(state.query);
iterator.remove();
}
}
}
private record Pair(UUID source, UUID destination) {
}
private static final class QueryState {
private final int query;
private boolean active;
private boolean inFlight;
private boolean hasResult;
private boolean visible = true;
private QueryState(int query) {
this.query = query;
}
}
}
@@ -1,56 +0,0 @@
package com.craisinlord.antarchy.content.client.renderer;
import com.craisinlord.antarchy.Antarchy;
final class PortalRenderWatchdog {
private static final long STALL_NANOS = 5_000_000_000L;
private static final long RECENT_PORTAL_NANOS = 10_000_000_000L;
private static volatile Thread renderThread;
private static volatile long lastFrameNanos;
private static volatile long lastPortalViewNanos = Long.MIN_VALUE;
private static volatile boolean reportedStall;
private static Thread watcher;
private PortalRenderWatchdog() {
}
static void frame() {
lastFrameNanos = System.nanoTime();
reportedStall = false;
if (watcher == null) {
renderThread = Thread.currentThread();
watcher = new Thread(PortalRenderWatchdog::watch, "Antarchy portal render watchdog");
watcher.setDaemon(true);
watcher.start();
}
}
static void portalViewDrawn() {
lastPortalViewNanos = System.nanoTime();
}
private static void watch() {
while (true) {
try {
Thread.sleep(1000L);
} catch (InterruptedException exception) {
return;
}
long now = System.nanoTime();
long lastFrame = lastFrameNanos;
long lastPortal = lastPortalViewNanos;
Thread thread = renderThread;
if (reportedStall || thread == null || lastPortal == Long.MIN_VALUE
|| now - lastFrame < STALL_NANOS || lastFrame - lastPortal > RECENT_PORTAL_NANOS) {
continue;
}
reportedStall = true;
StringBuilder stack = new StringBuilder();
for (StackTraceElement element : thread.getStackTrace()) {
stack.append("\n\tat ").append(element);
}
Antarchy.LOGGER.warn("Render thread has not finished a frame for {} ms after portal views were drawn (state {}). Stack:{}",
(now - lastFrame) / 1_000_000L, thread.getState(), stack);
}
}
}
@@ -1,5 +0,0 @@
package com.craisinlord.antarchy.content.client.renderer;
public interface PortalSectionGraphMarker {
void antarchy$markPortalGraph();
}
@@ -1,170 +0,0 @@
package com.craisinlord.antarchy.content.client.renderer;
import com.craisinlord.antarchy.Antarchy;
import com.mojang.blaze3d.pipeline.RenderTarget;
import com.mojang.blaze3d.systems.RenderSystem;
import java.util.Collections;
import java.util.IdentityHashMap;
import java.util.Map;
import java.util.Objects;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL30;
public final class PortalStencilTarget {
private static final int GL_INCR = 7682;
private static final int GL_DECR = 7683;
private static final Map<RenderTarget, Attachment> MAIN_STENCIL_ATTACHMENTS = Collections.synchronizedMap(new IdentityHashMap<>());
private static final Map<RenderTarget, Integer> UNSUPPORTED_MAIN_TARGETS = Collections.synchronizedMap(new IdentityHashMap<>());
private PortalStencilTarget() {
}
public static void prepareMainTarget(RenderTarget target) {
target.bindWrite(true);
if (stencilBits() > 0) {
UNSUPPORTED_MAIN_TARGETS.remove(target);
return;
}
if (Objects.equals(UNSUPPORTED_MAIN_TARGETS.get(target), target.frameBufferId)) {
return;
}
try {
java.lang.reflect.Method enabled = target.getClass().getMethod("isStencilEnabled");
if (!Boolean.TRUE.equals(enabled.invoke(target))) {
target.getClass().getMethod("enableStencil").invoke(target);
}
} catch (ReflectiveOperationException ignored) {
}
target.bindWrite(true);
if (stencilBits() <= 0) {
try {
ensureMainStencilAttachment(target);
} catch (IllegalStateException exception) {
UNSUPPORTED_MAIN_TARGETS.put(target, target.frameBufferId);
Antarchy.LOGGER.debug("Portal gun direct stencil is unavailable ({}); using offscreen portal rendering", exception.getMessage());
}
}
}
public static boolean supportsDirect(RenderTarget target) {
target.bindWrite(true);
return stencilBits() > 0;
}
public static void beginFrame(RenderTarget target) {
target.bindWrite(true);
RenderSystem.disableScissor();
RenderSystem.stencilMask(0xFF);
RenderSystem.clearStencil(0);
GL11.glClear(GL11.GL_STENCIL_BUFFER_BIT);
GL11.glEnable(GL11.GL_STENCIL_TEST);
drawInside(0);
}
public static void endFrame() {
GL11.glDisable(GL11.GL_STENCIL_TEST);
RenderSystem.stencilMask(0xFF);
RenderSystem.stencilFunc(GL11.GL_ALWAYS, 0, 0xFF);
RenderSystem.stencilOp(GL11.GL_KEEP, GL11.GL_KEEP, GL11.GL_KEEP);
RenderSystem.colorMask(true, true, true, true);
RenderSystem.depthMask(true);
RenderSystem.enableDepthTest();
}
public static void pushAperture(int parentLevel, Runnable drawAperture) {
GL11.glEnable(GL11.GL_STENCIL_TEST);
RenderSystem.stencilMask(0xFF);
RenderSystem.stencilFunc(GL11.GL_EQUAL, parentLevel, 0xFF);
RenderSystem.stencilOp(GL11.GL_KEEP, GL11.GL_KEEP, GL_INCR);
RenderSystem.colorMask(false, false, false, false);
RenderSystem.depthMask(false);
RenderSystem.enableDepthTest();
try {
drawAperture.run();
} finally {
drawInside(parentLevel + 1);
}
}
public static void popAperture(int parentLevel, Runnable drawApertureDepth) {
GL11.glEnable(GL11.GL_STENCIL_TEST);
RenderSystem.stencilMask(0xFF);
RenderSystem.stencilFunc(GL11.GL_EQUAL, parentLevel + 1, 0xFF);
RenderSystem.stencilOp(GL11.GL_KEEP, GL11.GL_KEEP, GL_DECR);
RenderSystem.colorMask(false, false, false, false);
RenderSystem.depthMask(true);
RenderSystem.enableDepthTest();
try {
drawApertureDepth.run();
} finally {
drawInside(parentLevel);
}
}
public static void drawInside(int level) {
RenderSystem.stencilMask(0x00);
RenderSystem.stencilFunc(GL11.GL_EQUAL, level, 0xFF);
RenderSystem.stencilOp(GL11.GL_KEEP, GL11.GL_KEEP, GL11.GL_KEEP);
RenderSystem.colorMask(true, true, true, true);
RenderSystem.depthMask(true);
RenderSystem.enableDepthTest();
}
public static void release(RenderTarget target) {
UNSUPPORTED_MAIN_TARGETS.remove(target);
Attachment mainAttachment = MAIN_STENCIL_ATTACHMENTS.remove(target);
if (mainAttachment != null) {
GL30.glDeleteRenderbuffers(mainAttachment.renderbuffer());
}
}
public static void clear() {
for (Attachment attachment : MAIN_STENCIL_ATTACHMENTS.values()) {
GL30.glDeleteRenderbuffers(attachment.renderbuffer());
}
MAIN_STENCIL_ATTACHMENTS.clear();
UNSUPPORTED_MAIN_TARGETS.clear();
}
private static void ensureMainStencilAttachment(RenderTarget target) {
Attachment attachment = MAIN_STENCIL_ATTACHMENTS.get(target);
if (attachment != null
&& attachment.framebuffer() == target.frameBufferId
&& attachment.width() == target.width
&& attachment.height() == target.height) {
throw new IllegalStateException("Main render target stencil attachment has no stencil bits");
}
if (attachment != null) {
GL30.glDeleteRenderbuffers(attachment.renderbuffer());
MAIN_STENCIL_ATTACHMENTS.remove(target);
}
int previousRenderbuffer = GL11.glGetInteger(GL30.GL_RENDERBUFFER_BINDING);
int renderbuffer = GL30.glGenRenderbuffers();
try {
GL30.glBindRenderbuffer(GL30.GL_RENDERBUFFER, renderbuffer);
GL30.glRenderbufferStorage(GL30.GL_RENDERBUFFER, GL30.GL_STENCIL_INDEX8, target.width, target.height);
GL30.glFramebufferRenderbuffer(GL30.GL_FRAMEBUFFER, GL30.GL_STENCIL_ATTACHMENT, GL30.GL_RENDERBUFFER, renderbuffer);
int status = GL30.glCheckFramebufferStatus(GL30.GL_FRAMEBUFFER);
if (status != GL30.GL_FRAMEBUFFER_COMPLETE) {
GL30.glFramebufferRenderbuffer(GL30.GL_FRAMEBUFFER, GL30.GL_STENCIL_ATTACHMENT, GL30.GL_RENDERBUFFER, 0);
throw new IllegalStateException("Main render target is incomplete with stencil attachment: 0x" + Integer.toHexString(status));
}
MAIN_STENCIL_ATTACHMENTS.put(target, new Attachment(target.frameBufferId, renderbuffer, target.width, target.height));
} catch (RuntimeException | Error exception) {
GL30.glDeleteRenderbuffers(renderbuffer);
throw exception;
} finally {
GL30.glBindRenderbuffer(GL30.GL_RENDERBUFFER, previousRenderbuffer);
}
}
private static int stencilBits() {
int type = GL30.glGetFramebufferAttachmentParameteri(GL30.GL_FRAMEBUFFER, GL30.GL_STENCIL_ATTACHMENT,
GL30.GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE);
return type == GL11.GL_NONE ? 0 : GL30.glGetFramebufferAttachmentParameteri(GL30.GL_FRAMEBUFFER,
GL30.GL_STENCIL_ATTACHMENT, GL30.GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE);
}
private record Attachment(int framebuffer, int renderbuffer, int width, int height) {
}
}
@@ -1,33 +0,0 @@
package com.craisinlord.antarchy.content.client.renderer;
import java.util.function.Consumer;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.culling.Frustum;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.phys.Vec3;
import org.joml.Matrix4f;
public interface PortalTerrain {
static boolean canView(Minecraft minecraft, Vec3 exitCenter) {
return SodiumCompat.isLoaded()
? com.craisinlord.antarchy.content.client.renderer.sodium.SodiumPortalTerrain.canView(minecraft, exitCenter)
: PortalGunPortalSectionViews.canView(minecraft, exitCenter);
}
static PortalTerrain prepare(Minecraft minecraft, java.util.UUID exitId, Vec3 exitCenter, Vec3 exitNormal, Frustum frustum) {
return SodiumCompat.isLoaded()
? com.craisinlord.antarchy.content.client.renderer.sodium.SodiumPortalTerrain.prepare(minecraft, frustum)
: VanillaPortalTerrain.prepare(minecraft, exitId, exitCenter, exitNormal, frustum);
}
int sectionCount();
void begin(Minecraft minecraft);
void end(Minecraft minecraft);
void drawLayer(Minecraft minecraft, RenderType renderType, Vec3 cameraPosition, Matrix4f viewMatrix, Matrix4f projection);
void forEachBlockEntity(Minecraft minecraft, Frustum frustum, Consumer<BlockEntity> consumer);
}
@@ -1,26 +0,0 @@
package com.craisinlord.antarchy.content.client.renderer;
import com.mojang.blaze3d.vertex.DefaultVertexFormat;
import com.mojang.blaze3d.vertex.VertexFormat;
import net.minecraft.client.renderer.RenderStateShard;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.resources.ResourceLocation;
public class RoyalBeamRenderTypes extends RenderType {
public RoyalBeamRenderTypes(String name, VertexFormat format, VertexFormat.Mode mode, int bufferSize,
boolean affectsCrumbling, boolean sortOnUpload, Runnable setupState, Runnable clearState) {
super(name, format, mode, bufferSize, affectsCrumbling, sortOnUpload, setupState, clearState);
}
public static RenderType beam(ResourceLocation texture) {
return create("tremorzilla_beam", DefaultVertexFormat.NEW_ENTITY, VertexFormat.Mode.QUADS, 256, true, true,
CompositeState.builder()
.setTextureState(new TextureStateShard(texture, false, false))
.setShaderState(RenderStateShard.RENDERTYPE_ENERGY_SWIRL_SHADER)
.setTransparencyState(RenderStateShard.TRANSLUCENT_TRANSPARENCY)
.setCullState(RenderStateShard.NO_CULL)
.setLightmapState(RenderStateShard.LIGHTMAP)
.setOutputState(RenderStateShard.ITEM_ENTITY_TARGET)
.createCompositeState(false));
}
}
@@ -1,5 +1,6 @@
package com.craisinlord.antarchy.content.client.renderer;
import com.craisinlord.anthology.beam.client.BeamRenderTypes;
import com.craisinlord.antarchy.content.client.model.RoyalBossModel;
import com.craisinlord.antarchy.content.entity.royal.RoyalBossEntity;
import com.craisinlord.antarchy.content.entity.royal.KingEntity;
@@ -204,13 +205,13 @@ public class RoyalBossRenderer extends GeoEntityRenderer<RoyalBossEntity> {
poseStack.mulPose(Axis.ZP.rotationDegrees(45.0F));
org.joml.Matrix4f pose = poseStack.last().pose();
VertexConsumer innerBuffer = bufferSource.getBuffer(RoyalBeamRenderTypes.beam(inner));
VertexConsumer innerBuffer = bufferSource.getBuffer(BeamRenderTypes.beam(inner));
drawLocalBeam(innerBuffer, pose, length, BEAM_INNER_RADIUS, 4, innerV, 0.5F);
VertexConsumer outerBuffer = bufferSource.getBuffer(RoyalBeamRenderTypes.beam(outer));
VertexConsumer outerBuffer = bufferSource.getBuffer(BeamRenderTypes.beam(outer));
drawLocalBeam(outerBuffer, pose, length, BEAM_OUTER_RADIUS, 8, outerV, 0.15F);
ResourceLocation endTexture = endTextures[(entity.tickCount / 2) % endTextures.length];
VertexConsumer endBuffer = bufferSource.getBuffer(RoyalBeamRenderTypes.beam(endTexture));
VertexConsumer endBuffer = bufferSource.getBuffer(BeamRenderTypes.beam(endTexture));
drawBeamImpact(endBuffer, pose, (float) length - 1.5F, BEAM_INNER_RADIUS);
poseStack.popPose();
}
@@ -1,5 +1,6 @@
package com.craisinlord.antarchy.content.client.renderer;
import com.craisinlord.anthology.beam.client.BeamRenderTypes;
import com.craisinlord.antarchy.content.client.model.RoyalMountModel;
import com.craisinlord.antarchy.content.entity.royal.RoyalMountEntity;
import com.mojang.blaze3d.vertex.PoseStack;
@@ -95,12 +96,12 @@ public class RoyalMountRenderer extends GeoEntityRenderer<RoyalMountEntity> {
poseStack.mulPose(com.mojang.math.Axis.XP.rotation((float) (-Math.PI * 0.5D + pitch)));
poseStack.mulPose(com.mojang.math.Axis.ZP.rotationDegrees(45.0F));
org.joml.Matrix4f pose = poseStack.last().pose();
VertexConsumer innerBuffer = bufferSource.getBuffer(RoyalBeamRenderTypes.beam(inner));
VertexConsumer innerBuffer = bufferSource.getBuffer(BeamRenderTypes.beam(inner));
drawLocalBeam(innerBuffer, pose, length, 0.375F, 4, innerV, 0.5F);
VertexConsumer outerBuffer = bufferSource.getBuffer(RoyalBeamRenderTypes.beam(outer));
VertexConsumer outerBuffer = bufferSource.getBuffer(BeamRenderTypes.beam(outer));
drawLocalBeam(outerBuffer, pose, length, 0.44F, 8, outerV, 0.15F);
ResourceLocation endTexture = endTextures[(entity.tickCount / 2) % endTextures.length];
VertexConsumer endBuffer = bufferSource.getBuffer(RoyalBeamRenderTypes.beam(endTexture));
VertexConsumer endBuffer = bufferSource.getBuffer(BeamRenderTypes.beam(endTexture));
RoyalBossRenderer.drawBeamImpact(endBuffer, pose, (float) length - 1.5F, 0.375F);
poseStack.popPose();
}
@@ -1,26 +0,0 @@
package com.craisinlord.antarchy.content.client.renderer;
public final class SodiumCompat {
private static final String SODIUM_RENDERER_CLASS = "net.caffeinemc.mods.sodium.client.render.SodiumWorldRenderer";
private static Boolean loaded;
private SodiumCompat() {
}
public static boolean isLoaded() {
if (loaded == null) {
loaded = detect();
}
return loaded;
}
private static boolean detect() {
String resource = SODIUM_RENDERER_CLASS.replace('.', '/') + ".class";
ClassLoader contextLoader = Thread.currentThread().getContextClassLoader();
if (contextLoader != null && contextLoader.getResource(resource) != null) {
return true;
}
ClassLoader ownLoader = SodiumCompat.class.getClassLoader();
return ownLoader != null && ownLoader.getResource(resource) != null;
}
}
@@ -1,6 +1,6 @@
package com.craisinlord.antarchy.content.client.renderer;
import com.craisinlord.antarchy.content.client.ContractionAfterimages;
import com.craisinlord.anthology.time.client.ContractionAfterimages;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import java.util.Deque;
@@ -1,6 +1,6 @@
package com.craisinlord.antarchy.content.client.renderer;
import com.craisinlord.antarchy.content.client.ContractionAfterimages;
import com.craisinlord.anthology.time.client.ContractionAfterimages;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import java.util.Deque;
@@ -1,25 +0,0 @@
package com.craisinlord.antarchy.content.client.renderer;
import com.craisinlord.antarchy.content.time.TimeDilationFieldEntity;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.entity.EntityRenderer;
import net.minecraft.client.renderer.entity.EntityRendererProvider;
import net.minecraft.resources.ResourceLocation;
public class TimeDilationFieldRenderer extends EntityRenderer<TimeDilationFieldEntity> {
public TimeDilationFieldRenderer(EntityRendererProvider.Context context) {
super(context);
this.shadowRadius = 0.0F;
}
@Override
public void render(TimeDilationFieldEntity entity, float entityYaw, float partialTick, PoseStack poseStack,
MultiBufferSource bufferSource, int packedLight) {
}
@Override
public ResourceLocation getTextureLocation(TimeDilationFieldEntity entity) {
return ResourceLocation.withDefaultNamespace("textures/misc/unknown_pack.png");
}
}
@@ -1,134 +0,0 @@
package com.craisinlord.antarchy.content.client.renderer;
import com.craisinlord.antarchy.content.client.PortalGunPortalRenderState;
import com.craisinlord.antarchy.mixins.client.LevelRendererPortalViewAreaAccessor;
import com.mojang.blaze3d.shaders.Uniform;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.VertexBuffer;
import com.mojang.blaze3d.vertex.VertexFormat;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import it.unimi.dsi.fastutil.objects.ObjectListIterator;
import java.util.UUID;
import java.util.function.Consumer;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.ShaderInstance;
import net.minecraft.client.renderer.chunk.RenderRegionCache;
import net.minecraft.client.renderer.chunk.SectionRenderDispatcher;
import net.minecraft.client.renderer.culling.Frustum;
import net.minecraft.core.BlockPos;
import net.minecraft.core.SectionPos;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
import org.joml.Matrix4f;
final class VanillaPortalTerrain implements PortalTerrain {
private static final int MAX_REBUILDS_PER_SCENE = 32;
private final ObjectArrayList<SectionRenderDispatcher.RenderSection> sections;
private VanillaPortalTerrain(ObjectArrayList<SectionRenderDispatcher.RenderSection> sections) {
this.sections = sections;
}
static VanillaPortalTerrain prepare(Minecraft minecraft, UUID exitId, Vec3 exitCenter, Vec3 exitNormal, Frustum frustum) {
PortalGunPortalSectionViews.View view = PortalGunPortalSectionViews.prepare(minecraft, exitId, exitCenter, exitNormal, frustum);
view.visibleSections().removeIf(section -> !PortalGunPortalRenderState.shouldRenderBounds(section.getBoundingBox()));
queueDirtySections(minecraft, view.visibleSections());
return new VanillaPortalTerrain(view.visibleSections());
}
@Override
public int sectionCount() {
return this.sections.size();
}
@Override
public void begin(Minecraft minecraft) {
}
@Override
public void end(Minecraft minecraft) {
}
@Override
public void drawLayer(Minecraft minecraft, RenderType renderType, Vec3 cameraPosition, Matrix4f viewMatrix, Matrix4f projection) {
renderType.setupRenderState();
ShaderInstance shader = RenderSystem.getShader();
if (shader == null) {
renderType.clearRenderState();
return;
}
shader.setDefaultUniforms(VertexFormat.Mode.QUADS, viewMatrix, projection, minecraft.getWindow());
shader.apply();
Uniform chunkOffset = shader.CHUNK_OFFSET;
boolean frontToBack = renderType != RenderType.translucent();
ObjectListIterator<SectionRenderDispatcher.RenderSection> iterator =
this.sections.listIterator(frontToBack ? 0 : this.sections.size());
try {
while (frontToBack ? iterator.hasNext() : iterator.hasPrevious()) {
SectionRenderDispatcher.RenderSection section = frontToBack ? iterator.next() : iterator.previous();
if (section.getCompiled().isEmpty(renderType)) {
continue;
}
VertexBuffer buffer = section.getBuffer(renderType);
BlockPos origin = section.getOrigin();
if (chunkOffset != null) {
chunkOffset.set((float) (origin.getX() - cameraPosition.x), (float) (origin.getY() - cameraPosition.y),
(float) (origin.getZ() - cameraPosition.z));
chunkOffset.upload();
}
buffer.bind();
buffer.draw();
}
} finally {
if (chunkOffset != null) {
chunkOffset.set(0.0F, 0.0F, 0.0F);
}
shader.clear();
VertexBuffer.unbind();
renderType.clearRenderState();
}
}
@Override
public void forEachBlockEntity(Minecraft minecraft, Frustum frustum, Consumer<BlockEntity> consumer) {
for (SectionRenderDispatcher.RenderSection section : this.sections) {
for (BlockEntity blockEntity : section.getCompiled().getRenderableBlockEntities()) {
consumer.accept(blockEntity);
}
}
LevelRendererPortalViewAreaAccessor state = (LevelRendererPortalViewAreaAccessor) minecraft.levelRenderer;
synchronized (state.antarchy$getGlobalBlockEntities()) {
for (BlockEntity blockEntity : state.antarchy$getGlobalBlockEntities()) {
if (frustum.isVisible(new AABB(blockEntity.getBlockPos()).inflate(1.0D))) {
consumer.accept(blockEntity);
}
}
}
}
private static void queueDirtySections(Minecraft minecraft, ObjectArrayList<SectionRenderDispatcher.RenderSection> sections) {
SectionRenderDispatcher dispatcher = minecraft.levelRenderer.getSectionRenderDispatcher();
if (dispatcher == null) {
return;
}
RenderRegionCache cache = null;
int queued = 0;
for (SectionRenderDispatcher.RenderSection section : sections) {
if (queued >= MAX_REBUILDS_PER_SCENE) {
break;
}
if (!section.isDirty() || !minecraft.level.getLightEngine().lightOnInSection(SectionPos.of(section.getOrigin()))) {
continue;
}
if (cache == null) {
cache = new RenderRegionCache();
}
section.rebuildSectionAsync(dispatcher, cache);
section.setNotDirty();
queued++;
}
}
}
@@ -1,208 +0,0 @@
package com.craisinlord.antarchy.content.client.renderer.sodium;
import com.craisinlord.antarchy.content.client.PortalGunPortalRenderState;
import com.craisinlord.antarchy.content.client.renderer.PortalTerrain;
import com.craisinlord.antarchy.mixins.compat.sodium.RenderSectionManagerAccessor;
import com.craisinlord.antarchy.mixins.compat.sodium.SodiumWorldRendererAccessor;
import java.util.ArrayDeque;
import java.util.EnumMap;
import java.util.Map;
import java.util.function.Consumer;
import net.caffeinemc.mods.sodium.client.SodiumClientMod;
import net.caffeinemc.mods.sodium.client.gl.device.RenderDevice;
import net.caffeinemc.mods.sodium.client.render.SodiumWorldRenderer;
import net.caffeinemc.mods.sodium.client.render.chunk.ChunkRenderMatrices;
import net.caffeinemc.mods.sodium.client.render.chunk.RenderSection;
import net.caffeinemc.mods.sodium.client.render.chunk.RenderSectionManager;
import net.caffeinemc.mods.sodium.client.render.chunk.TaskQueueType;
import net.caffeinemc.mods.sodium.client.render.chunk.lists.CoordinateSectionVisitor;
import net.caffeinemc.mods.sodium.client.render.chunk.lists.SortedRenderLists;
import net.caffeinemc.mods.sodium.client.render.chunk.lists.TreeSectionCollector;
import net.caffeinemc.mods.sodium.client.render.chunk.terrain.DefaultTerrainRenderPasses;
import net.caffeinemc.mods.sodium.client.render.chunk.terrain.TerrainRenderPass;
import net.caffeinemc.mods.sodium.client.render.viewport.Viewport;
import net.caffeinemc.mods.sodium.client.render.viewport.ViewportProvider;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.culling.Frustum;
import net.minecraft.core.SectionPos;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
import org.joml.Matrix4f;
public final class SodiumPortalTerrain implements PortalTerrain {
private static final int FIRST_PORTAL_FRAME = -1_000_000;
private static final int MAX_PENDING_TASKS_PER_QUEUE = 256;
private static final Map<TaskQueueType, ArrayDeque<RenderSection>> PENDING_TASKS = new EnumMap<>(TaskQueueType.class);
private static int nextFrame = FIRST_PORTAL_FRAME;
private final SodiumWorldRenderer worldRenderer;
private final RenderSectionManager sectionManager;
private final SortedRenderLists renderLists;
private final int sectionCount;
private SortedRenderLists previousRenderLists;
private boolean previousEntityCulling;
private SodiumPortalTerrain(SodiumWorldRenderer worldRenderer, RenderSectionManager sectionManager, SortedRenderLists renderLists, int sectionCount) {
this.worldRenderer = worldRenderer;
this.sectionManager = sectionManager;
this.renderLists = renderLists;
this.sectionCount = sectionCount;
}
public static boolean canView(Minecraft minecraft, Vec3 exitCenter) {
SodiumWorldRenderer worldRenderer = SodiumWorldRenderer.instanceNullable();
if (worldRenderer == null || minecraft.player == null
|| ((SodiumWorldRendererAccessor) worldRenderer).antarchy$getRenderSectionManager() == null) {
return false;
}
int limit = minecraft.options.getEffectiveRenderDistance();
return Math.abs(SectionPos.posToSectionCoord(exitCenter.x) - minecraft.player.chunkPosition().x) <= limit
&& Math.abs(SectionPos.posToSectionCoord(exitCenter.z) - minecraft.player.chunkPosition().z) <= limit;
}
public static PortalTerrain prepare(Minecraft minecraft, Frustum frustum) {
SodiumWorldRenderer worldRenderer = SodiumWorldRenderer.instanceNullable();
RenderSectionManager sectionManager = worldRenderer == null ? null
: ((SodiumWorldRendererAccessor) worldRenderer).antarchy$getRenderSectionManager();
if (sectionManager == null) {
return EmptyTerrain.INSTANCE;
}
RenderSectionManagerAccessor manager = (RenderSectionManagerAccessor) sectionManager;
int frame = nextFrame--;
if (nextFrame > FIRST_PORTAL_FRAME) {
nextFrame = FIRST_PORTAL_FRAME;
}
Viewport viewport = ((ViewportProvider) frustum).sodium$createViewport();
TreeSectionCollector collector = new TreeSectionCollector(frame,
SodiumClientMod.options().performance.chunkBuildDeferMode.getImportantRebuildQueueType(),
manager.antarchy$getSortBehavior().getDeferMode().getImportantRebuildQueueType(),
manager.antarchy$getSectionByPosition());
int[] visited = {0};
CoordinateSectionVisitor apertureFilter = (x, y, z) -> {
int minX = SectionPos.sectionToBlockCoord(x);
int minY = SectionPos.sectionToBlockCoord(y);
int minZ = SectionPos.sectionToBlockCoord(z);
if (PortalGunPortalRenderState.shouldRenderBounds(new AABB(minX, minY, minZ, minX + 16, minY + 16, minZ + 16))) {
collector.visit(x, y, z);
visited[0]++;
}
};
manager.antarchy$getRenderableSectionTree().prepareForTraversal();
manager.antarchy$getRenderableSectionTree().traverse(apertureFilter, viewport, manager.antarchy$getSearchDistance());
SortedRenderLists renderLists = collector.createRenderLists(viewport);
stashPendingTasks(collector.getTaskLists());
sectionManager.markGraphDirty();
return new SodiumPortalTerrain(worldRenderer, sectionManager, renderLists, visited[0]);
}
public static void drainPendingTasks(Map<TaskQueueType, ArrayDeque<RenderSection>> taskLists) {
if (PENDING_TASKS.isEmpty() || taskLists == null) {
return;
}
for (Map.Entry<TaskQueueType, ArrayDeque<RenderSection>> entry : PENDING_TASKS.entrySet()) {
ArrayDeque<RenderSection> queue = taskLists.get(entry.getKey());
if (queue == null) {
continue;
}
int limit = entry.getKey().queueSizeLimit();
for (RenderSection section : entry.getValue()) {
if (queue.size() >= limit) {
break;
}
if (!section.isDisposed() && section.getPendingUpdate() != 0 && section.getRunningJob() == null) {
queue.add(section);
}
}
}
PENDING_TASKS.clear();
}
private static void stashPendingTasks(Map<TaskQueueType, ArrayDeque<RenderSection>> taskLists) {
for (Map.Entry<TaskQueueType, ArrayDeque<RenderSection>> entry : taskLists.entrySet()) {
if (entry.getValue().isEmpty()) {
continue;
}
ArrayDeque<RenderSection> pending = PENDING_TASKS.computeIfAbsent(entry.getKey(), type -> new ArrayDeque<>());
for (RenderSection section : entry.getValue()) {
if (pending.size() >= MAX_PENDING_TASKS_PER_QUEUE) {
break;
}
pending.add(section);
}
}
}
@Override
public int sectionCount() {
return this.sectionCount;
}
@Override
public void begin(Minecraft minecraft) {
RenderSectionManagerAccessor manager = (RenderSectionManagerAccessor) this.sectionManager;
SodiumWorldRendererAccessor renderer = (SodiumWorldRendererAccessor) this.worldRenderer;
this.previousRenderLists = manager.antarchy$getRenderLists();
this.previousEntityCulling = renderer.antarchy$getUseEntityCulling();
manager.antarchy$setRenderLists(this.renderLists);
renderer.antarchy$setUseEntityCulling(false);
}
@Override
public void end(Minecraft minecraft) {
((RenderSectionManagerAccessor) this.sectionManager).antarchy$setRenderLists(this.previousRenderLists);
((SodiumWorldRendererAccessor) this.worldRenderer).antarchy$setUseEntityCulling(this.previousEntityCulling);
}
@Override
public void forEachBlockEntity(Minecraft minecraft, Frustum frustum, Consumer<BlockEntity> consumer) {
this.worldRenderer.iterateVisibleBlockEntities(consumer);
}
@Override
public void drawLayer(Minecraft minecraft, RenderType renderType, Vec3 cameraPosition, Matrix4f viewMatrix, Matrix4f projection) {
TerrainRenderPass[] passes;
if (renderType == RenderType.solid()) {
passes = new TerrainRenderPass[] {DefaultTerrainRenderPasses.SOLID, DefaultTerrainRenderPasses.CUTOUT};
} else if (renderType == RenderType.translucent()) {
passes = new TerrainRenderPass[] {DefaultTerrainRenderPasses.TRANSLUCENT};
} else {
return;
}
ChunkRenderMatrices matrices = new ChunkRenderMatrices(projection, viewMatrix);
RenderDevice.enterManagedCode();
try {
for (TerrainRenderPass pass : passes) {
this.sectionManager.renderLayer(matrices, pass, cameraPosition.x, cameraPosition.y, cameraPosition.z);
}
} finally {
RenderDevice.exitManagedCode();
}
}
private enum EmptyTerrain implements PortalTerrain {
INSTANCE;
@Override
public int sectionCount() {
return 0;
}
@Override
public void begin(Minecraft minecraft) {
}
@Override
public void end(Minecraft minecraft) {
}
@Override
public void drawLayer(Minecraft minecraft, RenderType renderType, Vec3 cameraPosition, Matrix4f viewMatrix, Matrix4f projection) {
}
@Override
public void forEachBlockEntity(Minecraft minecraft, Frustum frustum, Consumer<BlockEntity> consumer) {
}
}
}
@@ -1,28 +0,0 @@
package com.craisinlord.antarchy.content.effect;
import net.minecraft.world.effect.MobEffect;
import net.minecraft.world.effect.MobEffectCategory;
import net.minecraft.world.entity.LivingEntity;
public final class ContractedMobEffect extends MobEffect {
public static final double RATE = 2.0D;
public static final double STRONG_RATE = 3.0D;
public ContractedMobEffect() {
super(MobEffectCategory.BENEFICIAL, 0xD5A63A);
}
@Override
public boolean applyEffectTick(LivingEntity entity, int amplifier) {
return true;
}
@Override
public boolean shouldApplyEffectTickThisTick(int duration, int amplifier) {
return true;
}
public static double rateForAmplifier(int amplifier) {
return amplifier > 0 ? STRONG_RATE : RATE;
}
}
@@ -1,28 +0,0 @@
package com.craisinlord.antarchy.content.effect;
import net.minecraft.world.effect.MobEffect;
import net.minecraft.world.effect.MobEffectCategory;
import net.minecraft.world.entity.LivingEntity;
public final class DilatedMobEffect extends MobEffect {
public static final double RATE = 0.35D;
public static final double STRONG_RATE = 0.2D;
public DilatedMobEffect() {
super(MobEffectCategory.HARMFUL, 0x6C4AB6);
}
@Override
public boolean applyEffectTick(LivingEntity entity, int amplifier) {
return true;
}
@Override
public boolean shouldApplyEffectTickThisTick(int duration, int amplifier) {
return true;
}
public static double rateForAmplifier(int amplifier) {
return amplifier > 0 ? STRONG_RATE : RATE;
}
}
@@ -1,5 +1,6 @@
package com.craisinlord.antarchy.content.effect;
import com.craisinlord.anthology.effect.AnthologyEffects;
import com.craisinlord.antarchy.Antarchy;
import net.minecraft.core.Holder;
import net.minecraft.core.registries.BuiltInRegistries;
@@ -11,10 +12,6 @@ import net.minecraft.world.effect.MobEffect;
public final class RoyalEffectHooks {
private static final ResourceKey<MobEffect> COMMANDED_KEY = ResourceKey.create(Registries.MOB_EFFECT,
ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "commanded"));
private static final ResourceKey<MobEffect> DILATED_KEY = ResourceKey.create(Registries.MOB_EFFECT,
ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "dilated"));
private static final ResourceKey<MobEffect> CONTRACTED_KEY = ResourceKey.create(Registries.MOB_EFFECT,
ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "contracted"));
private RoyalEffectHooks() {
}
@@ -24,10 +21,10 @@ public final class RoyalEffectHooks {
}
public static Holder<MobEffect> dilatedHolder() {
return BuiltInRegistries.MOB_EFFECT.getHolder(DILATED_KEY).orElse(null);
return AnthologyEffects.dilated();
}
public static Holder<MobEffect> contractedHolder() {
return BuiltInRegistries.MOB_EFFECT.getHolder(CONTRACTED_KEY).orElse(null);
return AnthologyEffects.contracted();
}
}
@@ -10,7 +10,6 @@ import com.craisinlord.antarchy.content.fluid.AntarchyFluidChecks;
import com.craisinlord.anthology.gravity.GravityApi;
import com.craisinlord.anthology.gravity.GravityDirection;
import com.craisinlord.anthology.gravity.GravityRotationUtil;
import com.craisinlord.antarchy.content.movement.DreamSandLowGravityAccess;
import java.util.EnumSet;
import java.util.List;
import java.util.UUID;
@@ -659,9 +658,6 @@ public class ReverieEntity extends PathfinderMob implements FlyingAnimal, GeoEnt
if (this.isPlayerInsideAntiwater(focus)) {
return false;
}
if (focus instanceof DreamSandLowGravityAccess access && access.antarchy$getDreamSandLandingGraceTicks() > 0) {
return false;
}
GravityDirection gravityDirection = GravityApi.getGravityDirection(focus);
Vec3 localMotion = GravityRotationUtil.vecWorldToPlayer(focus.getDeltaMovement(), gravityDirection);
@@ -226,10 +226,10 @@ public class ToreterrorEntity extends Monster implements GeoEntity {
super.tick();
if (this.level().isClientSide) {
if (this.isJumpShaking()) {
com.craisinlord.antarchy.content.client.CameraShakeClientState.register(
com.craisinlord.anthology.shake.client.CameraShake.register(
this,
com.craisinlord.antarchy.content.client.CameraShakeClientState.TORETERROR_RANGE,
com.craisinlord.antarchy.content.client.CameraShakeClientState.TORETERROR_STRENGTH,
48.0D,
1.0F,
this::isJumpShaking
);
}
@@ -1,7 +1,7 @@
package com.craisinlord.antarchy.content.entity.basilisk;
import com.craisinlord.antarchy.content.entity.multipart.MultipartLayout;
import com.craisinlord.antarchy.content.entity.multipart.MultipartPartDefinition;
import com.craisinlord.anthology.multipart.MultipartLayout;
import com.craisinlord.anthology.multipart.MultipartPartDefinition;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.phys.Vec3;
@@ -2,7 +2,7 @@ package com.craisinlord.antarchy.content.entity.glimmer;
import com.craisinlord.antarchy.Antarchy;
import com.craisinlord.antarchy.content.AntarchyObjects;
import com.craisinlord.antarchy.content.entity.multipart.MultipartEntityOwner;
import com.craisinlord.anthology.multipart.MultipartEntityOwner;
import net.minecraft.core.registries.Registries;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
@@ -8,8 +8,8 @@ import com.craisinlord.antarchy.content.boss.BossCombatUtil;
import com.craisinlord.antarchy.content.damage.AntarchyDamageSources;
import com.craisinlord.antarchy.content.entity.MissileSquidEntity;
import com.craisinlord.antarchy.content.entity.OctopusBombEntity;
import com.craisinlord.antarchy.content.entity.multipart.MultipartEntityOwner;
import com.craisinlord.antarchy.content.entity.multipart.MultipartLayout;
import com.craisinlord.anthology.multipart.MultipartEntityOwner;
import com.craisinlord.anthology.multipart.MultipartLayout;
import com.craisinlord.antarchy.content.damage.AntarchyDamageTypes;
import java.util.List;
@@ -162,18 +162,18 @@ public class KrakenEntity extends Monster implements GeoEntity, MultipartEntityO
}
@Override
public MultipartLayout antarchy$getMultipartLayout() {
public MultipartLayout anthology$getMultipartLayout() {
return KrakenMultipartLayout.INSTANCE;
}
@Override
@Nullable
public Entity[] antarchy$getMultipartParts() {
public Entity[] anthology$getMultipartParts() {
return this.multipartParts;
}
@Override
public void antarchy$setMultipartParts(@Nullable Entity[] parts) {
public void anthology$setMultipartParts(@Nullable Entity[] parts) {
this.multipartParts = parts;
}
@@ -1,7 +1,7 @@
package com.craisinlord.antarchy.content.entity.kraken;
import com.craisinlord.antarchy.content.entity.multipart.MultipartLayout;
import com.craisinlord.antarchy.content.entity.multipart.MultipartPartDefinition;
import com.craisinlord.anthology.multipart.MultipartLayout;
import com.craisinlord.anthology.multipart.MultipartPartDefinition;
public final class KrakenMultipartLayout implements MultipartLayout {
public static final KrakenMultipartLayout INSTANCE = new KrakenMultipartLayout();
@@ -35,7 +35,7 @@ public class LucidEyeProjectileEntity extends ThrowableItemProjectile {
@Override
protected double getDefaultGravity() {
return 0.03D;
return -0.03D;
}
@Override
@@ -1,40 +0,0 @@
package com.craisinlord.antarchy.content.entity.multipart;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.phys.Vec3;
public interface MultipartEntityOwner {
MultipartLayout antarchy$getMultipartLayout();
Entity[] antarchy$getMultipartParts();
void antarchy$setMultipartParts(Entity[] parts);
default Entity[] antarchy$createMultipartParts() {
return MultipartFramework.createMultipartParts(this);
}
default void antarchy$spawnMultipartParts() {
MultipartFramework.spawnMultipartParts(this);
}
default void antarchy$discardMultipartParts() {
MultipartFramework.discardMultipartParts(this);
}
default void antarchy$syncMultipartParts() {
MultipartFramework.syncMultipartParts(this);
}
default boolean antarchy$hurtMultipartPart(Entity part, DamageSource source, float amount) {
return ((Entity) this).hurt(source, amount);
}
default InteractionResult antarchy$interactMultipartPart(Entity part, Player player, Vec3 hitPosition, InteractionHand hand) {
return ((Entity) this).interactAt(player, hitPosition, hand);
}
}
@@ -1,119 +0,0 @@
package com.craisinlord.antarchy.content.entity.multipart;
import java.util.Objects;
import java.util.UUID;
import org.jetbrains.annotations.Nullable;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.Level;
public final class MultipartFramework {
private static final org.slf4j.Logger LOGGER = org.slf4j.LoggerFactory.getLogger(MultipartFramework.class);
private static boolean warnedMissingFactory;
public interface PartFactory {
Entity create(MultipartEntityOwner owner, int partIndex, MultipartPartDefinition spec);
}
public interface NetworkBridge {
void sendAttack(UUID parentId, int partIndex, float damage);
void sendInteract(UUID parentId, int partIndex, int handId);
}
private static final NetworkBridge NOOP_BRIDGE = new NetworkBridge() {
@Override
public void sendAttack(UUID parentId, int partIndex, float damage) {
}
@Override
public void sendInteract(UUID parentId, int partIndex, int handId) {
}
};
@Nullable
private static PartFactory partFactory;
private static NetworkBridge networkBridge = NOOP_BRIDGE;
private MultipartFramework() {
}
public static void bootstrap(
PartFactory partFactory,
NetworkBridge networkBridge
) {
MultipartFramework.partFactory = Objects.requireNonNull(partFactory, "partFactory");
MultipartFramework.networkBridge = Objects.requireNonNull(networkBridge, "networkBridge");
}
public static Entity[] createMultipartParts(MultipartEntityOwner owner) {
if (partFactory == null) {
if (!warnedMissingFactory) {
warnedMissingFactory = true;
LOGGER.warn("Multipart part factory has not been bootstrapped on this platform; "
+ "multipart entities will fall back to their base hitbox only");
}
return new Entity[0];
}
MultipartLayout layout = owner.antarchy$getMultipartLayout();
MultipartPartDefinition[] parts = layout.parts();
Entity[] created = new Entity[parts.length];
for (int i = 0; i < parts.length; i++) {
created[i] = createMultipartPart(owner, i, parts[i]);
}
return created;
}
public static Entity createMultipartPart(MultipartEntityOwner owner, int index, MultipartPartDefinition spec) {
PartFactory factory = partFactory;
if (factory == null) {
throw new IllegalStateException("Multipart part factory has not been bootstrapped");
}
return factory.create(owner, index, spec);
}
public static void spawnMultipartParts(MultipartEntityOwner owner) {
Entity[] parts = owner.antarchy$getMultipartParts();
if (parts == null) {
parts = owner.antarchy$createMultipartParts();
owner.antarchy$setMultipartParts(parts);
}
syncMultipartParts(owner);
}
public static void discardMultipartParts(MultipartEntityOwner owner) {
Entity[] parts = owner.antarchy$getMultipartParts();
if (parts == null) {
return;
}
for (Entity part : parts) {
if (part != null) {
part.discard();
}
}
owner.antarchy$setMultipartParts(null);
}
public static void syncMultipartParts(MultipartEntityOwner owner) {
Entity[] parts = owner.antarchy$getMultipartParts();
if (parts == null) {
return;
}
for (Entity part : parts) {
if (part instanceof MultipartPartAccess access) {
access.antarchy$syncFromParent();
}
}
}
public static void sendMultipartAttack(UUID parentId, int partIndex, float damage) {
networkBridge.sendAttack(parentId, partIndex, damage);
}
public static void sendMultipartInteract(UUID parentId, int partIndex, int handId) {
networkBridge.sendInteract(parentId, partIndex, handId);
}
}
@@ -1,24 +0,0 @@
package com.craisinlord.antarchy.content.entity.multipart;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.phys.Vec3;
public interface MultipartLayout {
MultipartPartDefinition[] parts();
default MultipartPartDefinition part(int index) {
return this.parts()[index];
}
default Vec3 getPartPosition(Entity parent, MultipartPartDefinition spec) {
double yawRadians = Math.toRadians(parent.getYRot());
Vec3 forward = new Vec3(-Math.sin(yawRadians), 0.0D, Math.cos(yawRadians));
Vec3 right = new Vec3(forward.z, 0.0D, -forward.x);
return new Vec3(
parent.getX() + forward.x * spec.forwardOffset() + right.x * spec.lateralOffset(),
parent.getY() + spec.yOffset(),
parent.getZ() + forward.z * spec.forwardOffset() + right.z * spec.lateralOffset()
);
}
}
@@ -1,5 +0,0 @@
package com.craisinlord.antarchy.content.entity.multipart;
public interface MultipartPartAccess {
void antarchy$syncFromParent();
}
@@ -1,13 +0,0 @@
package com.craisinlord.antarchy.content.entity.multipart;
public record MultipartPartDefinition(
String name,
float width,
float height,
float damageMultiplier,
double forwardOffset,
double yOffset,
double lateralOffset,
boolean collisionEnabled
) {
}
@@ -1,28 +0,0 @@
package com.craisinlord.antarchy.content.entity.multipart.network;
import com.craisinlord.antarchy.Antarchy;
import io.netty.buffer.ByteBuf;
import java.util.UUID;
import net.minecraft.network.codec.ByteBufCodecs;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
import net.minecraft.resources.ResourceLocation;
public record MultipartAttackPayload(UUID parentId, int partIndex, float damage) implements CustomPacketPayload {
public static final Type<MultipartAttackPayload> TYPE =
new Type<>(ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "multipart_attack"));
public static final StreamCodec<ByteBuf, MultipartAttackPayload> STREAM_CODEC = StreamCodec.composite(
ByteBufCodecs.STRING_UTF8.map(UUID::fromString, UUID::toString),
MultipartAttackPayload::parentId,
ByteBufCodecs.VAR_INT,
MultipartAttackPayload::partIndex,
ByteBufCodecs.FLOAT,
MultipartAttackPayload::damage,
MultipartAttackPayload::new
);
@Override
public Type<MultipartAttackPayload> type() {
return TYPE;
}
}
@@ -1,28 +0,0 @@
package com.craisinlord.antarchy.content.entity.multipart.network;
import com.craisinlord.antarchy.Antarchy;
import io.netty.buffer.ByteBuf;
import java.util.UUID;
import net.minecraft.network.codec.ByteBufCodecs;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
import net.minecraft.resources.ResourceLocation;
public record MultipartInteractPayload(UUID parentId, int partIndex, int handId) implements CustomPacketPayload {
public static final Type<MultipartInteractPayload> TYPE =
new Type<>(ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "multipart_interact"));
public static final StreamCodec<ByteBuf, MultipartInteractPayload> STREAM_CODEC = StreamCodec.composite(
ByteBufCodecs.STRING_UTF8.map(UUID::fromString, UUID::toString),
MultipartInteractPayload::parentId,
ByteBufCodecs.VAR_INT,
MultipartInteractPayload::partIndex,
ByteBufCodecs.VAR_INT,
MultipartInteractPayload::handId,
MultipartInteractPayload::new
);
@Override
public Type<MultipartInteractPayload> type() {
return TYPE;
}
}
@@ -348,10 +348,10 @@ public class NightmareEntity extends Monster implements GeoEntity {
this.tickClientParticles();
this.updateFlightRotation();
if (this.isRoaring()) {
com.craisinlord.antarchy.content.client.CameraShakeClientState.register(
com.craisinlord.anthology.shake.client.CameraShake.register(
this,
com.craisinlord.antarchy.content.client.CameraShakeClientState.NIGHTMARE_RANGE,
com.craisinlord.antarchy.content.client.CameraShakeClientState.NIGHTMARE_STRENGTH,
32.0D,
1.35F,
this::isRoaring
);
}
@@ -1,7 +1,7 @@
package com.craisinlord.antarchy.content.entity.nightmare;
import com.craisinlord.antarchy.content.entity.multipart.MultipartLayout;
import com.craisinlord.antarchy.content.entity.multipart.MultipartPartDefinition;
import com.craisinlord.anthology.multipart.MultipartLayout;
import com.craisinlord.anthology.multipart.MultipartPartDefinition;
public final class NightmareMultipartLayout implements MultipartLayout {
public static final NightmareMultipartLayout INSTANCE = new NightmareMultipartLayout();
@@ -53,8 +53,8 @@ import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.Nullable;
import org.joml.Vector3f;
import com.craisinlord.antarchy.content.entity.royal.beam.RoyalBeamSettings;
import com.craisinlord.antarchy.content.entity.royal.beam.RoyalBeamTerrainMode;
import com.craisinlord.anthology.beam.BeamSettings;
import com.craisinlord.anthology.beam.BeamTerrainMode;
import com.craisinlord.antarchy.content.entity.royal.beam.RoyalBeamElement;
import com.craisinlord.antarchy.content.menu.RoyalJudgmentMenu;
import com.craisinlord.antarchy.content.menu.RoyalJudgmentState;
@@ -398,8 +398,8 @@ public class KingEntity extends RoyalBossEntity {
protected SoundEvent royalBeamEndSound() { return AntarchySoundEvents.KING_BEAM_SHOOT.get(); }
@Override
protected RoyalBeamSettings royalBeamSettings() {
return new RoyalBeamSettings(AntarchySettings.kingBeamRange(), AntarchySettings.kingBeamTracking(), 7.5D,
protected BeamSettings royalBeamSettings() {
return new BeamSettings(AntarchySettings.kingBeamRange(), AntarchySettings.kingBeamTracking(), 7.5D,
AntarchySettings.kingBeamDurationTicks(), AntarchySettings.kingBeamTravelTicks(), AntarchySettings.kingBeamCooldownTicks(), 6.0F, 6.0F,
(float) AntarchySettings.kingBeamDamage(), 1.0F, 3, 100.0D,
(float) AntarchySettings.kingBeamTerrainRadius(), 4.0F, AntarchySettings.kingBeamTerrainCap(),
@@ -407,8 +407,8 @@ public class KingEntity extends RoyalBossEntity {
}
@Override
protected RoyalBeamTerrainMode royalBeamTerrainMode() {
return RoyalBeamTerrainMode.BUILD_ICE;
protected BeamTerrainMode royalBeamTerrainMode() {
return BeamTerrainMode.BUILD_ICE;
}
@Override
@@ -422,8 +422,8 @@ public class KingEntity extends RoyalBossEntity {
}
@Override
protected RoyalBeamTerrainMode royalBeamTerrainMode(@Nullable RoyalHead head) {
return head != null && head.slot() == RoyalHead.Slot.RIGHT ? RoyalBeamTerrainMode.BUILD_ICE : RoyalBeamTerrainMode.NONE;
protected BeamTerrainMode royalBeamTerrainMode(@Nullable RoyalHead head) {
return head != null && head.slot() == RoyalHead.Slot.RIGHT ? BeamTerrainMode.BUILD_ICE : BeamTerrainMode.NONE;
}
@Override
@@ -10,8 +10,8 @@ import com.craisinlord.anthology.gravity.GravityApi;
import com.craisinlord.anthology.gravity.GravityDirection;
import com.craisinlord.anthology.gravity.GravityRotationUtil;
import com.craisinlord.anthology.gravity.GravityTransition;
import com.craisinlord.antarchy.content.time.TimeDilationApi;
import com.craisinlord.antarchy.content.time.TimeDilationFieldEntity;
import com.craisinlord.anthology.time.TimeDilationApi;
import com.craisinlord.anthology.time.TimeDilationFieldEntity;
import com.craisinlord.antarchy.content.worldgen.thoraxis.ThoraxisUndersideManager;
import java.util.ArrayList;
import java.util.HashMap;
@@ -51,8 +51,8 @@ import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.Vec3;
import net.minecraft.world.phys.AABB;
import com.craisinlord.antarchy.content.entity.royal.beam.RoyalBeamSettings;
import com.craisinlord.antarchy.content.entity.royal.beam.RoyalBeamTerrainMode;
import com.craisinlord.anthology.beam.BeamSettings;
import com.craisinlord.anthology.beam.BeamTerrainMode;
import com.craisinlord.antarchy.content.entity.royal.beam.RoyalBeamElement;
import com.craisinlord.antarchy.content.entity.royal.attack.RoyalAttackLane;
import com.craisinlord.antarchy.content.entity.royal.attack.RoyalEffectController;
@@ -406,8 +406,8 @@ public class QueenEntity extends RoyalBossEntity {
protected SoundEvent royalBeamEndSound() { return AntarchySoundEvents.QUEEN_BEAM_END.get(); }
@Override
protected RoyalBeamSettings royalBeamSettings() {
return new RoyalBeamSettings(AntarchySettings.queenBeamRange(), AntarchySettings.queenBeamTracking(), 7.5D,
protected BeamSettings royalBeamSettings() {
return new BeamSettings(AntarchySettings.queenBeamRange(), AntarchySettings.queenBeamTracking(), 7.5D,
AntarchySettings.queenBeamDurationTicks(), AntarchySettings.queenBeamTravelTicks(), AntarchySettings.queenBeamCooldownTicks(), 6.0F, 6.0F,
(float) AntarchySettings.queenBeamDamage(), 1.0F, 3, 100.0D,
(float) AntarchySettings.queenBeamTerrainRadius(), 4.0F, AntarchySettings.queenBeamTerrainCap(),
@@ -415,8 +415,8 @@ public class QueenEntity extends RoyalBossEntity {
}
@Override
protected RoyalBeamTerrainMode royalBeamTerrainMode() {
return RoyalBeamTerrainMode.DESTROY;
protected BeamTerrainMode royalBeamTerrainMode() {
return BeamTerrainMode.DESTROY;
}
@Override
@@ -1157,7 +1157,7 @@ public class QueenEntity extends RoyalBossEntity {
TimeDilationFieldEntity field = TimeDilationApi.createField(level, this.position(),
QUEEN_CHRONOSPHERE_RADIUS, QUEEN_CHRONOSPHERE_RATE,
QUEEN_CHRONOSPHERE_DURATION, this.getUUID());
field.configureQueenChronosphere(this);
field.configureOwnerAnchored(this);
this.startRoyalRecovery(35);
}
case "debris_cyclone" -> {
@@ -1,8 +1,9 @@
package com.craisinlord.antarchy.content.entity.royal;
import com.craisinlord.anthology.time.TimeDilationAnchor;
import com.craisinlord.antarchy.content.AntarchyObjects;
import com.craisinlord.antarchy.content.time.TimeDilationApi;
import com.craisinlord.antarchy.content.time.TimeDilationFieldEntity;
import com.craisinlord.anthology.time.TimeDilationApi;
import com.craisinlord.anthology.time.TimeDilationFieldEntity;
import com.craisinlord.antarchy.config.AntarchySettings;
import com.craisinlord.antarchy.content.entity.BlockSuction;
import java.util.UUID;
@@ -36,7 +37,7 @@ import software.bernie.geckolib.animation.PlayState;
import software.bernie.geckolib.animation.RawAnimation;
import software.bernie.geckolib.util.GeckoLibUtil;
public class RoyalBlackHoleEntity extends Entity implements GeoEntity {
public class RoyalBlackHoleEntity extends Entity implements GeoEntity, TimeDilationAnchor {
private static final EntityDataAccessor<Float> RADIUS =
SynchedEntityData.defineId(RoyalBlackHoleEntity.class, EntityDataSerializers.FLOAT);
private static final EntityDataAccessor<Boolean> COLLAPSING =
@@ -248,6 +249,11 @@ public class RoyalBlackHoleEntity extends Entity implements GeoEntity {
}
/** Returns the center of the animated black-hole cube in world coordinates. */
@Override
public Vec3 timeDilationCenter() {
return this.effectCenter();
}
public Vec3 effectCenter() {
double animationSeconds = Math.min(CORE_RISE_END_SECONDS, this.animationAge / 20.0D);
double riseProgress = animationSeconds / CORE_RISE_END_SECONDS;
@@ -1,12 +1,13 @@
package com.craisinlord.antarchy.content.entity.royal;
import com.craisinlord.anthology.beam.BeamOwner;
import com.craisinlord.antarchy.config.AntarchySettings;
import com.craisinlord.antarchy.content.entity.multipart.MultipartEntityOwner;
import com.craisinlord.antarchy.content.entity.multipart.MultipartLayout;
import com.craisinlord.antarchy.content.entity.multipart.MultipartPartDefinition;
import com.craisinlord.antarchy.content.entity.royal.beam.RoyalBeamController;
import com.craisinlord.antarchy.content.entity.royal.beam.RoyalBeamSettings;
import com.craisinlord.antarchy.content.entity.royal.beam.RoyalBeamTerrainMode;
import com.craisinlord.anthology.multipart.MultipartEntityOwner;
import com.craisinlord.anthology.multipart.MultipartLayout;
import com.craisinlord.anthology.multipart.MultipartPartDefinition;
import com.craisinlord.anthology.beam.BeamController;
import com.craisinlord.anthology.beam.BeamSettings;
import com.craisinlord.anthology.beam.BeamTerrainMode;
import com.craisinlord.antarchy.content.entity.royal.beam.RoyalBeamElement;
import com.craisinlord.antarchy.content.entity.royal.attack.RoyalAttackLane;
import com.craisinlord.antarchy.content.entity.royal.attack.RoyalAttackScheduler;
@@ -69,7 +70,7 @@ import software.bernie.geckolib.animation.PlayState;
import software.bernie.geckolib.animation.RawAnimation;
import software.bernie.geckolib.util.GeckoLibUtil;
public abstract class RoyalBossEntity extends Monster implements GeoEntity, MultipartEntityOwner {
public abstract class RoyalBossEntity extends Monster implements GeoEntity, MultipartEntityOwner, BeamOwner {
public static final float MODEL_RENDER_SCALE = 2.0F;
public static final float GAMEPLAY_WIDTH = 28.0F;
public static final float GAMEPLAY_HEIGHT = 15.0F;
@@ -165,7 +166,7 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
private static final RawAnimation DEATH_ANIM = RawAnimation.begin().thenPlayAndHold("death");
private final AnimatableInstanceCache geoCache = GeckoLibUtil.createInstanceCache(this);
protected final RoyalBeamController[] beamControllers = new RoyalBeamController[3];
protected final BeamController[] beamControllers = new BeamController[3];
protected final RoyalAttackScheduler attackScheduler = new RoyalAttackScheduler();
private final ServerBossEvent bossEvent;
private final RoyalHead[] heads = {
@@ -202,7 +203,7 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
protected RoyalBossEntity(EntityType<? extends RoyalBossEntity> entityType, Level level) {
super(entityType, level);
for (int i = 0; i < this.beamControllers.length; i++) {
this.beamControllers[i] = new RoyalBeamController(this);
this.beamControllers[i] = new BeamController(this);
}
this.xpReward = 500;
this.groundMoveControl = new MoveControl(this);
@@ -247,9 +248,9 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
protected abstract SoundEvent royalBeamEndSound();
protected abstract RoyalBeamSettings royalBeamSettings();
protected abstract BeamSettings royalBeamSettings();
protected abstract RoyalBeamTerrainMode royalBeamTerrainMode();
protected abstract BeamTerrainMode royalBeamTerrainMode();
protected RoyalBeamElement royalBeamElement(@Nullable RoyalHead head) {
return RoyalBeamElement.GENERIC;
@@ -266,7 +267,7 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
protected void tickRoyalBeamEffects(RoyalHead head, Vec3 start, Vec3 end) {
}
protected RoyalBeamTerrainMode royalBeamTerrainMode(@Nullable RoyalHead head) {
protected BeamTerrainMode royalBeamTerrainMode(@Nullable RoyalHead head) {
return this.royalBeamTerrainMode();
}
@@ -350,18 +351,18 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
}
@Override
public MultipartLayout antarchy$getMultipartLayout() {
public MultipartLayout anthology$getMultipartLayout() {
return RoyalBossMultipartLayout.INSTANCE;
}
@Override
@Nullable
public Entity[] antarchy$getMultipartParts() {
public Entity[] anthology$getMultipartParts() {
return this.multipartParts;
}
@Override
public void antarchy$setMultipartParts(@Nullable Entity[] parts) {
public void anthology$setMultipartParts(@Nullable Entity[] parts) {
this.multipartParts = parts;
}
@@ -642,7 +643,7 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
}
for (RoyalHead head : this.heads) {
int index = head.slot().ordinal();
RoyalBeamController controller = this.beamControllers[index];
BeamController controller = this.beamControllers[index];
if (controller.isFiring()) {
LivingEntity beamTarget = head.target(this.level());
if (!head.shooting() || beamTarget == null || !beamTarget.isAlive()
@@ -688,7 +689,7 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
|| !this.targetInBeamLine(head, target)) {
continue;
}
RoyalBeamSettings beamSettings = this.royalBeamSettings();
BeamSettings beamSettings = this.royalBeamSettings();
int duration = Math.max(1, beamSettings.durationTicks());
int cooldown = Math.max(1, Mth.floor(beamSettings.cooldownTicks() * phase.cooldownScale()));
int windup = Math.max(0, AntarchySettings.queenBeamWindupTicks());
@@ -1220,7 +1221,7 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
for (RoyalHead head : this.heads) {
head.reset();
}
for (RoyalBeamController controller : this.beamControllers) {
for (BeamController controller : this.beamControllers) {
controller.stop();
}
this.setFiringRoyalBeam(false);
@@ -1330,7 +1331,7 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
public void setFiringRoyalBeam(boolean firing) {
}
public RoyalBeamController beamController() {
public BeamController beamController() {
return this.beamControllers[0];
}
@@ -1463,6 +1464,16 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
this.applyRoyalDamageModifier();
}
@Override
public float scaleBeamDamage(float damage) {
return this.scaleRoyalDamage(damage);
}
@Override
public boolean canBeamDamage(LivingEntity target) {
return this.canDamageWithRoyalAttack(target);
}
public float scaleRoyalDamage(double damage) {
return (float) (damage * this.royalDamageMultiplier);
}
@@ -1,7 +1,7 @@
package com.craisinlord.antarchy.content.entity.royal;
import com.craisinlord.antarchy.content.entity.multipart.MultipartLayout;
import com.craisinlord.antarchy.content.entity.multipart.MultipartPartDefinition;
import com.craisinlord.anthology.multipart.MultipartLayout;
import com.craisinlord.anthology.multipart.MultipartPartDefinition;
public final class RoyalBossMultipartLayout implements MultipartLayout {
public static final RoyalBossMultipartLayout INSTANCE = new RoyalBossMultipartLayout();
@@ -4,12 +4,12 @@ import com.craisinlord.antarchy.config.AntarchySettings;
import com.craisinlord.antarchy.content.AntarchySoundEvents;
import com.craisinlord.antarchy.content.AntarchyTags;
import com.craisinlord.antarchy.content.entity.royal.beam.RoyalBeamElement;
import com.craisinlord.antarchy.content.entity.royal.beam.RoyalBeamController;
import com.craisinlord.antarchy.content.entity.royal.beam.RoyalBeamSettings;
import com.craisinlord.antarchy.content.entity.royal.beam.RoyalBeamTerrainMode;
import com.craisinlord.anthology.beam.BeamController;
import com.craisinlord.anthology.beam.BeamSettings;
import com.craisinlord.anthology.beam.BeamTerrainMode;
import com.craisinlord.antarchy.content.effect.RoyalEffectEligibility;
import com.craisinlord.antarchy.content.effect.RoyalEffectHooks;
import com.craisinlord.antarchy.content.time.TimeDilationApi;
import com.craisinlord.anthology.time.TimeDilationApi;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.core.BlockPos;
import net.minecraft.nbt.CompoundTag;
@@ -136,7 +136,7 @@ public abstract class RoyalMountEntity extends TamableAnimal implements GeoEntit
private int flapSoundTimer;
private boolean riderAscendPressed;
private boolean riderDescendPressed;
private final RoyalBeamController beamController = new RoyalBeamController(this);
private final BeamController beamController = new BeamController(this);
private int beamWindupTicks;
private int beamCooldownTicks;
private int beamHead;
@@ -540,7 +540,7 @@ public abstract class RoyalMountEntity extends TamableAnimal implements GeoEntit
}
}
if (this.beamController.isFiring()) {
this.beamController.tick(this.beamShootFrom(), this.getTarget(), this.beamSettings(), RoyalBeamTerrainMode.NONE, this::setBeamEndPosition);
this.beamController.tick(this.beamShootFrom(), this.getTarget(), this.beamSettings(), BeamTerrainMode.NONE, this::setBeamEndPosition);
if (!this.beamController.isFiring()) {
this.entityData.set(BEAM_ACTIVE, false);
this.entityData.set(BEAM_HEAD, -1);
@@ -598,9 +598,9 @@ public abstract class RoyalMountEntity extends TamableAnimal implements GeoEntit
level.sendParticles(ParticleTypes.END_ROD, center.x, center.y + 0.5D, center.z, 28, 1.2D, 0.6D, 1.2D, 0.03D);
}
private RoyalBeamSettings beamSettings() {
private BeamSettings beamSettings() {
float damage = this instanceof PrincessEntity ? 10.0F : 12.0F;
return new RoyalBeamSettings(28.0D, 0.08D, 4.0D, BEAM_DURATION_TICKS, 6, BEAM_COOLDOWN_TICKS,
return new BeamSettings(28.0D, 0.08D, 4.0D, BEAM_DURATION_TICKS, 6, BEAM_COOLDOWN_TICKS,
0.35F, 0.45F, damage, 0.45F, 3, 0.0D, 0.0F, 0.0F, 0, 0.0F, 0.0F, 0.0F, false, true);
}
@@ -622,7 +622,7 @@ public abstract class RoyalMountEntity extends TamableAnimal implements GeoEntit
return this.entityData.get(HAS_BEAM_END) ? new Vec3(this.entityData.get(BEAM_END_X), this.entityData.get(BEAM_END_Y), this.entityData.get(BEAM_END_Z)) : null;
}
public RoyalBeamElement getBeamElement() { return RoyalBeamElement.GENERIC; }
public RoyalBeamController beamController() { return this.beamController; }
public BeamController beamController() { return this.beamController; }
private void setBeamEndPosition(@Nullable Vec3 position) {
this.entityData.set(HAS_BEAM_END, position != null);
if (position != null) {
@@ -1,385 +0,0 @@
package com.craisinlord.antarchy.content.entity.royal.beam;
import com.craisinlord.antarchy.content.entity.royal.RoyalBossEntity;
import java.util.List;
import java.util.HashSet;
import java.util.Set;
import java.util.function.Consumer;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.tags.BlockTags;
import net.minecraft.util.Mth;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntitySelector;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.Mob;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.entity.ai.attributes.Attributes;
import net.minecraft.world.level.ClipContext;
import net.minecraft.world.level.GameRules;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.HitResult;
import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.Nullable;
public final class RoyalBeamController {
private static final int INITIAL_TRACKING_DELAY_TICKS = 8;
private final LivingEntity owner;
private Vec3 serverTarget;
private Vec3 beamEndPosition;
private int beamTicks;
private int beamAgeTicks;
private int trackingDelayTicks;
private int terrainMutationsThisTick;
private final Set<Integer> damagedThisTick = new HashSet<>();
private static final int ICE_MUTATIONS_PER_TICK = 3;
private static final float ICE_IMPACT_RADIUS = 2.0F;
public RoyalBeamController(LivingEntity owner) {
this.owner = owner;
}
public RoyalBeamController(Mob owner) {
this((LivingEntity) owner);
}
public boolean isFiring() {
return this.beamTicks > 0;
}
public int beamTicks() {
return this.beamTicks;
}
@Nullable
public Vec3 beamEndPosition() {
return this.beamEndPosition;
}
public void start(Vec3 shootFrom, Vec3 target, int durationTicks) {
this.serverTarget = target;
this.beamEndPosition = target;
this.beamTicks = Math.max(1, durationTicks);
this.beamAgeTicks = 0;
this.trackingDelayTicks = INITIAL_TRACKING_DELAY_TICKS;
}
public void stop() {
this.serverTarget = null;
this.beamEndPosition = null;
this.beamTicks = 0;
this.beamAgeTicks = 0;
}
public void tick(
Vec3 shootFrom,
@Nullable LivingEntity target,
RoyalBeamSettings settings,
RoyalBeamTerrainMode terrainMode,
Consumer<@Nullable Vec3> syncedEndPosition
) {
this.tick(shootFrom, owner.getLookAngle(), target, settings, terrainMode, syncedEndPosition);
}
public void tick(
Vec3 shootFrom,
Vec3 shootDirection,
@Nullable LivingEntity target,
RoyalBeamSettings settings,
RoyalBeamTerrainMode terrainMode,
Consumer<@Nullable Vec3> syncedEndPosition
) {
this.tick(shootFrom, shootDirection, target, settings, terrainMode, syncedEndPosition, false);
}
public void tick(
Vec3 shootFrom,
Vec3 shootDirection,
@Nullable LivingEntity target,
RoyalBeamSettings settings,
RoyalBeamTerrainMode terrainMode,
Consumer<@Nullable Vec3> syncedEndPosition,
boolean useTrackedTargetAim
) {
if (this.beamTicks <= 0) {
syncedEndPosition.accept(null);
return;
}
this.beamTicks--;
this.beamAgeTicks++;
this.terrainMutationsThisTick = 0;
if (this.trackingDelayTicks > 0) {
this.trackingDelayTicks--;
} else if (target != null && target.isAlive()) {
Vec3 targetEye = target.getEyePosition();
this.serverTarget = this.serverTarget == null
? targetEye
: this.serverTarget.add(targetEye.subtract(this.serverTarget).scale(settings.targetTracking()));
}
if (this.serverTarget == null) {
this.stop();
syncedEndPosition.accept(null);
return;
}
Vec3 trackedDirection = this.serverTarget.subtract(shootFrom);
Vec3 direction = useTrackedTargetAim && trackedDirection.lengthSqr() >= 1.0E-7D
? trackedDirection.normalize()
: shootDirection.lengthSqr() < 1.0E-7D
? this.owner.getLookAngle().normalize()
: shootDirection.normalize();
double committedDistance = Math.min(settings.range(), Math.max(settings.pathStep(),
this.serverTarget.subtract(shootFrom).dot(direction)));
double travelProgress = Mth.clamp(this.beamAgeTicks / (double) settings.travelTicks(), 0.0D, 1.0D);
double currentRange = Math.max(settings.pathStep(), committedDistance * travelProgress);
Vec3 clipEnd = shootFrom.add(direction.scale(currentRange));
HitResult hit = this.owner.level().clip(new ClipContext(
shootFrom,
clipEnd,
ClipContext.Block.COLLIDER,
ClipContext.Fluid.NONE,
this.owner
));
this.beamEndPosition = hit.getLocation();
syncedEndPosition.accept(this.beamEndPosition);
if (!this.owner.level().isClientSide && this.beamEndPosition != null) {
boolean impactedBlock = hit.getType() == HitResult.Type.BLOCK;
marchDamageAndTerrain(shootFrom, this.beamEndPosition, settings, terrainMode, impactedBlock);
}
if (this.beamTicks <= 0) {
this.stop();
syncedEndPosition.accept(null);
}
}
private void marchDamageAndTerrain(
Vec3 shootFrom,
Vec3 beamEnd,
RoyalBeamSettings settings,
RoyalBeamTerrainMode terrainMode,
boolean impactedBlock
) {
Vec3 direction = beamEnd.subtract(shootFrom).normalize();
double distance = shootFrom.distanceTo(beamEnd);
DamageSource damageSource = this.owner instanceof Player player
? player.damageSources().playerAttack(player)
: this.owner.damageSources().mobAttack(this.owner);
this.damagedThisTick.clear();
boolean pathMutates = terrainMode == RoyalBeamTerrainMode.DESTROY;
for (double walked = settings.pathStep(); walked < Math.min(distance, settings.range()); walked += settings.pathStep()) {
Vec3 sample = shootFrom.add(direction.scale(walked));
hurtEntitiesAround(sample, settings.pathDamageRadius(), this.scaledDamage(settings.damage()), settings.knockback(), damageSource, settings.requireLineOfSightForDamage(), this.damagedThisTick);
if (pathMutates && shouldMutateTerrain(settings, walked)) {
mutateTerrainAround(sample, settings.pathTerrainRadius(), settings, terrainMode);
}
}
hurtEntitiesAround(beamEnd, settings.impactDamageRadius(), this.scaledDamage(settings.damage()), settings.knockback(), damageSource, settings.requireLineOfSightForDamage(), this.damagedThisTick);
if (!shouldMutateTerrain(settings, distance)) {
return;
}
if (terrainMode == RoyalBeamTerrainMode.BUILD_ICE) {
if (impactedBlock) {
mutateTerrainAround(beamEnd, ICE_IMPACT_RADIUS, settings, terrainMode);
}
} else {
mutateTerrainAround(beamEnd, settings.impactTerrainRadius(), settings, terrainMode);
}
}
private float scaledDamage(float damage) {
return this.owner instanceof RoyalBossEntity royalBoss ? royalBoss.scaleRoyalDamage(damage) : damage;
}
private boolean shouldMutateTerrain(RoyalBeamSettings settings, double distanceFromOwner) {
return settings.terrainEnabled()
&& settings.maxTerrainMutationsPerTick() > 0
&& this.beamTicks % settings.terrainIntervalTicks() == 0
&& distanceFromOwner <= settings.maxTerrainDistance()
&& this.owner.level().getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING);
}
private boolean hurtEntitiesAround(
Vec3 center,
float radius,
float damageAmount,
float knockbackAmount,
DamageSource damageSource,
boolean requireLineOfSight,
Set<Integer> damagedThisTick
) {
if (radius <= 0.0F || damageAmount <= 0.0F) {
return false;
}
AABB aabb = new AABB(center.subtract(radius, radius, radius), center.add(radius, radius, radius));
List<LivingEntity> entities = this.owner.level().getEntitiesOfClass(
LivingEntity.class,
aabb,
EntitySelector.NO_CREATIVE_OR_SPECTATOR
);
boolean hurtAny = false;
for (LivingEntity living : entities) {
if (living.is(this.owner)
|| this.owner.isAlliedTo(living)
|| !(this.owner instanceof Player) && living.getType() == this.owner.getType()
|| this.owner instanceof RoyalBossEntity royalBoss && !royalBoss.canDamageWithRoyalAttack(living)
|| living.distanceToSqr(center) > radius * radius
|| damagedThisTick.contains(living.getId())
|| requireLineOfSight && !canEntityBeHurtFrom(center, living)) {
continue;
}
if (living.hurt(damageSource, damageAmount)) {
damagedThisTick.add(living.getId());
hurtAny = true;
knockbackTarget(living, knockbackAmount);
}
}
return hurtAny;
}
private boolean canEntityBeHurtFrom(Vec3 center, LivingEntity living) {
Vec3 targetCenter = living.position().add(0.0D, living.getBbHeight() * 0.5D, 0.0D);
return this.owner.level().clip(new ClipContext(
center,
targetCenter,
ClipContext.Block.COLLIDER,
ClipContext.Fluid.NONE,
this.owner
)).getType() == HitResult.Type.MISS;
}
private void knockbackTarget(Entity target, double strength) {
if (strength <= 0.0D) {
return;
}
double resistance = target instanceof LivingEntity living
? living.getAttributeValue(Attributes.KNOCKBACK_RESISTANCE)
: 0.0D;
double appliedStrength = strength * Math.max(0.0D, 1.0D - resistance);
if (appliedStrength <= 0.0D) {
return;
}
Vec3 push = target.position().subtract(this.owner.position()).multiply(1.0D, 0.0D, 1.0D).normalize().scale(appliedStrength);
Vec3 movement = target.getDeltaMovement();
target.setDeltaMovement(
movement.x * 0.5D + push.x,
Math.min(0.4D, movement.y * 0.5D + appliedStrength),
movement.z * 0.5D + push.z
);
target.hasImpulse = true;
}
private void mutateTerrainAround(
Vec3 center,
float radius,
RoyalBeamSettings settings,
RoyalBeamTerrainMode terrainMode
) {
if (terrainMode == RoyalBeamTerrainMode.NONE || radius <= 0.0F) {
return;
}
Level level = this.owner.level();
int minX = Mth.floor(center.x - radius);
int minY = Mth.floor(center.y - radius);
int minZ = Mth.floor(center.z - radius);
int maxX = Mth.floor(center.x + radius);
int maxY = Mth.floor(center.y + radius);
int maxZ = Mth.floor(center.z + radius);
float radiusSqr = radius * radius;
int cap = terrainMode == RoyalBeamTerrainMode.BUILD_ICE
? Math.min(ICE_MUTATIONS_PER_TICK, settings.maxTerrainMutationsPerTick())
: settings.maxTerrainMutationsPerTick();
for (BlockPos cursor : BlockPos.betweenClosed(minX, minY, minZ, maxX, maxY, maxZ)) {
if (this.terrainMutationsThisTick >= cap) {
return;
}
BlockPos pos = cursor.immutable();
if (pos.distToCenterSqr(center.x, center.y, center.z) > radiusSqr
|| this.owner.getRandom().nextFloat() > settings.terrainMutationChance()
|| pos.getY() < level.getMinBuildHeight()
|| pos.getY() >= level.getMaxBuildHeight()
|| !level.getWorldBorder().isWithinBounds(pos)) {
continue;
}
BlockState existing = level.getBlockState(pos);
if (terrainMode == RoyalBeamTerrainMode.DESTROY) {
destroyBeamBlock(level, pos, existing, settings);
} else if (terrainMode == RoyalBeamTerrainMode.BUILD_ICE) {
buildIceBlock(level, pos, existing, settings);
}
}
}
private void destroyBeamBlock(Level level, BlockPos pos, BlockState existing, RoyalBeamSettings settings) {
if (existing.isAir()
|| level.getBlockEntity(pos) != null
|| existing.getDestroySpeed(level, pos) < 0.0F
|| existing.getBlock().getExplosionResistance() > settings.maxTerrainResistance()
|| existing.getCollisionShape(level, pos).isEmpty()) {
return;
}
if (this.owner.getRandom().nextFloat() <= settings.terrainDropChance()) {
level.destroyBlock(pos, true, this.owner);
} else {
level.levelEvent(2001, pos, Block.getId(existing));
level.setBlockAndUpdate(pos, Blocks.AIR.defaultBlockState());
}
this.terrainMutationsThisTick++;
}
private void buildIceBlock(Level level, BlockPos pos, BlockState existing, RoyalBeamSettings settings) {
boolean replaceable = existing.isAir()
|| existing.canBeReplaced()
|| !existing.getFluidState().isEmpty();
if (!replaceable || level.getBlockEntity(pos) != null || existing.is(BlockTags.ICE)) {
return;
}
if (!touchesFreezableSurface(level, pos)) {
return;
}
level.levelEvent(2001, pos, Block.getId(existing));
level.setBlock(pos, randomIceBlock().defaultBlockState(), Block.UPDATE_ALL);
this.terrainMutationsThisTick++;
}
private static boolean touchesFreezableSurface(Level level, BlockPos pos) {
for (Direction direction : Direction.values()) {
BlockPos neighbor = pos.relative(direction);
BlockState state = level.getBlockState(neighbor);
if (state.is(BlockTags.ICE) || state.is(Blocks.SNOW_BLOCK)) {
continue;
}
if (state.isFaceSturdy(level, neighbor, direction.getOpposite())) {
return true;
}
}
return false;
}
private Block randomIceBlock() {
return switch (this.owner.getRandom().nextInt(5)) {
case 0 -> Blocks.BLUE_ICE;
case 1 -> Blocks.PACKED_ICE;
case 2 -> Blocks.FROSTED_ICE;
case 3 -> Blocks.SNOW_BLOCK;
default -> Blocks.ICE;
};
}
}
@@ -1,80 +0,0 @@
package com.craisinlord.antarchy.content.entity.royal.beam;
public record RoyalBeamSettings(
double range,
double targetTracking,
double pathStep,
int durationTicks,
int travelTicks,
int cooldownTicks,
float pathDamageRadius,
float impactDamageRadius,
float damage,
float knockback,
int terrainIntervalTicks,
double maxTerrainDistance,
float pathTerrainRadius,
float impactTerrainRadius,
int maxTerrainMutationsPerTick,
float terrainMutationChance,
float terrainDropChance,
float maxTerrainResistance,
boolean terrainEnabled,
boolean requireLineOfSightForDamage
) {
public static RoyalBeamSettings gauntletPower() {
return new RoyalBeamSettings(64.0D, 0.0D, 2.0D, 18, 4, 50, 0.9F, 1.8F,
9.0F, 0.8F, 1, 64.0D, 0.8F, 1.8F, 6, 1.0F, 1.0F, 50.0F, true, false);
}
public static RoyalBeamSettings gauntletInfinity() {
return new RoyalBeamSettings(64.0D, 0.0D, 1.5D, 24, 4, 1200, 1.25F, 2.5F,
28.0F, 1.8F, 1, 64.0D, 1.0F, 2.0F, 0, 0.0F, 0.0F, 0.0F, false, false);
}
public static RoyalBeamSettings tremorzillaLike(float damage, boolean terrainEnabled) {
return new RoyalBeamSettings(
100.0D,
0.1D,
7.5D,
30,
12,
90,
6.0F,
6.0F,
damage,
1.0F,
3,
100.0D,
5.0F,
4.0F,
192,
1.0F,
0.08F,
15.0F,
terrainEnabled,
true
);
}
public RoyalBeamSettings {
range = Math.max(1.0D, range);
targetTracking = Math.clamp(targetTracking, 0.0D, 1.0D);
pathStep = Math.max(1.0D, pathStep);
durationTicks = Math.max(1, durationTicks);
travelTicks = Math.max(1, travelTicks);
cooldownTicks = Math.max(1, cooldownTicks);
pathDamageRadius = Math.max(0.0F, pathDamageRadius);
impactDamageRadius = Math.max(0.0F, impactDamageRadius);
damage = Math.max(0.0F, damage);
knockback = Math.max(0.0F, knockback);
terrainIntervalTicks = Math.max(1, terrainIntervalTicks);
maxTerrainDistance = Math.max(0.0D, maxTerrainDistance);
pathTerrainRadius = Math.max(0.0F, pathTerrainRadius);
impactTerrainRadius = Math.max(0.0F, impactTerrainRadius);
maxTerrainMutationsPerTick = Math.max(0, maxTerrainMutationsPerTick);
terrainMutationChance = Math.clamp(terrainMutationChance, 0.0F, 1.0F);
terrainDropChance = Math.clamp(terrainDropChance, 0.0F, 1.0F);
maxTerrainResistance = Math.max(0.0F, maxTerrainResistance);
}
}
@@ -1,7 +0,0 @@
package com.craisinlord.antarchy.content.entity.royal.beam;
public enum RoyalBeamTerrainMode {
NONE,
DESTROY,
BUILD_ICE
}
@@ -1,9 +1,9 @@
package com.craisinlord.antarchy.content.item;
import com.craisinlord.anthology.shake.CameraShakeSync;
import com.craisinlord.antarchy.config.AntarchySettings;
import com.craisinlord.anthology.gravity.GravityDirection;
import com.craisinlord.anthology.gravity.GravityRotationUtil;
import com.craisinlord.antarchy.content.network.ImpactShakeSync;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
@@ -279,7 +279,7 @@ public final class AttitudeAdjusterSlamManager {
}
private static void shake(ServerLevel level, Vec3 pos, float intensity, int durationTicks) {
ImpactShakeSync.sendNearby(level, pos, intensity, durationTicks, IMPACT_SHAKE_RADIUS);
CameraShakeSync.sendNearby(level, pos, intensity, durationTicks, IMPACT_SHAKE_RADIUS);
}
private static Vec3 horizontalAway(Vec3 from, Vec3 to, Vec3 fallback) {
@@ -1,10 +1,10 @@
package com.craisinlord.antarchy.content.item;
import com.craisinlord.anthology.shake.CameraShakeSync;
import com.craisinlord.antarchy.config.AntarchySettings;
import com.craisinlord.antarchy.content.effect.CommandedEntityAccess;
import com.craisinlord.antarchy.content.AntarchySoundEvents;
import com.craisinlord.antarchy.content.entity.royal.RoyalIceSpikeEntity;
import com.craisinlord.antarchy.content.network.ImpactShakeSync;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
@@ -111,7 +111,7 @@ public final class RoyalGuardianSwordAbilities {
level.sendParticles(FIRE_GOLD, center.x, center.y, center.z, 28, 1.1D, 0.8D, 1.1D, 0.04D);
level.sendParticles(ParticleTypes.EXPLOSION, center.x, center.y, center.z, 3, 0.8D, 0.6D, 0.8D, 0.0D);
level.playSound(null, BlockPos.containing(center), SoundEvents.GENERIC_EXPLODE.value(), SoundSource.PLAYERS, 1.0F, 1.05F);
ImpactShakeSync.sendNearby(level, center, FIRE_SHAKE_INTENSITY, FIRE_SHAKE_DURATION, FIRE_SHAKE_RADIUS);
CameraShakeSync.sendNearby(level, center, FIRE_SHAKE_INTENSITY, FIRE_SHAKE_DURATION, FIRE_SHAKE_RADIUS);
}
private static void placeBoundedFire(ServerLevel level, Vec3 center, int cap) {
@@ -1,4 +1,4 @@
package com.craisinlord.antarchy.content.time;
package com.craisinlord.antarchy.content.item;
public interface TemporalTunerAccess {
double antarchy$getTemporalTunerRate();
@@ -1,7 +1,6 @@
package com.craisinlord.antarchy.content.item;
import com.craisinlord.antarchy.config.AntarchySettings;
import com.craisinlord.antarchy.content.time.TemporalTunerAccess;
import net.minecraft.ChatFormatting;
import net.minecraft.network.chat.Component;
import net.minecraft.world.entity.player.Player;
@@ -1,94 +0,0 @@
package com.craisinlord.antarchy.content.movement;
import com.craisinlord.antarchy.Antarchy;
import com.craisinlord.antarchy.content.client.PortalGunRollClientState;
import com.craisinlord.antarchy.content.entity.BedBugEntity;
import com.craisinlord.antarchy.content.entity.portal.DimensionalTearEntity;
import com.craisinlord.antarchy.content.fluid.AntarchyFluidChecks;
import com.craisinlord.antarchy.content.worldgen.thoraxis.ThoraxisUndersideManager;
import com.craisinlord.anthology.effect.AnthologyEffects;
import com.craisinlord.anthology.gravity.GravityApi;
import com.craisinlord.anthology.gravity.GravityHooks;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.effect.MobEffectInstance;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.Mob;
public final class AntarchyGravityHooks {
private AntarchyGravityHooks() {
}
public static void register() {
GravityHooks.addInvertedRegion(ThoraxisUndersideManager::isInUnderside);
GravityHooks.addInvertedFluid(AntarchyFluidChecks::isAntiwater);
GravityHooks.addLoadListener(AntarchyGravityHooks::clearStaleUndersideInversion);
}
public static void registerClient() {
GravityHooks.addCameraRollSource(PortalGunRollClientState::getRollDegrees);
}
private static void clearStaleUndersideInversion(Entity entity) {
if (!(entity instanceof Mob mob)
|| !GravityApi.isGravityInverted(mob)
|| !GravityApi.isGravityForced(mob)) {
return;
}
MobEffectInstance invertedEffect = mob.getEffect(AnthologyEffects.inverted());
boolean aboveUndersideExit = ThoraxisUndersideManager.isAboveUndersideExit(mob);
int nearbyDimensionalTears = countNearbyDimensionalTears(mob);
if (invertedEffect != null
&& invertedEffect.isAmbient()
&& !invertedEffect.isVisible()
&& aboveUndersideExit) {
clearInversion(mob);
Antarchy.LOGGER.warn(
"[antarchy-gravity] cleared stale underside inversion from loaded mob type={} uuid={} pos=({}, {}, {}) dim={} effectDuration={} nearbyDimensionalTears={}",
mob.getType(), mob.getUUID(), mob.getX(), mob.getY(), mob.getZ(),
mob.level() != null ? mob.level().dimension().location() : "?",
invertedEffect.getDuration(), nearbyDimensionalTears
);
return;
}
if (invertedEffect != null
&& mob instanceof BedBugEntity
&& aboveUndersideExit
&& ThoraxisUndersideManager.isThoraxis(mob.level())) {
clearInversion(mob);
Antarchy.LOGGER.warn(
"[antarchy-gravity] cleared loaded inverted bed bug above underside exit type={} uuid={} pos=({}, {}, {}) dim={} effectDuration={} effectAmbient={} effectVisible={} nearbyDimensionalTears={}",
mob.getType(), mob.getUUID(), mob.getX(), mob.getY(), mob.getZ(),
mob.level() != null ? mob.level().dimension().location() : "?",
invertedEffect.getDuration(), invertedEffect.isAmbient(), invertedEffect.isVisible(), nearbyDimensionalTears
);
return;
}
Antarchy.LOGGER.info(
"[antarchy-gravity] loaded inverted mob type={} uuid={} pos=({}, {}, {}) dim={} hasInvertedEffect={} effectDuration={} effectAmbient={} effectVisible={} aboveUndersideExit={} nearbyDimensionalTears={}",
mob.getType(), mob.getUUID(), mob.getX(), mob.getY(), mob.getZ(),
mob.level() != null ? mob.level().dimension().location() : "?",
invertedEffect != null,
invertedEffect != null ? invertedEffect.getDuration() : -1,
invertedEffect != null && invertedEffect.isAmbient(),
invertedEffect != null && invertedEffect.isVisible(),
aboveUndersideExit,
nearbyDimensionalTears
);
}
private static void clearInversion(Mob mob) {
mob.removeEffect(AnthologyEffects.inverted());
GravityApi.resetGravityState(mob);
}
private static int countNearbyDimensionalTears(Mob mob) {
if (!(mob.level() instanceof ServerLevel serverLevel)) {
return -1;
}
return serverLevel.getEntitiesOfClass(
DimensionalTearEntity.class,
mob.getBoundingBox().inflate(48.0D),
Entity::isAlive
).size();
}
}
@@ -1,21 +0,0 @@
package com.craisinlord.antarchy.content.movement;
public interface DreamSandLowGravityAccess {
boolean antarchy$isDreamSandLowGravityActive();
void antarchy$setDreamSandLowGravityActive(boolean active);
int antarchy$getDreamSandLowGravityTicksRemaining();
void antarchy$setDreamSandLowGravityTicksRemaining(int ticks);
int antarchy$getDreamSandLandingGraceTicks();
void antarchy$setDreamSandLandingGraceTicks(int ticks);
default void antarchy$clearDreamSandLowGravity() {
antarchy$setDreamSandLowGravityActive(false);
antarchy$setDreamSandLowGravityTicksRemaining(0);
antarchy$setDreamSandLandingGraceTicks(0);
}
}
@@ -1,33 +0,0 @@
package com.craisinlord.antarchy.content.network;
import com.craisinlord.antarchy.Antarchy;
import io.netty.buffer.ByteBuf;
import net.minecraft.network.codec.ByteBufCodecs;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
import net.minecraft.resources.ResourceLocation;
public record ImpactShakePayload(double x, double y, double z, float intensity, int durationTicks, float radius) implements CustomPacketPayload {
public static final Type<ImpactShakePayload> TYPE =
new Type<>(ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "impact_shake"));
public static final StreamCodec<ByteBuf, ImpactShakePayload> STREAM_CODEC = StreamCodec.composite(
ByteBufCodecs.DOUBLE,
ImpactShakePayload::x,
ByteBufCodecs.DOUBLE,
ImpactShakePayload::y,
ByteBufCodecs.DOUBLE,
ImpactShakePayload::z,
ByteBufCodecs.FLOAT,
ImpactShakePayload::intensity,
ByteBufCodecs.VAR_INT,
ImpactShakePayload::durationTicks,
ByteBufCodecs.FLOAT,
ImpactShakePayload::radius,
ImpactShakePayload::new
);
@Override
public Type<ImpactShakePayload> type() {
return TYPE;
}
}
@@ -1,33 +0,0 @@
package com.craisinlord.antarchy.content.network;
import java.util.function.BiConsumer;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.phys.Vec3;
public final class ImpactShakeSync {
private static final BiConsumer<ServerPlayer, ImpactShakePayload> NOOP = (player, payload) -> {
};
private static volatile BiConsumer<ServerPlayer, ImpactShakePayload> sink = NOOP;
private ImpactShakeSync() {
}
public static void setSink(BiConsumer<ServerPlayer, ImpactShakePayload> sink) {
ImpactShakeSync.sink = sink == null ? NOOP : sink;
}
public static void send(ServerPlayer player, ImpactShakePayload payload) {
sink.accept(player, payload);
}
public static void sendNearby(ServerLevel level, Vec3 pos, float intensity, int durationTicks, float radius) {
ImpactShakePayload payload = new ImpactShakePayload(pos.x, pos.y, pos.z, intensity, durationTicks, radius);
double radiusSqr = radius * radius;
for (ServerPlayer player : level.players()) {
if (player.distanceToSqr(pos.x, pos.y, pos.z) <= radiusSqr) {
send(player, payload);
}
}
}
}
@@ -1,43 +0,0 @@
package com.craisinlord.antarchy.content.network;
import com.craisinlord.antarchy.Antarchy;
import io.netty.buffer.ByteBuf;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.phys.Vec3;
public record PortalGunTransitPayload(int portalId, Vec3 start, Vec3 end, Vec3 velocity) implements CustomPacketPayload {
public static final Type<PortalGunTransitPayload> TYPE = new Type<>(ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "portal_gun_transit"));
public static final StreamCodec<ByteBuf, PortalGunTransitPayload> STREAM_CODEC = StreamCodec.of(PortalGunTransitPayload::write, PortalGunTransitPayload::read);
private static void write(ByteBuf buffer, PortalGunTransitPayload payload) {
buffer.writeInt(payload.portalId());
writeVec(buffer, payload.start());
writeVec(buffer, payload.end());
writeVec(buffer, payload.velocity());
}
private static PortalGunTransitPayload read(ByteBuf buffer) {
int portalId = buffer.readInt();
Vec3 start = readVec(buffer);
Vec3 end = readVec(buffer);
Vec3 velocity = readVec(buffer);
return new PortalGunTransitPayload(portalId, start, end, velocity);
}
private static void writeVec(ByteBuf buffer, Vec3 vector) {
buffer.writeDouble(vector.x);
buffer.writeDouble(vector.y);
buffer.writeDouble(vector.z);
}
private static Vec3 readVec(ByteBuf buffer) {
return new Vec3(buffer.readDouble(), buffer.readDouble(), buffer.readDouble());
}
@Override
public Type<PortalGunTransitPayload> type() {
return TYPE;
}
}
@@ -1,46 +0,0 @@
package com.craisinlord.antarchy.content.network;
import com.craisinlord.antarchy.Antarchy;
import com.craisinlord.antarchy.content.time.TimeDilationFieldSnapshot;
import io.netty.buffer.ByteBuf;
import java.util.List;
import net.minecraft.network.codec.ByteBufCodecs;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
import net.minecraft.resources.ResourceLocation;
public record TimeDilationFieldsPayload(List<TimeDilationFieldSnapshot> fields) implements CustomPacketPayload {
public static final Type<TimeDilationFieldsPayload> TYPE =
new Type<>(ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "time_dilation_fields"));
private static final StreamCodec<ByteBuf, TimeDilationFieldSnapshot> FIELD_CODEC = StreamCodec.of(
(buf, field) -> {
buf.writeDouble(field.x());
buf.writeDouble(field.y());
buf.writeDouble(field.z());
buf.writeDouble(field.radius());
buf.writeDouble(field.rate());
buf.writeInt(field.age());
buf.writeInt(field.durationTicks());
},
buf -> new TimeDilationFieldSnapshot(
buf.readDouble(), buf.readDouble(), buf.readDouble(), buf.readDouble(),
buf.readDouble(), buf.readInt(), buf.readInt()
)
);
public static final StreamCodec<ByteBuf, TimeDilationFieldsPayload> STREAM_CODEC = StreamCodec.composite(
ByteBufCodecs.collection(java.util.ArrayList::new, FIELD_CODEC, 128),
TimeDilationFieldsPayload::fields,
TimeDilationFieldsPayload::new
);
public TimeDilationFieldsPayload {
fields = List.copyOf(fields);
}
@Override
public Type<TimeDilationFieldsPayload> type() {
return TYPE;
}
}
@@ -1,33 +0,0 @@
package com.craisinlord.antarchy.content.network;
import com.craisinlord.antarchy.Antarchy;
import io.netty.buffer.ByteBuf;
import java.util.UUID;
import net.minecraft.network.codec.ByteBufCodecs;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
import net.minecraft.resources.ResourceLocation;
public record TimeDilationRatePayload(UUID entityUuid, double rate) implements CustomPacketPayload {
public static final Type<TimeDilationRatePayload> TYPE =
new Type<>(ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "time_dilation_rate"));
private static final StreamCodec<ByteBuf, UUID> UUID_CODEC = StreamCodec.of(
(buf, uuid) -> {
buf.writeLong(uuid.getMostSignificantBits());
buf.writeLong(uuid.getLeastSignificantBits());
},
buf -> new UUID(buf.readLong(), buf.readLong())
);
public static final StreamCodec<ByteBuf, TimeDilationRatePayload> STREAM_CODEC = StreamCodec.composite(
UUID_CODEC,
TimeDilationRatePayload::entityUuid,
ByteBufCodecs.DOUBLE,
TimeDilationRatePayload::rate,
TimeDilationRatePayload::new
);
@Override
public Type<TimeDilationRatePayload> type() {
return TYPE;
}
}
@@ -1,116 +0,0 @@
package com.craisinlord.antarchy.content.portal;
import java.util.LinkedHashSet;
import java.util.Set;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.vehicle.DismountHelper;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.levelgen.Heightmap;
import net.minecraft.world.level.portal.DimensionTransition;
import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.Nullable;
public final class CrossDimensionTravelApi {
private static final int HORIZONTAL_SEARCH_RADIUS = 8;
private static final int VERTICAL_SEARCH_RADIUS = 8;
private CrossDimensionTravelApi() {
}
public static boolean transferToSafePosition(Entity entity, ServerLevel destination, BlockPos preferred) {
if (!entity.isAlive() || !(entity.level() instanceof ServerLevel source)) {
return false;
}
Vec3 arrival = findSafePosition(entity, destination, preferred);
if (arrival == null) {
return false;
}
if (entity instanceof ServerPlayer player) {
player.teleportTo(destination, arrival.x, arrival.y, arrival.z, player.getYRot(), player.getXRot());
player.setPortalCooldown();
return true;
}
if (source == destination) {
entity.teleportTo(arrival.x, arrival.y, arrival.z);
entity.setPortalCooldown();
return true;
}
Entity moved = entity.changeDimension(new DimensionTransition(
destination,
arrival,
Vec3.ZERO,
entity.getYRot(),
entity.getXRot(),
DimensionTransition.PLAY_PORTAL_SOUND
));
if (moved != null) {
moved.setPortalCooldown();
return true;
}
return false;
}
@Nullable
public static Vec3 findSafePosition(Entity entity, ServerLevel destination, BlockPos preferred) {
Set<BlockPos> candidates = new LinkedHashSet<>();
candidates.add(preferred);
for (int radius = 0; radius <= HORIZONTAL_SEARCH_RADIUS; radius++) {
for (int x = -radius; x <= radius; x++) {
for (int z = -radius; z <= radius; z++) {
if (radius > 0 && Math.abs(x) != radius && Math.abs(z) != radius) {
continue;
}
BlockPos column = preferred.offset(x, 0, z);
if (destination.hasChunkAt(column)) {
BlockPos surface = destination.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, column);
candidates.add(surface);
candidates.add(surface.above());
}
for (int y = 0; y <= VERTICAL_SEARCH_RADIUS; y++) {
candidates.add(column.above(y));
if (y > 0) {
candidates.add(column.below(y));
}
}
}
}
}
for (BlockPos candidate : candidates) {
if (!isAreaLoaded(destination, candidate)) {
continue;
}
Vec3 safe = DismountHelper.findSafeDismountLocation(entity.getType(), destination, candidate, true);
if (safe != null && isValidPosition(destination, safe)) {
return safe;
}
}
return null;
}
private static boolean isAreaLoaded(ServerLevel level, BlockPos center) {
if (center.getY() <= level.getMinBuildHeight() || center.getY() >= level.getMaxBuildHeight() - 1
|| !level.getWorldBorder().isWithinBounds(center)) {
return false;
}
for (Direction direction : Direction.values()) {
if (!level.hasChunkAt(center.relative(direction))) {
return false;
}
}
return level.hasChunkAt(center) && level.hasChunkAt(center.above());
}
private static boolean isValidPosition(ServerLevel level, Vec3 position) {
BlockPos feet = BlockPos.containing(position);
BlockPos head = feet.above();
BlockState feetState = level.getBlockState(feet);
BlockState headState = level.getBlockState(head);
return feetState.getFluidState().isEmpty() && headState.getFluidState().isEmpty()
&& feetState.getCollisionShape(level, feet).isEmpty()
&& headState.getCollisionShape(level, head).isEmpty();
}
}
@@ -1,63 +0,0 @@
package com.craisinlord.antarchy.content.portalgun;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
/**
* Conservative side planes through the destination opening and virtual eye.
* The exit plane itself is handled separately so bounds crossing it remain visible.
*/
public final class PortalGunApertureCuller {
private static final double MIN_EYE_DISTANCE = 0.05D;
private static final double PLANE_PADDING = 0.5D;
private final Vec3 eye;
private final Vec3[] inwardNormals;
private PortalGunApertureCuller(Vec3 eye, Vec3[] inwardNormals) {
this.eye = eye;
this.inwardNormals = inwardNormals;
}
public static PortalGunApertureCuller create(PortalGunWorldPortalShape aperture, Vec3 eye) {
double eyeDepth = aperture.localCoords(eye).depth();
if (eyeDepth >= -MIN_EYE_DISTANCE) {
return new PortalGunApertureCuller(eye, null);
}
Vec3[] corners = aperture.getCorners(0.0D);
Vec3 centerRay = aperture.center().subtract(eye);
Vec3[] normals = new Vec3[corners.length];
for (int index = 0; index < corners.length; index++) {
Vec3 first = corners[index].subtract(eye);
Vec3 second = corners[(index + 1) % corners.length].subtract(eye);
Vec3 normal = first.cross(second);
if (normal.lengthSqr() < 1.0E-8D) {
return new PortalGunApertureCuller(eye, null);
}
normal = normal.normalize();
if (normal.dot(centerRay) < 0.0D) {
normal = normal.scale(-1.0D);
}
normals[index] = normal;
}
return new PortalGunApertureCuller(eye, normals);
}
public boolean intersects(AABB bounds) {
if (inwardNormals == null) {
return true;
}
for (Vec3 normal : inwardNormals) {
double supportX = normal.x >= 0.0D ? bounds.maxX : bounds.minX;
double supportY = normal.y >= 0.0D ? bounds.maxY : bounds.minY;
double supportZ = normal.z >= 0.0D ? bounds.maxZ : bounds.minZ;
double maxDistance = (supportX - eye.x) * normal.x
+ (supportY - eye.y) * normal.y
+ (supportZ - eye.z) * normal.z;
if (maxDistance < -PLANE_PADDING) {
return false;
}
}
return true;
}
}
@@ -1,139 +0,0 @@
package com.craisinlord.antarchy.content.portalgun;
import java.util.List;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
import net.minecraft.world.phys.shapes.Shapes;
import net.minecraft.world.phys.shapes.VoxelShape;
public final class PortalGunCollisionHelper {
private static final double SEARCH_PADDING = 1.5D;
private static final double PLANE_TOLERANCE = 0.06D;
private static final double EDGE_PADDING = 0.2D;
private static final double DEPTH_PADDING = 0.45D;
private static final double BORDER_TOLERANCE = 0.02D;
private static final double BORDER_THICKNESS = 0.0125D;
private PortalGunCollisionHelper() {
}
public static Vec3 resolveCollision(Entity entity, AABB startBox, Vec3 movement, Vec3 collided) {
if (movement.lengthSqr() <= 1.0E-7D) {
return collided;
}
AABB pathBox = startBox.expandTowards(movement).inflate(SEARCH_PADDING);
List<PortalGunPortalEntity> nearby = PortalGunPortalEntity.findPortalsNearBounds(entity.level(), pathBox);
if (nearby.isEmpty()) {
return collided;
}
Vec3 adjusted = collided;
for (PortalGunPortalEntity portal : nearby) {
Vec3 candidateAdjusted = resolveCollisionAgainstPortal(entity, startBox, movement, adjusted, portal);
if (candidateAdjusted.distanceToSqr(movement) < adjusted.distanceToSqr(movement)) {
adjusted = candidateAdjusted;
}
}
return adjusted;
}
private static Vec3 resolveCollisionAgainstPortal(Entity entity, AABB startBox, Vec3 movement, Vec3 collided, PortalGunPortalEntity portal) {
PortalGunPortalEntity linked = portal.getLinkedPortal();
if (linked == null || !linked.isAlive() || portal.isTeleportCoolingDown(entity)) {
return collided;
}
PortalGunWorldPortalShape shape = portal.getWorldPortalShape();
Vec3 normal = portal.getNormalVec().normalize();
double desiredNormal = movement.dot(normal);
double collidedNormal = collided.dot(normal);
if (desiredNormal >= -1.0E-5D || desiredNormal >= collidedNormal - 1.0E-5D) {
return collided;
}
if (!canUsePortalPassThrough(entity, startBox, movement, portal, shape)) {
return collided;
}
double restoreAmount = desiredNormal - collidedNormal;
Vec3 adjusted = collided.add(normal.scale(restoreAmount));
return applyBorderCollision(entity, startBox, adjusted, portal);
}
private static boolean canUsePortalPassThrough(Entity entity, AABB startBox, Vec3 movement, PortalGunPortalEntity portal, PortalGunWorldPortalShape shape) {
AABB endBox = startBox.move(movement);
if (!portal.getPortalInsides(entity).intersects(startBox)
&& !portal.getPortalInsides(entity).intersects(endBox)
&& !intersectsPortalWindow(startBox, shape)
&& !intersectsPortalWindow(endBox, shape)
&& !segmentIntersectsPortalWindow(entity, portal, startBox, endBox)) {
return false;
}
Vec3 startProbe = portal.teleportProbePosition(entity, startBox);
Vec3 endProbe = portal.teleportProbePosition(entity, endBox);
if (portal.crossesPortal(startProbe, endProbe)) {
return true;
}
PortalGunWorldPortalShape.PortalLocalCoords startCoords = shape.localCoords(startProbe);
PortalGunWorldPortalShape.PortalLocalCoords endCoords = shape.localCoords(endProbe);
return startCoords.depth() > 0.0D
&& endCoords.depth() >= -PLANE_TOLERANCE
&& Math.abs(endCoords.horizontal()) <= shape.halfWidth() + EDGE_PADDING
&& Math.abs(endCoords.vertical()) <= shape.halfHeight() + EDGE_PADDING
&& projectedWindowOverlap(shape, startProbe, endProbe);
}
private static boolean intersectsPortalWindow(AABB box, PortalGunWorldPortalShape shape) {
for (Vec3 corner : corners(box)) {
PortalGunWorldPortalShape.PortalLocalCoords coords = shape.localCoords(corner);
if (Math.abs(coords.horizontal()) <= shape.halfWidth() + EDGE_PADDING
&& Math.abs(coords.vertical()) <= shape.halfHeight() + EDGE_PADDING
&& coords.depth() <= DEPTH_PADDING
&& coords.depth() >= -PLANE_TOLERANCE) {
return true;
}
}
return false;
}
private static boolean segmentIntersectsPortalWindow(Entity entity, PortalGunPortalEntity portal, AABB startBox, AABB endBox) {
return portal.crossesPortal(portal.teleportProbePosition(entity, startBox), portal.teleportProbePosition(entity, endBox));
}
private static boolean projectedWindowOverlap(PortalGunWorldPortalShape shape, Vec3 startProbe, Vec3 endProbe) {
PortalGunWorldPortalShape.PortalLocalCoords start = shape.localCoords(startProbe);
PortalGunWorldPortalShape.PortalLocalCoords end = shape.localCoords(endProbe);
return Math.abs(start.horizontal()) <= shape.halfWidth() + BORDER_TOLERANCE
&& Math.abs(start.vertical()) <= shape.halfHeight() + BORDER_TOLERANCE
&& Math.abs(end.horizontal()) <= shape.halfWidth() + BORDER_TOLERANCE
&& Math.abs(end.vertical()) <= shape.halfHeight() + BORDER_TOLERANCE;
}
private static Vec3 applyBorderCollision(Entity entity, AABB startBox, Vec3 movement, PortalGunPortalEntity portal) {
if (movement.lengthSqr() <= 1.0E-7D) {
return movement;
}
AABB endBox = startBox.move(movement);
if (!portal.getPortalInsides(entity).intersects(startBox)
&& !portal.getPortalInsides(entity).intersects(endBox)
&& !intersectsPortalWindow(startBox, portal.getWorldPortalShape())
&& !intersectsPortalWindow(endBox, portal.getWorldPortalShape())) {
return movement;
}
List<VoxelShape> shapes = portal.getWorldPortalShape().getCollisionBoundaries(BORDER_THICKNESS).stream()
.map(Shapes::create)
.toList();
Vec3 collided = Entity.collideBoundingBox(entity, movement, startBox, entity.level(), shapes);
return collided.distanceToSqr(movement) < 1.0E-7D ? movement : collided;
}
private static Vec3[] corners(AABB box) {
return new Vec3[] {
new Vec3(box.minX, box.minY, box.minZ),
new Vec3(box.minX, box.minY, box.maxZ),
new Vec3(box.minX, box.maxY, box.minZ),
new Vec3(box.minX, box.maxY, box.maxZ),
new Vec3(box.maxX, box.minY, box.minZ),
new Vec3(box.maxX, box.minY, box.maxZ),
new Vec3(box.maxX, box.maxY, box.minZ),
new Vec3(box.maxX, box.maxY, box.maxZ)
};
}
}
@@ -1,99 +0,0 @@
package com.craisinlord.antarchy.content.portalgun;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.AABB;
public final class PortalGunPortalRegistry {
private static final Map<Level, Map<UUID, PortalGunPortalEntity>> LEVELS = Collections.synchronizedMap(new WeakHashMap<>());
private static final AtomicInteger TRACKED = new AtomicInteger();
private PortalGunPortalRegistry() {
}
static void track(PortalGunPortalEntity portal) {
Level level = portal.level();
if (level == null) {
return;
}
Map<UUID, PortalGunPortalEntity> portals;
synchronized (LEVELS) {
portals = LEVELS.computeIfAbsent(level, ignored -> new ConcurrentHashMap<>());
}
if (portals.put(portal.getUUID(), portal) == null) {
TRACKED.incrementAndGet();
}
}
static void untrack(PortalGunPortalEntity portal) {
Map<UUID, PortalGunPortalEntity> portals = portalsIn(portal.level());
if (portals != null && portals.remove(portal.getUUID(), portal)) {
TRACKED.decrementAndGet();
}
}
public static boolean hasAny(Level level) {
if (TRACKED.get() <= 0) {
return false;
}
Map<UUID, PortalGunPortalEntity> portals = portalsIn(level);
return portals != null && !portals.isEmpty();
}
public static Collection<PortalGunPortalEntity> all(Level level) {
if (TRACKED.get() <= 0) {
return List.of();
}
Map<UUID, PortalGunPortalEntity> portals = portalsIn(level);
return portals == null ? List.of() : portals.values();
}
public static PortalGunPortalEntity find(Level level, UUID portalId) {
if (portalId == null) {
return null;
}
Map<UUID, PortalGunPortalEntity> portals = portalsIn(level);
if (portals == null) {
return null;
}
PortalGunPortalEntity portal = portals.get(portalId);
return portal != null && portal.isAlive() ? portal : null;
}
public static List<PortalGunPortalEntity> near(Level level, AABB bounds) {
if (TRACKED.get() <= 0) {
return List.of();
}
Map<UUID, PortalGunPortalEntity> portals = portalsIn(level);
if (portals == null || portals.isEmpty()) {
return List.of();
}
List<PortalGunPortalEntity> nearby = null;
for (PortalGunPortalEntity portal : portals.values()) {
if (portal.isAlive() && portal.getPortalInsides().intersects(bounds)) {
if (nearby == null) {
nearby = new ArrayList<>(2);
}
nearby.add(portal);
}
}
return nearby == null ? List.of() : nearby;
}
private static Map<UUID, PortalGunPortalEntity> portalsIn(Level level) {
if (level == null) {
return null;
}
synchronized (LEVELS) {
return LEVELS.get(level);
}
}
}
@@ -1,60 +0,0 @@
package com.craisinlord.antarchy.content.portalgun;
import net.minecraft.world.phys.Vec3;
import org.joml.Matrix4f;
import org.joml.Vector4f;
/** Applies the destination portal plane as the near plane of a camera projection. */
public final class PortalGunProjectionUtil {
public static final double MIN_EYE_PLANE_DISTANCE = 0.01D;
private PortalGunProjectionUtil() {
}
public static Matrix4f clipAtExit(Matrix4f projection, Vec3 eye, Vec3 look, Vec3 up,
Vec3 planePoint, Vec3 planeNormal) {
Vector4f clipPlane = planeInCameraSpace(eye, look, up, planePoint, planeNormal);
if (clipPlane.w() > -MIN_EYE_PLANE_DISTANCE) {
return projection;
}
Vector4f q = new Vector4f(signNonZero(clipPlane.x()), signNonZero(clipPlane.y()), 1.0F, 1.0F)
.mul(new Matrix4f(projection).invert());
float planeCornerDot = clipPlane.dot(q);
if (!Float.isFinite(planeCornerDot) || Math.abs(planeCornerDot) <= 1.0E-6F) {
return projection;
}
float scale = 2.0F / planeCornerDot;
Vector4f c = clipPlane.mul(scale, new Vector4f());
Matrix4f clipped = new Matrix4f(projection);
clipped.m02(c.x());
clipped.m12(c.y());
clipped.m22(c.z() + 1.0F);
clipped.m32(c.w());
return clipped;
}
private static Vector4f planeInCameraSpace(Vec3 eye, Vec3 look, Vec3 up, Vec3 planePoint, Vec3 planeNormal) {
Vec3 forward = look.normalize();
Vec3 correctedUp = up.subtract(forward.scale(up.dot(forward))).normalize();
Vec3 right = forward.cross(correctedUp).normalize();
Vec3 relativePoint = planePoint.subtract(eye);
Vector4f point = new Vector4f(
(float) relativePoint.dot(right),
(float) relativePoint.dot(correctedUp),
(float) -relativePoint.dot(forward),
1.0F
);
Vec3 unitNormal = planeNormal.normalize();
Vector4f normal = new Vector4f(
(float) unitNormal.dot(right),
(float) unitNormal.dot(correctedUp),
(float) -unitNormal.dot(forward),
0.0F
);
return new Vector4f(normal.x(), normal.y(), normal.z(), -normal.dot(point));
}
private static float signNonZero(float value) {
return value >= 0.0F ? 1.0F : -1.0F;
}
}
@@ -1,127 +0,0 @@
package com.craisinlord.antarchy.content.portalgun;
import net.minecraft.util.Mth;
import net.minecraft.world.phys.Vec3;
import org.joml.Matrix3f;
import org.joml.Quaternionf;
import org.joml.Vector3f;
public final class PortalGunTransformUtil {
private static final Vec3 WORLD_UP = new Vec3(0.0D, 1.0D, 0.0D);
private static final Vec3 WORLD_FORWARD_FALLBACK = new Vec3(0.0D, 0.0D, 1.0D);
private PortalGunTransformUtil() {
}
public static Quaternionf createTransform(PortalGunPortalEntity sourcePortal, PortalGunPortalEntity destinationPortal) {
return createTransform(sourcePortal.getWidthVec(), sourcePortal.getUpVec(), sourcePortal.getNormalVec().scale(-1.0D), destinationPortal.getWidthVec().scale(-1.0D), destinationPortal.getUpVec(), destinationPortal.getNormalVec());
}
public static Quaternionf createTransform(Vec3 sourceWidth, Vec3 sourceUp, Vec3 sourceForward, Vec3 destinationWidth, Vec3 destinationUp, Vec3 destinationForward) {
Matrix3f sourceBasis = new Matrix3f(
(float) sourceWidth.x, (float) sourceWidth.y, (float) sourceWidth.z,
(float) sourceUp.x, (float) sourceUp.y, (float) sourceUp.z,
(float) sourceForward.x, (float) sourceForward.y, (float) sourceForward.z
);
Matrix3f destinationBasis = new Matrix3f(
(float) destinationWidth.x, (float) destinationWidth.y, (float) destinationWidth.z,
(float) destinationUp.x, (float) destinationUp.y, (float) destinationUp.z,
(float) destinationForward.x, (float) destinationForward.y, (float) destinationForward.z
);
Matrix3f transform = destinationBasis.mul(sourceBasis.transpose(new Matrix3f()), new Matrix3f());
return transform.getNormalizedRotation(new Quaternionf());
}
public static Vec3 transform(Vec3 vector, Quaternionf quaternion) {
Vector3f rotated = new Vector3f((float) vector.x, (float) vector.y, (float) vector.z).rotate(quaternion);
return new Vec3(rotated.x(), rotated.y(), rotated.z());
}
public static Vec3 transformRelativePosition(PortalGunPortalEntity sourcePortal, PortalGunPortalEntity destinationPortal, Vec3 relativePosition) {
return transformPosition(sourcePortal, destinationPortal, relativePosition);
}
public static Vec3 transformPosition(PortalGunPortalEntity sourcePortal, PortalGunPortalEntity destinationPortal, Vec3 relativePosition) {
Vec3 sourceWidth = sourcePortal.getWidthVec().normalize();
Vec3 sourceUp = sourcePortal.getUpVec().normalize();
Vec3 sourceNormal = sourcePortal.getNormalVec().normalize();
Vec3 destinationWidth = destinationPortal.getWidthVec().normalize();
Vec3 destinationUp = destinationPortal.getUpVec().normalize();
Vec3 destinationNormal = destinationPortal.getNormalVec().normalize();
return destinationWidth.scale(-relativePosition.dot(sourceWidth))
.add(destinationUp.scale(relativePosition.dot(sourceUp)))
.add(destinationNormal.scale(-relativePosition.dot(sourceNormal)));
}
public static Vec3 transformVector(PortalGunPortalEntity sourcePortal, PortalGunPortalEntity destinationPortal, Vec3 vector) {
Vec3 sourceWidth = sourcePortal.getWidthVec().normalize();
Vec3 sourceUp = sourcePortal.getUpVec().normalize();
Vec3 sourceInto = sourcePortal.getNormalVec().normalize().scale(-1.0D);
Vec3 destinationWidth = destinationPortal.getWidthVec().normalize();
Vec3 destinationUp = destinationPortal.getUpVec().normalize();
Vec3 destinationOut = destinationPortal.getNormalVec().normalize();
return destinationWidth.scale(-vector.dot(sourceWidth))
.add(destinationUp.scale(vector.dot(sourceUp)))
.add(destinationOut.scale(vector.dot(sourceInto)));
}
public static Vec3 transformPosition(PortalGunWorldPortalShape source, PortalGunWorldPortalShape destination, Vec3 relativePosition) {
return transformVector(source, destination, relativePosition);
}
public static Vec3 transformVector(PortalGunWorldPortalShape source, PortalGunWorldPortalShape destination, Vec3 vector) {
return destination.right().scale(-vector.dot(source.right()))
.add(destination.up().scale(vector.dot(source.up())))
.add(destination.normal().scale(-vector.dot(source.normal())));
}
public static float yawFromLook(Vec3 look) {
return (float) Math.toDegrees(Math.atan2(-look.x, look.z));
}
public static float pitchFromLook(Vec3 look) {
return (float) Math.toDegrees(-Math.asin(Mth.clamp(look.y, -1.0D, 1.0D)));
}
public static Vec3 upVectorFromLookAndRoll(Vec3 look, float rollDegrees) {
Vec3 normalizedLook = look.normalize();
Vec3 referenceUp = Math.abs(normalizedLook.dot(WORLD_UP)) > 0.999D ? WORLD_FORWARD_FALLBACK : WORLD_UP;
Vec3 right = referenceUp.cross(normalizedLook).normalize();
Vec3 up = normalizedLook.cross(right).normalize();
if (Math.abs(rollDegrees) <= 1.0E-4F) {
return up;
}
Quaternionf roll = new Quaternionf().fromAxisAngleDeg((float) normalizedLook.x, (float) normalizedLook.y, (float) normalizedLook.z, rollDegrees);
return transform(up, roll).normalize();
}
public static float rollFromOrientation(Vec3 look, Vec3 up) {
Vec3 normalizedLook = look.normalize();
Vec3 normalizedUp = up.normalize();
Vec3 baseUp = upVectorFromLookAndRoll(normalizedLook, 0.0F);
Vec3 projectedUp = normalizedUp.subtract(normalizedLook.scale(normalizedUp.dot(normalizedLook)));
if (projectedUp.lengthSqr() < 1.0E-8D) {
return 0.0F;
}
projectedUp = projectedUp.normalize();
double sin = normalizedLook.dot(baseUp.cross(projectedUp));
double cos = Mth.clamp(baseUp.dot(projectedUp), -1.0D, 1.0D);
return normalizeDegrees((float) Math.toDegrees(Math.atan2(sin, cos)));
}
public static Quaternionf orientationQuaternion(Vec3 look, Vec3 up) {
Vec3 forward = look.normalize();
Vec3 correctedUp = up.subtract(forward.scale(up.dot(forward))).normalize();
Vec3 right = forward.cross(correctedUp).normalize();
Matrix3f basis = new Matrix3f(
(float) right.x, (float) right.y, (float) right.z,
(float) correctedUp.x, (float) correctedUp.y, (float) correctedUp.z,
(float) -forward.x, (float) -forward.y, (float) -forward.z
);
return basis.getNormalizedRotation(new Quaternionf());
}
public static float normalizeDegrees(float degrees) {
return Mth.wrapDegrees(degrees);
}
}
@@ -1,18 +0,0 @@
package com.craisinlord.antarchy.content.portalgun;
import com.craisinlord.antarchy.content.network.PortalGunTransitPayload;
import net.minecraft.server.level.ServerPlayer;
public final class PortalGunTransitHandler {
private PortalGunTransitHandler() {
}
public static void handle(ServerPlayer player, PortalGunTransitPayload payload) {
if (player == null) {
return;
}
if (!PortalGunPortalEntity.handleClientTransit(player, payload.portalId(), payload.start(), payload.end(), payload.velocity())) {
player.connection.teleport(player.getX(), player.getY(), player.getZ(), player.getYRot(), player.getXRot());
}
}
}
@@ -1,138 +0,0 @@
package com.craisinlord.antarchy.content.portalgun;
import com.craisinlord.anthology.gravity.GravityApi;
import com.craisinlord.anthology.gravity.GravityDirection;
import com.craisinlord.anthology.gravity.GravityRotationUtil;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
public final class PortalGunTransitMath {
public static final double EXIT_CLEARANCE = 0.02D;
public static final double LEADING_FACE_TOLERANCE = 0.05D;
private static final double UPWARD_EXIT_NORMAL_Y = 0.5D;
private PortalGunTransitMath() {
}
public static double extentAlong(AABB box, Vec3 axis) {
return (box.maxX - box.minX) * 0.5D * Math.abs(axis.x)
+ (box.maxY - box.minY) * 0.5D * Math.abs(axis.y)
+ (box.maxZ - box.minZ) * 0.5D * Math.abs(axis.z);
}
public static boolean fitsOpening(PortalGunWorldPortalShape shape, AABB box, double tolerance) {
PortalGunWorldPortalShape.PortalLocalCoords center = shape.localCoords(box.getCenter());
return Math.abs(center.horizontal()) + extentAlong(box, shape.right()) <= shape.halfWidth() + tolerance
&& Math.abs(center.vertical()) + extentAlong(box, shape.up()) <= shape.halfHeight() + tolerance;
}
public static boolean centredOverOpening(PortalGunWorldPortalShape shape, AABB box) {
PortalGunWorldPortalShape.PortalLocalCoords center = shape.localCoords(box.getCenter());
return Math.abs(center.horizontal()) <= shape.halfWidth() && Math.abs(center.vertical()) <= shape.halfHeight();
}
public static boolean crossesWithBody(PortalGunWorldPortalShape shape, AABB previousBox, AABB currentBox, Vec3 attemptedMovement) {
double previousDepth = shape.localCoords(previousBox.getCenter()).depth();
double currentDepth = shape.localCoords(currentBox.getCenter()).depth();
if (previousDepth <= 0.0D) {
return false;
}
boolean movingInward = attemptedMovement.dot(shape.normal()) < -1.0E-6D || currentDepth < previousDepth - 1.0E-6D;
if (!movingInward) {
return false;
}
if (currentDepth - extentAlong(currentBox, shape.normal()) > LEADING_FACE_TOLERANCE) {
return false;
}
return centredOverOpening(shape, currentBox);
}
public static Vec3 transformVector(PortalGunWorldPortalShape source, PortalGunWorldPortalShape destination, Vec3 vector) {
return PortalGunTransformUtil.transformVector(source, destination, vector);
}
public static AABB exitBox(PortalGunWorldPortalShape source, PortalGunWorldPortalShape destination, AABB box) {
PortalGunWorldPortalShape.PortalLocalCoords local = source.localCoords(box.getCenter());
double horizontal = clampLateral(-local.horizontal(), destination.halfWidth(), extentAlong(box, destination.right()));
double vertical = clampLateral(local.vertical(), destination.halfHeight(), extentAlong(box, destination.up()));
double depth = extentAlong(box, destination.normal()) + EXIT_CLEARANCE;
Vec3 center = destination.center()
.add(destination.right().scale(horizontal))
.add(destination.up().scale(vertical))
.add(destination.normal().scale(depth));
Vec3 offset = center.subtract(box.getCenter());
return box.move(offset);
}
public static Vec3 feetPosition(AABB box) {
return new Vec3((box.minX + box.maxX) * 0.5D, box.minY, (box.minZ + box.maxZ) * 0.5D);
}
public static Vec3 entityPosition(AABB box, Entity entity) {
return entityPosition(box, GravityApi.isGravityInverted(entity));
}
public static Vec3 entityPosition(AABB box, boolean inverted) {
if (!inverted) {
return feetPosition(box);
}
return new Vec3((box.minX + box.maxX) * 0.5D, box.maxY, (box.minZ + box.maxZ) * 0.5D);
}
public static Vec3 entityVelocityToWorld(Entity entity, Vec3 velocity) {
return entityVelocityToWorld(velocity, GravityApi.isGravityInverted(entity));
}
public static Vec3 entityVelocityToWorld(Vec3 velocity, boolean inverted) {
if (!inverted) {
return velocity;
}
return GravityRotationUtil.vecPlayerToWorld(velocity, GravityDirection.UP);
}
public static Vec3 entityVelocityFromWorld(Entity entity, Vec3 velocity) {
return entityVelocityFromWorld(velocity, GravityApi.isGravityInverted(entity));
}
public static Vec3 entityVelocityFromWorld(Vec3 velocity, boolean inverted) {
if (!inverted) {
return velocity;
}
return GravityRotationUtil.vecWorldToPlayer(velocity, GravityDirection.UP);
}
public static Vec3 exitVelocity(PortalGunWorldPortalShape source, PortalGunWorldPortalShape destination, Vec3 velocity, double minUpwardExitSpeed, double maxExitSpeed) {
Vec3 exit = transformVector(source, destination, velocity);
Vec3 normal = destination.normal();
if (minUpwardExitSpeed > 0.0D && normal.y > UPWARD_EXIT_NORMAL_Y) {
double outward = exit.dot(normal);
if (outward < minUpwardExitSpeed) {
exit = exit.add(normal.scale(minUpwardExitSpeed - outward));
}
}
if (maxExitSpeed > 0.0D && exit.lengthSqr() > maxExitSpeed * maxExitSpeed) {
exit = exit.normalize().scale(maxExitSpeed);
}
return exit;
}
public static float exitFallDistance(float fallDistance, Vec3 exitVelocity) {
return exitVelocity.y < 0.0D ? fallDistance : 0.0F;
}
public static float exitFallDistance(float fallDistance, Vec3 exitVelocity, Entity entity) {
if (!GravityApi.isGravityInverted(entity)) {
return exitFallDistance(fallDistance, exitVelocity);
}
return exitVelocity.y > 0.0D ? fallDistance : 0.0F;
}
private static double clampLateral(double coordinate, double halfSize, double extent) {
double limit = halfSize - extent;
if (limit <= 0.0D) {
return 0.0D;
}
return Math.max(-limit, Math.min(limit, coordinate));
}
}
@@ -1,201 +0,0 @@
package com.craisinlord.antarchy.content.portalgun;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
import java.util.List;
public record PortalGunWorldPortalShape(
Vec3 center,
Vec3 normal,
Vec3 up,
Vec3 right,
double halfWidth,
double halfHeight,
double halfDepth
) {
public static final double DEFAULT_SCAN_DISTANCE = 3.0D;
public AABB getPlane() {
return this.createPlaneAround(0.0D, 0.0125D);
}
public AABB getFlatPlane() {
return this.createPlaneAround(0.0D, 0.0D);
}
public AABB getScanRange() {
return this.getFlatPlane().expandTowards(this.normal.scale(DEFAULT_SCAN_DISTANCE));
}
public AABB getPortalInsides() {
return this.createPlaneAround(0.0D, this.halfDepth);
}
public AABB getTeleportPlane(double offset) {
return this.createPlaneAround(offset, this.halfDepth);
}
public PortalGunWorldPortalShape scaleAperture(double scale) {
double safeScale = Math.max(0.0D, scale);
return new PortalGunWorldPortalShape(this.center, this.normal, this.up, this.right, this.halfWidth * safeScale, this.halfHeight * safeScale, this.halfDepth);
}
public Vec3[] getCorners(double normalOffset) {
Vec3 center = this.center.add(this.normal.scale(normalOffset));
Vec3 width = this.right.scale(this.halfWidth);
Vec3 height = this.up.scale(this.halfHeight);
return new Vec3[] {
center.subtract(width).subtract(height),
center.subtract(width).add(height),
center.add(width).add(height),
center.add(width).subtract(height)
};
}
public Vec3[] getOutline(double normalOffset, int segments, double radiusHorizontal, double radiusVertical) {
int count = Math.max(3, segments);
Vec3 center = this.center.add(this.normal.scale(normalOffset));
Vec3[] outline = new Vec3[count];
for (int index = 0; index < count; index++) {
double angle = Math.PI * 2.0D * index / count;
outline[index] = center
.add(this.right.scale(Math.cos(angle) * radiusHorizontal))
.add(this.up.scale(Math.sin(angle) * radiusVertical));
}
return outline;
}
public PortalLocalCoords localCoords(Vec3 position) {
Vec3 relative = position.subtract(this.center);
return new PortalLocalCoords(relative.dot(this.right), relative.dot(this.up), relative.dot(this.normal));
}
public boolean contains(Vec3 position, double depthPadding) {
PortalLocalCoords coords = this.localCoords(position);
return Math.abs(coords.horizontal()) <= this.halfWidth
&& Math.abs(coords.vertical()) <= this.halfHeight
&& Math.abs(coords.depth()) <= this.halfDepth + depthPadding;
}
public boolean crosses(Vec3 previousPosition, Vec3 currentPosition) {
PortalLocalCoords previous = this.localCoords(previousPosition);
PortalLocalCoords current = this.localCoords(currentPosition);
double depthDelta = current.depth() - previous.depth();
if (depthDelta > -1.0E-6D || previous.depth() <= 0.0D || current.depth() > 0.0D) {
return false;
}
double t = -previous.depth() / depthDelta;
if (t < 0.0D || t > 1.0D) {
return false;
}
double horizontal = previous.horizontal() + (current.horizontal() - previous.horizontal()) * t;
double vertical = previous.vertical() + (current.vertical() - previous.vertical()) * t;
return Math.abs(horizontal) <= this.halfWidth && Math.abs(vertical) <= this.halfHeight;
}
public boolean shouldRenderFront(Vec3 cameraPos) {
return this.localCoords(cameraPos).depth() > 0.0D;
}
public boolean intersectsFront(AABB box, double tolerance) {
double maxDepth = Double.NEGATIVE_INFINITY;
for (Vec3 corner : corners(box)) {
maxDepth = Math.max(maxDepth, this.localCoords(corner).depth());
if (maxDepth >= -tolerance) {
return true;
}
}
return false;
}
public boolean intersectsPortalColumn(AABB box, double horizontalPadding, double verticalPadding, double depthPadding) {
double minHorizontal = Double.POSITIVE_INFINITY;
double maxHorizontal = Double.NEGATIVE_INFINITY;
double minVertical = Double.POSITIVE_INFINITY;
double maxVertical = Double.NEGATIVE_INFINITY;
double minDepth = Double.POSITIVE_INFINITY;
double maxDepth = Double.NEGATIVE_INFINITY;
for (Vec3 corner : corners(box)) {
PortalLocalCoords coords = this.localCoords(corner);
minHorizontal = Math.min(minHorizontal, coords.horizontal());
maxHorizontal = Math.max(maxHorizontal, coords.horizontal());
minVertical = Math.min(minVertical, coords.vertical());
maxVertical = Math.max(maxVertical, coords.vertical());
minDepth = Math.min(minDepth, coords.depth());
maxDepth = Math.max(maxDepth, coords.depth());
}
return maxHorizontal >= -this.halfWidth - horizontalPadding
&& minHorizontal <= this.halfWidth + horizontalPadding
&& maxVertical >= -this.halfHeight - verticalPadding
&& minVertical <= this.halfHeight + verticalPadding
&& maxDepth >= -this.halfDepth - depthPadding
&& minDepth <= this.halfDepth + depthPadding;
}
public AABB getBoundsForCulling() {
return this.createPlaneAround(0.0D, this.halfDepth).inflate(1.5D);
}
public List<AABB> getCollisionBoundaries(double borderThickness) {
double horizontalOuter = this.halfWidth + borderThickness;
double verticalOuter = this.halfHeight + borderThickness;
return List.of(
this.createLocalBox(-horizontalOuter, -this.halfWidth, -verticalOuter, verticalOuter, -this.halfDepth, this.halfDepth),
this.createLocalBox(this.halfWidth, horizontalOuter, -verticalOuter, verticalOuter, -this.halfDepth, this.halfDepth),
this.createLocalBox(-this.halfWidth, this.halfWidth, -verticalOuter, -this.halfHeight, -this.halfDepth, this.halfDepth),
this.createLocalBox(-this.halfWidth, this.halfWidth, this.halfHeight, verticalOuter, -this.halfDepth, this.halfDepth)
);
}
private AABB createPlaneAround(double planeOffset, double thickness) {
Vec3 boxCenter = this.center.add(this.normal.scale(planeOffset));
double halfX = this.halfWidth * Math.abs(this.right.x) + this.halfHeight * Math.abs(this.up.x) + thickness * Math.abs(this.normal.x);
double halfY = this.halfWidth * Math.abs(this.right.y) + this.halfHeight * Math.abs(this.up.y) + thickness * Math.abs(this.normal.y);
double halfZ = this.halfWidth * Math.abs(this.right.z) + this.halfHeight * Math.abs(this.up.z) + thickness * Math.abs(this.normal.z);
return new AABB(boxCenter.x - halfX, boxCenter.y - halfY, boxCenter.z - halfZ, boxCenter.x + halfX, boxCenter.y + halfY, boxCenter.z + halfZ);
}
private AABB createLocalBox(double minHorizontal, double maxHorizontal, double minVertical, double maxVertical, double minDepth, double maxDepth) {
Vec3[] corners = new Vec3[] {
this.center.add(this.right.scale(minHorizontal)).add(this.up.scale(minVertical)).add(this.normal.scale(minDepth)),
this.center.add(this.right.scale(minHorizontal)).add(this.up.scale(minVertical)).add(this.normal.scale(maxDepth)),
this.center.add(this.right.scale(minHorizontal)).add(this.up.scale(maxVertical)).add(this.normal.scale(minDepth)),
this.center.add(this.right.scale(minHorizontal)).add(this.up.scale(maxVertical)).add(this.normal.scale(maxDepth)),
this.center.add(this.right.scale(maxHorizontal)).add(this.up.scale(minVertical)).add(this.normal.scale(minDepth)),
this.center.add(this.right.scale(maxHorizontal)).add(this.up.scale(minVertical)).add(this.normal.scale(maxDepth)),
this.center.add(this.right.scale(maxHorizontal)).add(this.up.scale(maxVertical)).add(this.normal.scale(minDepth)),
this.center.add(this.right.scale(maxHorizontal)).add(this.up.scale(maxVertical)).add(this.normal.scale(maxDepth))
};
double minX = Double.POSITIVE_INFINITY;
double minY = Double.POSITIVE_INFINITY;
double minZ = Double.POSITIVE_INFINITY;
double maxX = Double.NEGATIVE_INFINITY;
double maxY = Double.NEGATIVE_INFINITY;
double maxZ = Double.NEGATIVE_INFINITY;
for (Vec3 corner : corners) {
minX = Math.min(minX, corner.x);
minY = Math.min(minY, corner.y);
minZ = Math.min(minZ, corner.z);
maxX = Math.max(maxX, corner.x);
maxY = Math.max(maxY, corner.y);
maxZ = Math.max(maxZ, corner.z);
}
return new AABB(minX, minY, minZ, maxX, maxY, maxZ);
}
private static Vec3[] corners(AABB box) {
return new Vec3[] {
new Vec3(box.minX, box.minY, box.minZ),
new Vec3(box.minX, box.minY, box.maxZ),
new Vec3(box.minX, box.maxY, box.minZ),
new Vec3(box.minX, box.maxY, box.maxZ),
new Vec3(box.maxX, box.minY, box.minZ),
new Vec3(box.maxX, box.minY, box.maxZ),
new Vec3(box.maxX, box.maxY, box.minZ),
new Vec3(box.maxX, box.maxY, box.maxZ)
};
}
public record PortalLocalCoords(double horizontal, double vertical, double depth) {
}
}
@@ -1,65 +0,0 @@
package com.craisinlord.antarchy.content.time;
import com.craisinlord.antarchy.content.enchantment.AntarchyEnchantments;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
public final class ChronosphereManager {
public static final double RATE = 0.35D;
private static final Map<UUID, UUID> ACTIVE_FIELDS = new HashMap<>();
private ChronosphereManager() {
}
public static void refresh(ServerPlayer player) {
if (!(player.level() instanceof ServerLevel level)) {
return;
}
int enchantmentLevel = AntarchyEnchantments.chronosphereLevel(player);
UUID playerId = player.getUUID();
UUID fieldId = ACTIVE_FIELDS.get(playerId);
TimeDilationFieldEntity field = fieldId != null && level.getEntity(fieldId) instanceof TimeDilationFieldEntity existing
? existing
: null;
if (enchantmentLevel <= 0 || !player.isAlive()) {
ACTIVE_FIELDS.remove(playerId);
if (field != null) {
field.discard();
}
return;
}
List<TimeDilationFieldEntity> fields = new ArrayList<>();
if (field == null) {
for (var entity : level.getAllEntities()) {
if (entity instanceof TimeDilationFieldEntity candidate && candidate.isChronosphere() && candidate.isOwnedBy(player)) {
fields.add(candidate);
}
}
}
if (field == null && fields.isEmpty()) {
field = TimeDilationApi.createField(level, player.position(), radius(enchantmentLevel), RATE, -1, player.getUUID(), false);
field.configureChronosphere(player);
} else if (field == null) {
field = fields.getFirst();
field.configureChronosphere(player);
for (int index = 1; index < fields.size(); index++) {
fields.get(index).discard();
}
}
field.setFieldRadius(radius(enchantmentLevel));
ACTIVE_FIELDS.put(playerId, field.getUUID());
}
public static double radius(int level) {
return switch (level) {
case 1 -> 1.5D;
case 2 -> 2.5D;
default -> 3.5D;
};
}
}
@@ -1,222 +0,0 @@
package com.craisinlord.antarchy.content.time;
import java.util.function.BiConsumer;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.function.Predicate;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.phys.Vec3;
public final class TimeDilationApi {
private static BiConsumer<Entity, Double> syncDispatcher = (entity, rate) -> {
};
private static BiConsumer<ServerPlayer, List<TimeDilationFieldSnapshot>> fieldSyncDispatcher = (player, fields) -> {
};
private static final Map<UUID, Double> SYNCED_CLIENT_RATES = new ConcurrentHashMap<>();
private static final ThreadLocal<Integer> ROTATION_BYPASS_DEPTH = ThreadLocal.withInitial(() -> 0);
private static final List<Predicate<Entity>> HORIZONTAL_ONLY_MOVEMENT = new CopyOnWriteArrayList<>();
private TimeDilationApi() {
}
public static void setSyncDispatcher(BiConsumer<Entity, Double> dispatcher) {
syncDispatcher = dispatcher != null ? dispatcher : (entity, rate) -> {
};
}
public static void setFieldSyncDispatcher(BiConsumer<ServerPlayer, List<TimeDilationFieldSnapshot>> dispatcher) {
fieldSyncDispatcher = dispatcher != null ? dispatcher : (player, fields) -> {
};
}
public static TimeDilationFieldEntity createField(ServerLevel level, Vec3 center, double radius, double rate, int durationTicks) {
return createField(level, center, radius, rate, durationTicks, null);
}
public static TimeDilationFieldEntity createField(ServerLevel level, Vec3 center, double radius, double rate, int durationTicks, UUID ownerId) {
return createField(level, center, radius, rate, durationTicks, ownerId, true);
}
public static TimeDilationFieldEntity createField(ServerLevel level, Vec3 center, double radius, double rate, int durationTicks, UUID ownerId, boolean visual) {
TimeDilationFieldEntity field = TimeDilationFieldEntity.create(level, center, radius, rate, durationTicks, ownerId, visual);
level.addFreshEntity(field);
return field;
}
public static double getRate(Entity entity) {
if (entity instanceof TimeDilationEntityAccess access) {
if (entity.level().isClientSide) {
Double syncedRate = SYNCED_CLIENT_RATES.get(entity.getUUID());
if (syncedRate != null) {
return syncedRate;
}
}
return access.antarchy$getTimeDilationRate();
}
return TimeDilationMath.NORMAL_RATE;
}
public static boolean isDilated(Entity entity) {
return getRate(entity) < TimeDilationMath.NORMAL_RATE;
}
public static double getVehicleRate(Entity entity) {
Entity vehicle = entity.getVehicle();
return vehicle == null ? TimeDilationMath.NORMAL_RATE : getRate(vehicle);
}
public static void enterRotationBypass() {
ROTATION_BYPASS_DEPTH.set(ROTATION_BYPASS_DEPTH.get() + 1);
}
public static void exitRotationBypass() {
int depth = ROTATION_BYPASS_DEPTH.get() - 1;
if (depth <= 0) {
ROTATION_BYPASS_DEPTH.remove();
} else {
ROTATION_BYPASS_DEPTH.set(depth);
}
}
public static boolean isRotationBypassed() {
return ROTATION_BYPASS_DEPTH.get() > 0;
}
public static void setInheritedRate(Entity entity, double rate) {
if (entity instanceof TimeDilationEntityAccess access) {
access.antarchy$setInheritedTimeDilationRate(rate);
}
}
public static void applySyncedRate(Entity entity, double rate) {
applySyncedRate(entity.getUUID(), rate);
if (entity instanceof TimeDilationEntityAccess access) {
access.antarchy$setTimeDilationRate(rate);
}
}
public static void applySyncedRate(UUID entityUuid, double rate) {
double clampedRate = TimeDilationMath.clampRate(rate, TimeDilationMath.ABSOLUTE_MAX_RATE);
SYNCED_CLIENT_RATES.put(entityUuid, clampedRate);
}
public static void clearRate(Entity entity) {
if (entity instanceof TimeDilationEntityAccess access) {
boolean wasDilated = Math.abs(access.antarchy$getTimeDilationRate() - TimeDilationMath.NORMAL_RATE) > 0.001D;
access.antarchy$setTimeDilationRate(TimeDilationMath.NORMAL_RATE);
if (wasDilated) {
syncEntityRate(entity, TimeDilationMath.NORMAL_RATE);
}
}
SYNCED_CLIENT_RATES.remove(entity.getUUID());
}
public static void clearSyncedClientRates() {
SYNCED_CLIENT_RATES.clear();
}
public static boolean hasSyncedClientRates() {
return !SYNCED_CLIENT_RATES.isEmpty();
}
public static void syncEntityRate(Entity entity, double rate) {
syncDispatcher.accept(entity, TimeDilationMath.clampRate(rate, TimeDilationMath.ABSOLUTE_MAX_RATE));
}
public static void addHorizontalOnlyMovement(Predicate<Entity> predicate) {
HORIZONTAL_ONLY_MOVEMENT.add(predicate);
}
public static boolean isHorizontalOnlyMovement(Entity entity) {
for (Predicate<Entity> predicate : HORIZONTAL_ONLY_MOVEMENT) {
if (predicate.test(entity)) {
return true;
}
}
return false;
}
public static double getRateCeiling(Entity entity) {
if (entity instanceof TimeDilationEntityAccess access) {
return access.antarchy$getTimeDilationCeiling();
}
return TimeDilationMath.MAX_RATE;
}
public static void setRateCeiling(Entity entity, double ceiling) {
if (entity instanceof TimeDilationEntityAccess access) {
access.antarchy$setTimeDilationCeiling(ceiling);
TimeDilationManager.trackPotentiallyAffected(entity);
}
}
public static void clearRateCeiling(Entity entity) {
setRateCeiling(entity, TimeDilationMath.MAX_RATE);
}
public static void setPersonalRate(Entity entity, String sourceId, double rate) {
if (entity instanceof TimeDilationEntityAccess access) {
double clampedRate = TimeDilationMath.clampRate(rate, TimeDilationMath.ABSOLUTE_MAX_RATE);
if (Math.abs(clampedRate - TimeDilationMath.NORMAL_RATE) < 0.001D) {
access.antarchy$getPersonalTimeDilationRates().remove(sourceId);
} else {
access.antarchy$getPersonalTimeDilationRates().put(sourceId, clampedRate);
}
TimeDilationManager.trackPotentiallyAffected(entity);
}
}
public static void clearPersonalRate(Entity entity, String sourceId) {
if (entity instanceof TimeDilationEntityAccess access
&& access.antarchy$getPersonalTimeDilationRates().remove(sourceId) != null) {
TimeDilationManager.trackPotentiallyAffected(entity);
}
}
public static double getPersonalRate(Entity entity, String sourceId) {
if (entity instanceof TimeDilationEntityAccess access) {
return access.antarchy$getPersonalTimeDilationRates().getOrDefault(sourceId, TimeDilationMath.NORMAL_RATE);
}
return TimeDilationMath.NORMAL_RATE;
}
public static boolean hasPersonalRates(Entity entity) {
return entity instanceof TimeDilationEntityAccess access && !access.antarchy$getPersonalTimeDilationRates().isEmpty();
}
public static TimeDilationFieldEntity createAttachedField(ServerLevel level, Entity owner, double radius, double rate, int durationTicks) {
TimeDilationFieldEntity field = TimeDilationFieldEntity.create(level, owner.position(), radius, rate, durationTicks, owner.getUUID(), true);
field.attachTo(owner);
level.addFreshEntity(field);
return field;
}
public static void syncFields(ServerPlayer player, List<TimeDilationFieldSnapshot> fields) {
fieldSyncDispatcher.accept(player, fields);
}
public static boolean consumeTick(Entity entity, String timerKey) {
return consumeTicks(entity, timerKey) > 0;
}
public static int consumeTicks(Entity entity, String timerKey) {
double rate = getRate(entity);
if (entity instanceof TimeDilationEntityAccess access) {
return access.antarchy$consumeTimeDilationTicks(timerKey, rate);
}
return 1;
}
public static int scaleCooldownTicks(Entity entity, int ticks) {
double rate = getRate(entity);
if (ticks <= 0 || Math.abs(rate - TimeDilationMath.NORMAL_RATE) < 0.001D) {
return ticks;
}
return (int) Math.ceil(ticks / Math.max(TimeDilationMath.MIN_RATE, rate));
}
}
@@ -1,101 +0,0 @@
package com.craisinlord.antarchy.content.time;
import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.arguments.DoubleArgumentType;
import com.mojang.brigadier.arguments.IntegerArgumentType;
import com.mojang.brigadier.context.CommandContext;
import java.util.ArrayList;
import java.util.List;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.Commands;
import net.minecraft.commands.arguments.coordinates.Vec3Argument;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.phys.Vec3;
public final class TimeDilationCommand {
private TimeDilationCommand() {
}
public static void register(CommandDispatcher<CommandSourceStack> dispatcher) {
dispatcher.register(
Commands.literal("timedilation")
.requires(source -> source.hasPermission(2))
.then(Commands.literal("create")
.then(Commands.argument("center", Vec3Argument.vec3())
.then(Commands.argument("radius", DoubleArgumentType.doubleArg(0.5D, 128.0D))
.then(Commands.argument("rate", DoubleArgumentType.doubleArg(TimeDilationMath.MIN_RATE, 1.0D))
.then(Commands.argument("durationTicks", IntegerArgumentType.integer(-1))
.executes(TimeDilationCommand::create))))))
.then(Commands.literal("create_here")
.then(Commands.argument("radius", DoubleArgumentType.doubleArg(0.5D, 128.0D))
.then(Commands.argument("rate", DoubleArgumentType.doubleArg(TimeDilationMath.MIN_RATE, 1.0D))
.then(Commands.argument("durationTicks", IntegerArgumentType.integer(-1))
.executes(TimeDilationCommand::createHere)))))
.then(Commands.literal("list").executes(TimeDilationCommand::list))
.then(Commands.literal("clear").executes(TimeDilationCommand::clear))
);
}
private static int create(CommandContext<CommandSourceStack> context) {
return createField(context, Vec3Argument.getVec3(context, "center"));
}
private static int createHere(CommandContext<CommandSourceStack> context) {
return createField(context, context.getSource().getPosition());
}
private static int createField(CommandContext<CommandSourceStack> context, Vec3 center) {
ServerLevel level = context.getSource().getLevel();
double radius = DoubleArgumentType.getDouble(context, "radius");
double rate = DoubleArgumentType.getDouble(context, "rate");
int durationTicks = IntegerArgumentType.getInteger(context, "durationTicks");
TimeDilationFieldEntity field = TimeDilationApi.createField(level, center, radius, rate, durationTicks);
context.getSource().sendSuccess(
() -> Component.literal("Created time dilation field " + field.getUUID() + " radius=" + radius
+ " rate=" + rate + " durationTicks=" + durationTicks),
true
);
return 1;
}
private static int list(CommandContext<CommandSourceStack> context) {
int count = 0;
for (ServerLevel level : context.getSource().getServer().getAllLevels()) {
for (Entity entity : level.getAllEntities()) {
if (!(entity instanceof TimeDilationFieldEntity field)) {
continue;
}
count++;
context.getSource().sendSuccess(
() -> Component.literal(field.getUUID() + " " + level.dimension().location() + " @ "
+ format(field.position()) + " radius=" + field.fieldRadius() + " rate=" + field.fieldRate()
+ " age=" + field.fieldAge()
+ (field.isInfinite() ? " duration=infinite" : " duration=" + field.fieldDurationTicks())),
false
);
}
}
return count;
}
private static int clear(CommandContext<CommandSourceStack> context) {
List<TimeDilationFieldEntity> fields = new ArrayList<>();
for (ServerLevel level : context.getSource().getServer().getAllLevels()) {
for (Entity entity : level.getAllEntities()) {
if (entity instanceof TimeDilationFieldEntity field) {
fields.add(field);
}
}
}
fields.forEach(Entity::discard);
int count = fields.size();
context.getSource().sendSuccess(() -> Component.literal("Cleared " + count + " time dilation field(s)"), true);
return count;
}
private static String format(Vec3 vec) {
return String.format(java.util.Locale.ROOT, "%.2f %.2f %.2f", vec.x, vec.y, vec.z);
}
}
@@ -1,31 +0,0 @@
package com.craisinlord.antarchy.content.time;
import java.util.Map;
public interface TimeDilationEntityAccess {
double antarchy$getTimeDilationCeiling();
void antarchy$setTimeDilationCeiling(double ceiling);
Map<String, Double> antarchy$getPersonalTimeDilationRates();
double antarchy$getTimeDilationRate();
void antarchy$setTimeDilationRate(double rate);
double antarchy$getInheritedTimeDilationRate();
void antarchy$setInheritedTimeDilationRate(double rate);
boolean antarchy$consumeTimeDilationTick(String timerKey, double rate);
int antarchy$consumeTimeDilationTicks(String timerKey, double rate);
boolean antarchy$isInTimeDilationMove();
void antarchy$setInTimeDilationMove(boolean inMove);
boolean antarchy$isApplyingExternalImpulse();
void antarchy$setApplyingExternalImpulse(boolean applying);
}
@@ -1,237 +0,0 @@
package com.craisinlord.antarchy.content.time;
import com.craisinlord.antarchy.content.AntarchyObjects;
import com.craisinlord.antarchy.content.AntarchyTags;
import com.craisinlord.antarchy.content.entity.royal.QueenEntity;
import com.craisinlord.antarchy.content.entity.royal.RoyalBlackHoleEntity;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.syncher.EntityDataAccessor;
import net.minecraft.network.syncher.EntityDataSerializers;
import net.minecraft.network.syncher.SynchedEntityData;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.item.ItemEntity;
import net.minecraft.world.entity.projectile.Projectile;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.Vec3;
import java.util.UUID;
public class TimeDilationFieldEntity extends Entity {
private static final EntityDataAccessor<Float> RADIUS =
SynchedEntityData.defineId(TimeDilationFieldEntity.class, EntityDataSerializers.FLOAT);
private static final EntityDataAccessor<Float> RATE =
SynchedEntityData.defineId(TimeDilationFieldEntity.class, EntityDataSerializers.FLOAT);
private static final String RADIUS_KEY = "Radius";
private static final String RATE_KEY = "Rate";
private static final String DURATION_KEY = "DurationTicks";
private static final String AGE_KEY = "Age";
private int durationTicks = -1;
private int age;
private UUID ownerId;
private UUID anchorId;
private int missingAnchorTicks;
private boolean visual = true;
private boolean chronosphere;
private boolean queenChronosphere;
public TimeDilationFieldEntity(EntityType<? extends TimeDilationFieldEntity> entityType, Level level) {
super(entityType, level);
this.noPhysics = true;
this.noCulling = true;
}
public static TimeDilationFieldEntity create(Level level, Vec3 center, double radius, double rate, int durationTicks) {
return create(level, center, radius, rate, durationTicks, null);
}
public static TimeDilationFieldEntity create(Level level, Vec3 center, double radius, double rate, int durationTicks, UUID ownerId) {
return create(level, center, radius, rate, durationTicks, ownerId, true);
}
public static TimeDilationFieldEntity create(Level level, Vec3 center, double radius, double rate, int durationTicks, UUID ownerId, boolean visual) {
TimeDilationFieldEntity field = new TimeDilationFieldEntity(AntarchyObjects.TIME_DILATION_FIELD.get(), level);
field.setPos(center.x, center.y, center.z);
field.setFieldRadius(radius);
field.setFieldRate(rate);
field.durationTicks = durationTicks;
field.ownerId = ownerId;
field.visual = visual;
return field;
}
public boolean isOwnedBy(Entity entity) {
return this.ownerId != null && entity != null && this.ownerId.equals(entity.getUUID());
}
public void attachTo(Entity anchor) {
this.anchorId = anchor == null ? null : anchor.getUUID();
this.missingAnchorTicks = 0;
}
public void configureChronosphere(Entity owner) {
this.ownerId = owner.getUUID();
this.attachTo(owner);
this.durationTicks = -1;
this.visual = false;
this.chronosphere = true;
}
public void configureQueenChronosphere(QueenEntity owner) {
this.ownerId = owner.getUUID();
this.attachTo(owner);
this.queenChronosphere = true;
}
public boolean isChronosphere() {
return this.chronosphere;
}
public boolean affects(Entity entity) {
if (!this.queenChronosphere && entity instanceof RoyalBlackHoleEntity || this.isOwnedBy(entity)
|| this.anchorId != null && this.anchorId.equals(entity.getUUID())) {
return false;
}
if (this.ownerId != null && this.level() instanceof ServerLevel level
&& level.getEntity(this.ownerId) instanceof QueenEntity
&& entity instanceof LivingEntity living
&& !com.craisinlord.antarchy.content.effect.RoyalEffectEligibility.canApplyDilated(living)) {
return false;
}
if (this.chronosphere && !(entity instanceof LivingEntity || entity instanceof Projectile || entity instanceof ItemEntity)) {
return false;
}
if (!this.queenChronosphere && this.ownerId != null && this.level() instanceof ServerLevel level
&& level.getEntity(this.ownerId) instanceof QueenEntity) {
return !entity.getType().is(AntarchyTags.Entities.QUEEN_DOES_NOT_ATTACK);
}
return true;
}
public boolean isVisual() {
return this.visual;
}
@Override
protected void defineSynchedData(SynchedEntityData.Builder builder) {
builder.define(RADIUS, 8.0F);
builder.define(RATE, (float) TimeDilationMath.NORMAL_RATE);
}
@Override
public void tick() {
super.tick();
if (this.level().isClientSide) {
return;
}
TimeDilationManager.registerActiveField(this);
if (this.anchorId != null && this.level() instanceof ServerLevel level) {
Entity anchor = level.getEntity(this.anchorId);
if (anchor != null && anchor.isAlive()) {
Vec3 center = anchor instanceof RoyalBlackHoleEntity blackHole
? blackHole.effectCenter()
: anchor.position();
this.setPos(center.x, center.y, center.z);
this.missingAnchorTicks = 0;
if (this.chronosphere && this.age % 20 == 0) {
if (!(anchor instanceof net.minecraft.server.level.ServerPlayer player)) {
this.discard();
return;
}
int levelValue = com.craisinlord.antarchy.content.enchantment.AntarchyEnchantments.chronosphereLevel(player);
if (levelValue <= 0) {
this.discard();
return;
}
this.setFieldRadius(ChronosphereManager.radius(levelValue));
}
} else if (++this.missingAnchorTicks > 20) {
this.discard();
return;
}
}
this.age++;
if (this.durationTicks >= 0 && this.age >= this.durationTicks) {
this.discard();
}
}
public void setFieldRadius(double radius) {
this.entityData.set(RADIUS, (float) Math.max(0.5D, radius));
}
public double fieldRadius() {
return this.entityData.get(RADIUS);
}
public double influenceRadius() {
return this.chronosphere ? this.fieldRadius() * 2.0D : this.fieldRadius();
}
public double fieldRadiusSqr() {
double radius = this.fieldRadius();
return radius * radius;
}
public void setFieldRate(double rate) {
this.entityData.set(RATE, (float) TimeDilationMath.clampRate(rate));
}
public double fieldRate() {
return TimeDilationMath.clampRate(this.entityData.get(RATE));
}
public double effectiveFieldRate() {
return TimeDilationMath.effectiveFieldRate(this.fieldRate(), this.age, this.durationTicks);
}
public int fieldDurationTicks() {
return this.durationTicks;
}
public int fieldAge() {
return this.age;
}
public boolean isInfinite() {
return this.durationTicks < 0;
}
@Override
public boolean isIgnoringBlockTriggers() {
return true;
}
@Override
protected void readAdditionalSaveData(CompoundTag tag) {
this.setFieldRadius(tag.getDouble(RADIUS_KEY));
this.setFieldRate(tag.getDouble(RATE_KEY));
this.durationTicks = tag.contains(DURATION_KEY) ? tag.getInt(DURATION_KEY) : -1;
this.age = tag.getInt(AGE_KEY);
this.ownerId = tag.hasUUID("OwnerUuid") ? tag.getUUID("OwnerUuid") : null;
this.anchorId = tag.hasUUID("AnchorUuid") ? tag.getUUID("AnchorUuid") : null;
this.visual = !tag.contains("Visual") || tag.getBoolean("Visual");
this.chronosphere = tag.getBoolean("Chronosphere");
this.queenChronosphere = tag.getBoolean("QueenChronosphere");
}
@Override
protected void addAdditionalSaveData(CompoundTag tag) {
tag.putDouble(RADIUS_KEY, this.fieldRadius());
tag.putDouble(RATE_KEY, this.fieldRate());
tag.putInt(DURATION_KEY, this.durationTicks);
tag.putInt(AGE_KEY, this.age);
if (this.ownerId != null) {
tag.putUUID("OwnerUuid", this.ownerId);
}
if (this.anchorId != null) {
tag.putUUID("AnchorUuid", this.anchorId);
}
tag.putBoolean("Visual", this.visual);
tag.putBoolean("Chronosphere", this.chronosphere);
tag.putBoolean("QueenChronosphere", this.queenChronosphere);
}
}
@@ -1,65 +0,0 @@
package com.craisinlord.antarchy.content.time;
import java.util.List;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.phys.Vec3;
public final class TimeDilationFieldSampler {
private TimeDilationFieldSampler() {
}
public static double sample(List<TimeDilationFieldEntity> fields, Vec3 position) {
return sample(fields, position.x, position.y, position.z);
}
public static double sample(List<TimeDilationFieldEntity> fields, double x, double y, double z) {
double combinedRate = TimeDilationMath.NORMAL_RATE;
for (TimeDilationFieldEntity field : fields) {
Vec3 center = field.position();
double dx = center.x - x;
double dy = center.y - y;
double dz = center.z - z;
double distanceSqr = dx * dx + dy * dy + dz * dz;
if (distanceSqr >= field.fieldRadiusSqr()) {
continue;
}
double falloff = TimeDilationMath.gaussianFalloff(Math.sqrt(distanceSqr), field.fieldRadius());
combinedRate *= TimeDilationMath.localFieldRate(field.effectiveFieldRate(), falloff);
}
return TimeDilationMath.clampRate(combinedRate);
}
public static double sample(List<TimeDilationFieldEntity> fields, Entity entity) {
double combinedRate = TimeDilationMath.NORMAL_RATE;
for (TimeDilationFieldEntity field : fields) {
if (!field.affects(entity)) {
continue;
}
Vec3 center = field.position();
double dx = center.x - entity.getX();
double dy = center.y - entity.getY();
double dz = center.z - entity.getZ();
double distanceSqr = dx * dx + dy * dy + dz * dz;
double distance = Math.sqrt(distanceSqr);
double radius = field.fieldRadius();
double falloff;
if (field.isChronosphere()) {
if (distance >= radius * 2.0D) {
continue;
}
falloff = distance <= radius
? 1.0D
: TimeDilationMath.gaussianFalloff(distance - radius, radius);
} else {
if (distanceSqr >= field.fieldRadiusSqr()) {
continue;
}
falloff = TimeDilationMath.gaussianFalloff(distance, radius);
}
if (falloff > 0.0D) {
combinedRate *= TimeDilationMath.localFieldRate(field.effectiveFieldRate(), falloff);
}
}
return TimeDilationMath.clampRate(combinedRate);
}
}
@@ -1,19 +0,0 @@
package com.craisinlord.antarchy.content.time;
public record TimeDilationFieldSnapshot(
double x,
double y,
double z,
double radius,
double rate,
int age,
int durationTicks
) {
public double radiusSquared() {
return this.radius * this.radius;
}
public double effectiveRate() {
return TimeDilationMath.effectiveFieldRate(this.rate, this.age, this.durationTicks);
}
}
@@ -1,22 +0,0 @@
package com.craisinlord.antarchy.content.time;
public final class TimeDilationFieldSnapshotSampler {
private TimeDilationFieldSnapshotSampler() {
}
public static double sample(Iterable<TimeDilationFieldSnapshot> fields, double x, double y, double z) {
double combinedRate = TimeDilationMath.NORMAL_RATE;
for (TimeDilationFieldSnapshot field : fields) {
double dx = field.x() - x;
double dy = field.y() - y;
double dz = field.z() - z;
double distanceSqr = dx * dx + dy * dy + dz * dz;
if (distanceSqr >= field.radiusSquared()) {
continue;
}
double falloff = TimeDilationMath.gaussianFalloff(Math.sqrt(distanceSqr), field.radius());
combinedRate *= TimeDilationMath.localFieldRate(field.effectiveRate(), falloff);
}
return TimeDilationMath.clampRate(combinedRate);
}
}
@@ -1,257 +0,0 @@
package com.craisinlord.antarchy.content.time;
import java.util.ArrayList;
import java.util.Collections;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.projectile.Projectile;
import net.minecraft.world.entity.player.Player;
import com.craisinlord.antarchy.content.item.TemporalTunerItem;
import com.craisinlord.antarchy.content.effect.DilatedMobEffect;
import com.craisinlord.antarchy.content.effect.ContractedMobEffect;
import com.craisinlord.antarchy.content.effect.RoyalEffectEligibility;
import com.craisinlord.antarchy.content.effect.RoyalEffectHooks;
public final class TimeDilationManager {
private static final Map<ServerLevel, TrackingState> TRACKING = new WeakHashMap<>();
private static final Map<ServerLevel, Set<TimeDilationFieldEntity>> ACTIVE_FIELDS = new WeakHashMap<>();
private TimeDilationManager() {
}
public static void tickServer(MinecraftServer server) {
for (ServerLevel level : server.getAllLevels()) {
Set<TimeDilationFieldEntity> activeFields = ACTIVE_FIELDS.get(level);
if (activeFields != null) {
activeFields.removeIf(field -> !field.isAlive() || field.isRemoved());
if (activeFields.isEmpty()) {
ACTIVE_FIELDS.remove(level);
activeFields = null;
}
}
TrackingState tracking = TRACKING.get(level);
for (ServerPlayer player : level.players()) {
trackPotentiallyAffected(player);
}
if (activeFields == null && (tracking == null || tracking.entities.isEmpty())) {
continue;
}
List<TimeDilationFieldEntity> fields = activeFields == null ? List.of() : List.copyOf(activeFields);
if (tracking == null) {
tracking = TRACKING.computeIfAbsent(level, ignored -> new TrackingState());
}
discoverFieldEntities(level, fields, tracking);
syncFieldSnapshots(level, fields, tracking);
updateEntities(level, fields, tracking);
if (fields.isEmpty() && tracking.entities.isEmpty()) {
TRACKING.remove(level);
}
if (!fields.isEmpty()) {
TimeDilationParticles.spawnFieldBorders(level, fields.stream().filter(TimeDilationFieldEntity::isVisual).toList());
}
}
}
public static void registerActiveField(TimeDilationFieldEntity field) {
if (field.level() instanceof ServerLevel level) {
ACTIVE_FIELDS.computeIfAbsent(level, ignored -> Collections.newSetFromMap(new IdentityHashMap<>())).add(field);
}
}
public static void trackPotentiallyAffected(Entity entity) {
if (!(entity.level() instanceof ServerLevel level) || entity instanceof TimeDilationFieldEntity) {
return;
}
boolean hasTuner = entity instanceof Player player && TemporalTunerItem.isAvailable(player);
boolean hasEffect = entity instanceof net.minecraft.world.entity.LivingEntity living
&& ((RoyalEffectHooks.dilatedHolder() != null && living.hasEffect(RoyalEffectHooks.dilatedHolder()))
|| (RoyalEffectHooks.contractedHolder() != null && living.hasEffect(RoyalEffectHooks.contractedHolder())));
if (entity instanceof TimeDilationEntityAccess access
&& Math.abs(access.antarchy$getTimeDilationRate() - TimeDilationMath.NORMAL_RATE) < 0.001D
&& Math.abs(access.antarchy$getInheritedTimeDilationRate() - TimeDilationMath.NORMAL_RATE) < 0.001D
&& Math.abs(TimeDilationApi.getVehicleRate(entity) - TimeDilationMath.NORMAL_RATE) < 0.001D
&& !TimeDilationApi.hasPersonalRates(entity)
&& !hasTuner && !hasEffect) {
return;
}
TRACKING.computeIfAbsent(level, ignored -> new TrackingState()).entities.add(entity);
}
public static void resyncPersistentEffects(ServerPlayer player) {
if (!(player instanceof TimeDilationEntityAccess access)) {
return;
}
double targetRate = TimeDilationMath.NORMAL_RATE;
if (RoyalEffectHooks.dilatedHolder() != null) {
var effect = player.getEffect(RoyalEffectHooks.dilatedHolder());
if (effect != null) {
targetRate = Math.min(targetRate, DilatedMobEffect.rateForAmplifier(effect.getAmplifier()));
}
}
if (RoyalEffectHooks.contractedHolder() != null) {
var effect = player.getEffect(RoyalEffectHooks.contractedHolder());
if (effect != null && targetRate >= TimeDilationMath.NORMAL_RATE) {
targetRate = Math.max(targetRate, ContractedMobEffect.rateForAmplifier(effect.getAmplifier()));
}
}
if (Math.abs(targetRate - TimeDilationMath.NORMAL_RATE) >= 0.001D) {
access.antarchy$setTimeDilationRate(targetRate);
TimeDilationApi.syncEntityRate(player, targetRate);
}
trackPotentiallyAffected(player);
}
private static void syncFieldSnapshots(ServerLevel level, List<TimeDilationFieldEntity> fields, TrackingState tracking) {
List<TimeDilationFieldSnapshot> snapshots = fields.stream()
.filter(field -> !field.isChronosphere())
.map(field -> new TimeDilationFieldSnapshot(
field.getX(), field.getY(), field.getZ(), field.fieldRadius(), field.fieldRate(),
field.fieldAge(), field.fieldDurationTicks()))
.limit(128)
.toList();
if (level.getGameTime() % 4L != 0L && snapshots.size() == tracking.lastSnapshots.size()) {
return;
}
tracking.lastSnapshots = snapshots;
for (ServerPlayer player : level.players()) {
TimeDilationApi.syncFields(player, snapshots);
}
}
private static void discoverFieldEntities(ServerLevel level, List<TimeDilationFieldEntity> fields, TrackingState tracking) {
for (TimeDilationFieldEntity field : fields) {
double radius = field.influenceRadius();
var center = field.position();
var area = new net.minecraft.world.phys.AABB(
center.x - radius, center.y - radius, center.z - radius,
center.x + radius, center.y + radius, center.z + radius);
tracking.entities.addAll(level.getEntities(field, area,
entity -> entity.isAlive() && !(entity instanceof TimeDilationFieldEntity)
&& entity instanceof TimeDilationEntityAccess));
}
}
private static void updateEntities(ServerLevel level, List<TimeDilationFieldEntity> fields, TrackingState tracking) {
var iterator = tracking.entities.iterator();
while (iterator.hasNext()) {
Entity entity = iterator.next();
if (!(entity instanceof TimeDilationEntityAccess access) || entity instanceof TimeDilationFieldEntity) {
continue;
}
if (entity.getType().is(com.craisinlord.antarchy.content.AntarchyTags.Entities.TIME_DILATION_IMMUNE)) {
if (RoyalEffectHooks.dilatedHolder() != null && entity instanceof net.minecraft.world.entity.LivingEntity living) {
living.removeEffect(RoyalEffectHooks.dilatedHolder());
}
TimeDilationApi.clearRate(entity);
iterator.remove();
continue;
}
double previousRate = access.antarchy$getTimeDilationRate();
double fieldRate = TimeDilationFieldSampler.sample(fields, entity);
double effectRate = TimeDilationMath.NORMAL_RATE;
if (RoyalEffectHooks.dilatedHolder() != null
&& entity instanceof net.minecraft.world.entity.LivingEntity living) {
var effect = living.getEffect(RoyalEffectHooks.dilatedHolder());
if (effect != null) {
effectRate = DilatedMobEffect.rateForAmplifier(effect.getAmplifier());
}
}
if (RoyalEffectHooks.contractedHolder() != null
&& entity instanceof net.minecraft.world.entity.LivingEntity living) {
var effect = living.getEffect(RoyalEffectHooks.contractedHolder());
if (effect != null) {
effectRate = Math.max(effectRate, ContractedMobEffect.rateForAmplifier(effect.getAmplifier()));
}
}
double tunerRate = TimeDilationMath.NORMAL_RATE;
if (entity instanceof Player player && player instanceof TemporalTunerAccess tunerAccess) {
if (TemporalTunerItem.isAvailable(player)) {
tunerRate = TemporalTunerItem.getRate(player);
} else {
tunerAccess.antarchy$setTemporalTunerRate(TimeDilationMath.NORMAL_RATE);
TemporalTunerItem.syncTooltipRate(player, TimeDilationMath.NORMAL_RATE);
}
}
double inheritedRate = access.antarchy$getInheritedTimeDilationRate();
double vehicleRate = TimeDilationApi.getVehicleRate(entity);
if (vehicleRate < TimeDilationMath.NORMAL_RATE) {
inheritedRate = Math.min(inheritedRate, vehicleRate);
} else {
inheritedRate = Math.max(inheritedRate, vehicleRate);
}
if (entity instanceof Projectile projectile && projectile.getOwner() != null) {
double ownerRate = TimeDilationApi.getRate(projectile.getOwner());
inheritedRate = ownerRate < TimeDilationMath.NORMAL_RATE
? Math.min(inheritedRate, ownerRate)
: Math.max(inheritedRate, ownerRate);
}
double slowRate = TimeDilationMath.NORMAL_RATE;
double fastRate = TimeDilationMath.NORMAL_RATE;
if (fieldRate < TimeDilationMath.NORMAL_RATE) {
slowRate = Math.min(slowRate, fieldRate);
} else {
fastRate = Math.max(fastRate, fieldRate);
}
if (effectRate < TimeDilationMath.NORMAL_RATE) {
slowRate = Math.min(slowRate, effectRate);
} else {
fastRate = Math.max(fastRate, effectRate);
}
if (tunerRate < TimeDilationMath.NORMAL_RATE) {
slowRate = Math.min(slowRate, tunerRate);
} else {
fastRate = Math.max(fastRate, tunerRate);
}
if (inheritedRate < TimeDilationMath.NORMAL_RATE) {
slowRate = Math.min(slowRate, inheritedRate);
} else {
fastRate = Math.max(fastRate, inheritedRate);
}
for (double personalRate : access.antarchy$getPersonalTimeDilationRates().values()) {
if (personalRate < TimeDilationMath.NORMAL_RATE) {
slowRate = Math.min(slowRate, personalRate);
} else {
fastRate = Math.max(fastRate, personalRate);
}
}
double ceiling = access.antarchy$getTimeDilationCeiling();
double targetRate = TimeDilationMath.clampRate(slowRate < TimeDilationMath.NORMAL_RATE ? slowRate : fastRate, ceiling);
double rate = TimeDilationMath.transitionRate(previousRate, targetRate, ceiling);
if (Math.abs(targetRate - TimeDilationMath.NORMAL_RATE) < 0.001D
&& Math.abs(rate - TimeDilationMath.NORMAL_RATE) < 0.001D) {
TimeDilationApi.clearRate(entity);
} else {
access.antarchy$setTimeDilationRate(rate);
}
if (Math.abs(previousRate - rate) > 0.001D) {
TimeDilationApi.syncEntityRate(entity, rate);
}
boolean hasDilationEffect = entity instanceof net.minecraft.world.entity.LivingEntity living
&& ((RoyalEffectHooks.dilatedHolder() != null && living.hasEffect(RoyalEffectHooks.dilatedHolder()))
|| (RoyalEffectHooks.contractedHolder() != null && living.hasEffect(RoyalEffectHooks.contractedHolder())));
boolean hasInheritedRate = Math.abs(access.antarchy$getInheritedTimeDilationRate() - TimeDilationMath.NORMAL_RATE) >= 0.001D;
boolean hasActiveRate = Math.abs(access.antarchy$getTimeDilationRate() - TimeDilationMath.NORMAL_RATE) >= 0.001D;
if (!hasDilationEffect && !hasInheritedRate && !hasActiveRate
&& Math.abs(targetRate - TimeDilationMath.NORMAL_RATE) < 0.001D) {
iterator.remove();
}
}
tracking.entities.removeIf(entity -> !entity.isAlive() || entity.isRemoved());
}
private static final class TrackingState {
private final Set<Entity> entities = Collections.newSetFromMap(new IdentityHashMap<>());
private List<TimeDilationFieldSnapshot> lastSnapshots = List.of();
}
}
@@ -1,86 +0,0 @@
package com.craisinlord.antarchy.content.time;
public final class TimeDilationMath {
public static final double MIN_RATE = 0.05D;
public static final double NORMAL_RATE = 1.0D;
public static final double MAX_RATE = 3.0D;
public static final double ABSOLUTE_MAX_RATE = 20.0D;
public static final int FIELD_TRANSITION_TICKS = 20;
public static final int EFFECT_TRANSITION_TICKS = 20;
private static final double GAUSSIAN_EDGE_EXPONENT = -4.5D;
private TimeDilationMath() {
}
public static double clampRate(double rate) {
if (Double.isNaN(rate) || Double.isInfinite(rate)) {
return NORMAL_RATE;
}
return Math.max(MIN_RATE, Math.min(MAX_RATE, rate));
}
public static double clampCeiling(double ceiling) {
if (Double.isNaN(ceiling) || Double.isInfinite(ceiling)) {
return MAX_RATE;
}
return Math.max(NORMAL_RATE, Math.min(ABSOLUTE_MAX_RATE, ceiling));
}
public static double clampRate(double rate, double ceiling) {
if (Double.isNaN(rate) || Double.isInfinite(rate)) {
return NORMAL_RATE;
}
return Math.max(MIN_RATE, Math.min(clampCeiling(ceiling), rate));
}
public static double transitionRate(double currentRate, double targetRate) {
return transitionRate(currentRate, targetRate, MAX_RATE);
}
public static double transitionRate(double currentRate, double targetRate, double ceiling) {
double maximum = clampCeiling(ceiling);
double current = clampRate(currentRate, maximum);
double target = clampRate(targetRate, maximum);
double maximumStep = (maximum - MIN_RATE) / EFFECT_TRANSITION_TICKS;
double delta = Math.max(-maximumStep, Math.min(maximumStep, target - current));
return clampRate(current + delta, maximum);
}
public static double gaussianFalloff(double distance, double radius) {
if (distance < 0.0D || distance >= radius || radius <= 0.0D) {
return 0.0D;
}
double normalized = distance / radius;
return Math.exp(GAUSSIAN_EDGE_EXPONENT * normalized * normalized);
}
public static double localFieldRate(double fieldRate, double falloff) {
double clampedRate = clampRate(fieldRate);
double clampedFalloff = Math.max(0.0D, Math.min(1.0D, falloff));
if (clampedRate <= 0.0D) {
return 0.0D;
}
return Math.pow(clampedRate, clampedFalloff);
}
public static double smoothStep(double value) {
double clamped = Math.max(0.0D, Math.min(1.0D, value));
return clamped * clamped * (3.0D - 2.0D * clamped);
}
public static double fieldStrength(int age, int durationTicks) {
double fadeIn = smoothStep((double) age / FIELD_TRANSITION_TICKS);
if (durationTicks < 0) {
return fadeIn;
}
double remaining = durationTicks - age;
double fadeOut = smoothStep(remaining / FIELD_TRANSITION_TICKS);
return Math.min(fadeIn, fadeOut);
}
public static double effectiveFieldRate(double fieldRate, int age, int durationTicks) {
double strength = fieldStrength(age, durationTicks);
return NORMAL_RATE + (clampRate(fieldRate) - NORMAL_RATE) * strength;
}
}
@@ -1,49 +0,0 @@
package com.craisinlord.antarchy.content.time;
import java.util.List;
import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.util.Mth;
import net.minecraft.world.phys.Vec3;
public final class TimeDilationParticles {
private static final int PARTICLE_INTERVAL_TICKS = 4;
private static final int MAX_RING_POINTS = 96;
private TimeDilationParticles() {
}
public static void spawnFieldBorders(ServerLevel level, List<TimeDilationFieldEntity> fields) {
if (level.getGameTime() % PARTICLE_INTERVAL_TICKS != 0L) {
return;
}
for (TimeDilationFieldEntity field : fields) {
spawnBorder(level, field.position(), field.fieldRadius());
}
}
private static void spawnBorder(ServerLevel level, Vec3 center, double radius) {
int points = Mth.clamp((int) Math.ceil(radius * 10.0D), 24, MAX_RING_POINTS);
double phase = (level.getGameTime() % 80L) * 0.07853981633974483D;
for (int i = 0; i < points; i++) {
double angle = phase + (Math.PI * 2.0D * i / points);
double x = center.x + Math.cos(angle) * radius;
double z = center.z + Math.sin(angle) * radius;
spawnParticle(level, x, center.y, z);
if (i % 4 == 0) {
spawnParticle(level, x, center.y + radius * 0.5D, z);
spawnParticle(level, x, center.y - radius * 0.5D, z);
}
if (i % 8 == 0) {
spawnParticle(level, center.x + Math.cos(angle) * radius * 0.7D, center.y + Math.sin(angle) * radius, center.z);
spawnParticle(level, center.x, center.y + Math.sin(angle) * radius, center.z + Math.cos(angle) * radius * 0.7D);
}
}
}
private static void spawnParticle(ServerLevel level, double x, double y, double z) {
level.sendParticles(ParticleTypes.REVERSE_PORTAL, x, y, z, 1, 0.02D, 0.02D, 0.02D, 0.0D);
}
}
@@ -1,17 +0,0 @@
package com.craisinlord.antarchy.mixins;
import net.minecraft.world.entity.projectile.AbstractArrow;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
@Mixin(AbstractArrow.class)
public interface AbstractArrowAccessor {
@Accessor("inGround")
void antarchy$setInGround(boolean inGround);
@Accessor("inGroundTime")
void antarchy$setInGroundTime(int inGroundTime);
@Accessor("shakeTime")
void antarchy$setShakeTime(int shakeTime);
}
@@ -1,126 +0,0 @@
package com.craisinlord.antarchy.mixins;
import com.craisinlord.antarchy.config.AntarchySettings;
import com.craisinlord.antarchy.content.block.DreamSandBlock;
import com.craisinlord.anthology.gravity.GravityApi;
import com.craisinlord.anthology.gravity.GravityDirection;
import com.craisinlord.antarchy.content.movement.DreamSandLowGravityAccess;
import net.minecraft.core.BlockPos;
import net.minecraft.util.Mth;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(LivingEntity.class)
public abstract class DreamSandLowGravityMixin implements DreamSandLowGravityAccess {
@Unique
private boolean antarchy$dreamSandLowGravityActive;
@Unique
private int antarchy$dreamSandLowGravityTicksRemaining;
@Unique
private int antarchy$dreamSandLandingGraceTicks;
@Override
public boolean antarchy$isDreamSandLowGravityActive() {
return this.antarchy$dreamSandLowGravityActive;
}
@Override
public void antarchy$setDreamSandLowGravityActive(boolean active) {
this.antarchy$dreamSandLowGravityActive = active;
}
@Override
public int antarchy$getDreamSandLowGravityTicksRemaining() {
return this.antarchy$dreamSandLowGravityTicksRemaining;
}
@Override
public void antarchy$setDreamSandLowGravityTicksRemaining(int ticks) {
this.antarchy$dreamSandLowGravityTicksRemaining = Math.max(0, ticks);
}
@Override
public int antarchy$getDreamSandLandingGraceTicks() {
return this.antarchy$dreamSandLandingGraceTicks;
}
@Override
public void antarchy$setDreamSandLandingGraceTicks(int ticks) {
this.antarchy$dreamSandLandingGraceTicks = Math.max(0, ticks);
}
@Inject(method = "tick", at = @At("TAIL"))
private void antarchy$updateDreamSandLowGravity(CallbackInfo ci) {
LivingEntity self = (LivingEntity) (Object) this;
if (!AntarchySettings.dreamSandEnabled()) {
this.antarchy$clearDreamSandLowGravity();
return;
}
boolean supportedByDreamSand = this.antarchy$isSupportedByDreamSand(self);
int durationTicks = Math.max(1, Mth.ceil(AntarchySettings.dreamSandEffectDurationSeconds() * 20.0D));
if (supportedByDreamSand) {
this.antarchy$dreamSandLowGravityActive = true;
this.antarchy$dreamSandLowGravityTicksRemaining = durationTicks;
this.antarchy$dreamSandLandingGraceTicks = 5;
} else if (this.antarchy$dreamSandLowGravityTicksRemaining > 0) {
this.antarchy$dreamSandLowGravityTicksRemaining--;
this.antarchy$dreamSandLowGravityActive = true;
} else {
this.antarchy$dreamSandLowGravityActive = false;
}
if (this.antarchy$dreamSandLandingGraceTicks > 0 && !supportedByDreamSand) {
this.antarchy$dreamSandLandingGraceTicks--;
}
if (!this.antarchy$dreamSandLowGravityActive || self.onGround() || self.onClimbable() || self.isInWaterOrBubble() || self.isInLava()) {
return;
}
Vec3 motion = self.getDeltaMovement();
boolean movingWithGravity = motion.y < 0.0D;
if (!movingWithGravity) {
return;
}
double gravityMultiplier = Math.max(0.0D, Math.min(1.0D, AntarchySettings.dreamSandGravityMultiplier()));
self.setDeltaMovement(motion.x, motion.y * gravityMultiplier, motion.z);
self.fallDistance = 0.0F;
this.antarchy$dreamSandLandingGraceTicks = 5;
}
@Inject(method = "jumpFromGround", at = @At("TAIL"))
private void antarchy$boostDreamSandJump(CallbackInfo ci) {
if (!this.antarchy$dreamSandLowGravityActive) {
return;
}
LivingEntity self = (LivingEntity) (Object) this;
Vec3 motion = self.getDeltaMovement();
boolean movingAwayFromGravity = motion.y > 0.0D;
if (!movingAwayFromGravity) {
return;
}
self.setDeltaMovement(motion.x, motion.y * AntarchySettings.dreamSandJumpVelocityMultiplier(), motion.z);
self.fallDistance = 0.0F;
this.antarchy$dreamSandLandingGraceTicks = 5;
}
@Unique
private boolean antarchy$isSupportedByDreamSand(LivingEntity entity) {
GravityDirection gravityDirection = GravityApi.getGravityDirection(entity);
AABB box = entity.getBoundingBox();
BlockPos supportPos = gravityDirection.isInverted()
? BlockPos.containing(entity.getX(), box.maxY + 0.05D, entity.getZ())
: BlockPos.containing(entity.getX(), box.minY - 0.05D, entity.getZ());
return entity.level().getBlockState(supportPos).getBlock() instanceof DreamSandBlock;
}
}
Loaded 100 of 244 files, more files were not shown because too many files have changed in this diff. Show more