anthology split
This commit is contained in:
1 parent
bba2a56671
commit
f7e7bebac3
244 files changed
+451
-11594
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;
|
||||
}
|
||||
|
||||
+199
@@ -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"));
|
||||
|
||||
+2
-1
@@ -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) {
|
||||
|
||||
-92
@@ -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();
|
||||
}
|
||||
}
|
||||
-171
@@ -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();
|
||||
}
|
||||
}
|
||||
-136
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
-90
@@ -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);
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
-120
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-152
@@ -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
|
||||
) {
|
||||
}
|
||||
}
|
||||
-67
@@ -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;
|
||||
}
|
||||
}
|
||||
-26
@@ -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");
|
||||
}
|
||||
}
|
||||
-61
@@ -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) {
|
||||
}
|
||||
}
|
||||
}
|
||||
+7
-7
@@ -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();
|
||||
|
||||
+3
-100
@@ -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);
|
||||
}
|
||||
}
|
||||
-186
@@ -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);
|
||||
}
|
||||
}
|
||||
-242
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
-165
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
-129
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
-755
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
-92
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
-56
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
-5
@@ -1,5 +0,0 @@
|
||||
package com.craisinlord.antarchy.content.client.renderer;
|
||||
|
||||
public interface PortalSectionGraphMarker {
|
||||
void antarchy$markPortalGraph();
|
||||
}
|
||||
-170
@@ -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) {
|
||||
}
|
||||
}
|
||||
-33
@@ -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);
|
||||
}
|
||||
-26
@@ -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));
|
||||
}
|
||||
}
|
||||
+4
-3
@@ -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();
|
||||
}
|
||||
|
||||
+4
-3
@@ -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();
|
||||
}
|
||||
|
||||
-26
@@ -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
-1
@@ -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
-1
@@ -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;
|
||||
|
||||
-25
@@ -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");
|
||||
}
|
||||
}
|
||||
-134
@@ -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++;
|
||||
}
|
||||
}
|
||||
}
|
||||
-208
@@ -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) {
|
||||
}
|
||||
}
|
||||
}
|
||||
-28
@@ -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;
|
||||
}
|
||||
}
|
||||
-28
@@ -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;
|
||||
}
|
||||
}
|
||||
+3
-6
@@ -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);
|
||||
|
||||
+3
-3
@@ -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
|
||||
);
|
||||
}
|
||||
|
||||
+2
-2
@@ -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;
|
||||
|
||||
|
||||
+1
-1
@@ -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;
|
||||
|
||||
+5
-5
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -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();
|
||||
|
||||
+1
-1
@@ -35,7 +35,7 @@ public class LucidEyeProjectileEntity extends ThrowableItemProjectile {
|
||||
|
||||
@Override
|
||||
protected double getDefaultGravity() {
|
||||
return 0.03D;
|
||||
return -0.03D;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
-40
@@ -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);
|
||||
}
|
||||
}
|
||||
-119
@@ -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);
|
||||
}
|
||||
}
|
||||
-24
@@ -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()
|
||||
);
|
||||
}
|
||||
}
|
||||
-5
@@ -1,5 +0,0 @@
|
||||
package com.craisinlord.antarchy.content.entity.multipart;
|
||||
|
||||
public interface MultipartPartAccess {
|
||||
void antarchy$syncFromParent();
|
||||
}
|
||||
-13
@@ -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
|
||||
) {
|
||||
}
|
||||
-28
@@ -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;
|
||||
}
|
||||
}
|
||||
-28
@@ -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;
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -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
|
||||
);
|
||||
}
|
||||
|
||||
+2
-2
@@ -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();
|
||||
|
||||
+8
-8
@@ -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
|
||||
|
||||
+9
-9
@@ -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" -> {
|
||||
|
||||
+9
-3
@@ -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;
|
||||
|
||||
+30
-19
@@ -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);
|
||||
}
|
||||
|
||||
+2
-2
@@ -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();
|
||||
|
||||
+9
-9
@@ -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) {
|
||||
|
||||
-385
@@ -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;
|
||||
};
|
||||
}
|
||||
}
|
||||
-80
@@ -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);
|
||||
}
|
||||
}
|
||||
-7
@@ -1,7 +0,0 @@
|
||||
package com.craisinlord.antarchy.content.entity.royal.beam;
|
||||
|
||||
public enum RoyalBeamTerrainMode {
|
||||
NONE,
|
||||
DESTROY,
|
||||
BUILD_ICE
|
||||
}
|
||||
+2
-2
@@ -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) {
|
||||
|
||||
+2
-2
@@ -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
-1
@@ -1,4 +1,4 @@
|
||||
package com.craisinlord.antarchy.content.time;
|
||||
package com.craisinlord.antarchy.content.item;
|
||||
|
||||
public interface TemporalTunerAccess {
|
||||
double antarchy$getTemporalTunerRate();
|
||||
-1
@@ -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;
|
||||
|
||||
-94
@@ -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();
|
||||
}
|
||||
}
|
||||
-21
@@ -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);
|
||||
}
|
||||
}
|
||||
-33
@@ -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;
|
||||
}
|
||||
}
|
||||
-33
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-43
@@ -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;
|
||||
}
|
||||
}
|
||||
-46
@@ -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;
|
||||
}
|
||||
}
|
||||
-33
@@ -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;
|
||||
}
|
||||
}
|
||||
-116
@@ -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();
|
||||
}
|
||||
}
|
||||
-63
@@ -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;
|
||||
}
|
||||
}
|
||||
-139
@@ -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)
|
||||
};
|
||||
}
|
||||
}
|
||||
+104
-773
File diff suppressed because it is too large.
Load diff
-99
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
-60
@@ -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;
|
||||
}
|
||||
}
|
||||
-127
@@ -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);
|
||||
}
|
||||
}
|
||||
-18
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
-138
@@ -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));
|
||||
}
|
||||
}
|
||||
-201
@@ -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) {
|
||||
}
|
||||
}
|
||||
-65
@@ -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;
|
||||
};
|
||||
}
|
||||
}
|
||||
-222
@@ -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));
|
||||
}
|
||||
}
|
||||
-101
@@ -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);
|
||||
}
|
||||
}
|
||||
-31
@@ -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);
|
||||
}
|
||||
-237
@@ -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);
|
||||
}
|
||||
}
|
||||
-65
@@ -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);
|
||||
}
|
||||
}
|
||||
-19
@@ -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);
|
||||
}
|
||||
}
|
||||
-22
@@ -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);
|
||||
}
|
||||
}
|
||||
-257
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
-86
@@ -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;
|
||||
}
|
||||
}
|
||||
-49
@@ -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);
|
||||
}
|
||||
}
|
||||
-17
@@ -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);
|
||||
}
|
||||
-126
@@ -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
Reference in new issue
Block a user