portal gun fixes and variants

This commit is contained in:
craisinlord committed 2026-10-08 13:54:21 -05:00
1 parent 2d90cb8aaf
commit 216efeb39b
111 files changed
+1937 -2136

No files matched your search

@@ -143,8 +143,9 @@ public final class AntarchyConfigCatalog {
|| name.equals("portalGunCanPortalsResizeWhenCreated") || name.equals("portalGunCanPortalsResizeWhenCreated")
|| name.equals("portalGunCanPortalProjectilesChunkload") || name.equals("portalGunCanPortalProjectilesChunkload")
|| name.equals("portalGunMaxShootDistance") || name.equals("portalGunMaxShootDistance")
|| name.equals("portalGunEntityGrabWeightBase") || name.equals("portalGunMinFloorExitSpeed")
|| name.equals("portalGunIndicatorSize") || name.equals("portalGunMaxLiveViews")
|| name.equals("portalGunMaxExitSpeed")
|| name.equals("hushweedSporeLifetimeSeconds") || name.equals("hushweedSporeLifetimeSeconds")
|| name.equals("elythiaFireflyParticlesEnabled") || name.equals("elythiaFireflyParticlesEnabled")
|| name.equals("duplicatorTreeEnabled") || name.equals("duplicatorTreeEnabled")
@@ -555,9 +555,10 @@ public final class AntarchySettings {
private static boolean portalGunCanPortalProjectilesChunkload = true; private static boolean portalGunCanPortalProjectilesChunkload = true;
private static boolean portalGunSeeThroughPortals = true; private static boolean portalGunSeeThroughPortals = true;
private static boolean portalGunFancyPortals = true; private static boolean portalGunFancyPortals = true;
private static int portalGunIndicatorSize = 30;
private static int portalGunMaxShootDistance = 10000; private static int portalGunMaxShootDistance = 10000;
private static int portalGunEntityGrabWeightBase = 100; private static double portalGunMinFloorExitSpeed = 0.6D;
private static int portalGunMaxLiveViews = 8;
private static double portalGunMaxExitSpeed = 0.0D;
private static boolean elythiaFireflyParticlesEnabled = true; private static boolean elythiaFireflyParticlesEnabled = true;
private static int bloodCrystalArmorShieldRechargeTicks = 600; private static int bloodCrystalArmorShieldRechargeTicks = 600;
private static int bloodCrystalAppleShieldCount = 2; private static int bloodCrystalAppleShieldCount = 2;
@@ -1335,16 +1336,20 @@ public final class AntarchySettings {
portalGunFancyPortals = value; portalGunFancyPortals = value;
} }
public static void setPortalGunIndicatorSize(int value) {
portalGunIndicatorSize = Math.max(0, Math.min(100, value));
}
public static void setPortalGunMaxShootDistance(int value) { public static void setPortalGunMaxShootDistance(int value) {
portalGunMaxShootDistance = Math.max(1, value); portalGunMaxShootDistance = Math.max(1, value);
} }
public static void setPortalGunEntityGrabWeightBase(int value) { public static void setPortalGunMaxLiveViews(int value) {
portalGunEntityGrabWeightBase = Math.max(0, value); portalGunMaxLiveViews = Math.max(0, Math.min(32, value));
}
public static void setPortalGunMinFloorExitSpeed(double value) {
portalGunMinFloorExitSpeed = Math.max(0.0D, Math.min(4.0D, value));
}
public static void setPortalGunMaxExitSpeed(double value) {
portalGunMaxExitSpeed = Math.max(0.0D, Math.min(100.0D, value));
} }
public static boolean permanentPortalsEnabled() { return permanentPortalsEnabled; } public static boolean permanentPortalsEnabled() { return permanentPortalsEnabled; }
@@ -1358,9 +1363,10 @@ public final class AntarchySettings {
public static boolean portalGunCanPortalProjectilesChunkload() { return portalGunCanPortalProjectilesChunkload; } public static boolean portalGunCanPortalProjectilesChunkload() { return portalGunCanPortalProjectilesChunkload; }
public static boolean portalGunSeeThroughPortals() { return portalGunSeeThroughPortals; } public static boolean portalGunSeeThroughPortals() { return portalGunSeeThroughPortals; }
public static boolean portalGunFancyPortals() { return portalGunFancyPortals; } public static boolean portalGunFancyPortals() { return portalGunFancyPortals; }
public static int portalGunIndicatorSize() { return portalGunIndicatorSize; }
public static int portalGunMaxShootDistance() { return portalGunMaxShootDistance; } public static int portalGunMaxShootDistance() { return portalGunMaxShootDistance; }
public static int portalGunEntityGrabWeightBase() { return portalGunEntityGrabWeightBase; } public static double portalGunMinFloorExitSpeed() { return portalGunMinFloorExitSpeed; }
public static int portalGunMaxLiveViews() { return portalGunMaxLiveViews; }
public static double portalGunMaxExitSpeed() { return portalGunMaxExitSpeed; }
public static void setBrownAntRequiresReagent(boolean value) { public static void setBrownAntRequiresReagent(boolean value) {
brownAntRequiresReagent = value; brownAntRequiresReagent = value;
@@ -6,23 +6,14 @@ import com.craisinlord.antarchy.content.AntarchyObjects;
import com.craisinlord.antarchy.content.AntarchySoundEvents; import com.craisinlord.antarchy.content.AntarchySoundEvents;
import com.craisinlord.antarchy.content.AntarchyTags; import com.craisinlord.antarchy.content.AntarchyTags;
import com.mojang.serialization.MapCodec; import com.mojang.serialization.MapCodec;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction; import net.minecraft.core.Direction;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.sounds.SoundSource; import net.minecraft.sounds.SoundSource;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionHand;
import net.minecraft.world.ItemInteractionResult; import net.minecraft.world.ItemInteractionResult;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.item.ItemEntity;
import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.DiggerItem;
import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.ShearsItem;
import net.minecraft.world.item.SwordItem;
import net.minecraft.world.item.context.BlockPlaceContext; import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.Level; import net.minecraft.world.level.Level;
@@ -38,9 +29,7 @@ import net.minecraft.world.level.block.state.properties.BlockStateProperties;
import net.minecraft.world.level.block.state.properties.DirectionProperty; import net.minecraft.world.level.block.state.properties.DirectionProperty;
import net.minecraft.world.level.block.state.properties.EnumProperty; import net.minecraft.world.level.block.state.properties.EnumProperty;
import net.minecraft.world.level.block.state.properties.IntegerProperty; import net.minecraft.world.level.block.state.properties.IntegerProperty;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.BlockHitResult; import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.Vec3;
import net.minecraft.world.phys.shapes.CollisionContext; import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.Shapes; import net.minecraft.world.phys.shapes.Shapes;
import net.minecraft.world.phys.shapes.VoxelShape; import net.minecraft.world.phys.shapes.VoxelShape;
@@ -54,24 +43,6 @@ public class DuctTapeBlock extends Block {
public static final DirectionProperty FACING = BlockStateProperties.HORIZONTAL_FACING; public static final DirectionProperty FACING = BlockStateProperties.HORIZONTAL_FACING;
private static final int TAPE_HEIGHT = 2; private static final int TAPE_HEIGHT = 2;
private static final long STICK_SOUND_COOLDOWN_TICKS = 10L;
private static final long TAPE_DECAY_INTERVAL_TICKS = 30L;
private static final double FLOOR_TRIGGER_HEIGHT = 0.30D;
private static final double WALL_TRIGGER_DEPTH = 0.25D;
private static final double TRIGGER_PAD = 0.12D;
private static final double WALL_Y_PAD = 0.12D;
private static final double WALL_SIDE_PAD = 0.08D;
private static final double WALL_INNER_INSET = 0.03D;
private static final double TRIGGER_INFLATE_DEFAULT = 0.15D;
private static final int SEARCH_INTERVAL_TICKS = 4;
private static final double SEARCH_MOTION_THRESHOLD_SQR = 1.0E-8D;
private static final double SEARCH_PAD_XZ = Math.max(WALL_TRIGGER_DEPTH, TRIGGER_PAD);
private static final double SEARCH_PAD_Y = FLOOR_TRIGGER_HEIGHT;
private static final Map<UUID, Long> STICK_SOUND_TIMES = new HashMap<>();
private static final Map<UUID, String> STICK_CONTACT_KEYS = new HashMap<>();
private static final Map<String, Long> TAPE_DECAY_TIMES = new HashMap<>();
private static final Map<UUID, StuckContact> STUCK_ENTITIES = new HashMap<>();
public DuctTapeBlock(BlockBehaviour.Properties properties) { public DuctTapeBlock(BlockBehaviour.Properties properties) {
super(properties); super(properties);
@@ -138,14 +109,6 @@ public class DuctTapeBlock extends Block {
return state.canSurvive(context.getLevel(), context.getClickedPos()) ? state : null; return state.canSurvive(context.getLevel(), context.getClickedPos()) ? state : null;
} }
@Override
protected void onRemove(BlockState state, Level level, BlockPos pos, BlockState newState, boolean movedByPiston) {
if (!state.is(newState.getBlock())) {
releaseEntitiesForTape(level, pos);
}
super.onRemove(state, level, pos, newState, movedByPiston);
}
@Override @Override
protected ItemInteractionResult useItemOn( protected ItemInteractionResult useItemOn(
ItemStack stack, ItemStack stack,
@@ -160,14 +123,6 @@ public class DuctTapeBlock extends Block {
return ItemInteractionResult.PASS_TO_DEFAULT_BLOCK_INTERACTION; return ItemInteractionResult.PASS_TO_DEFAULT_BLOCK_INTERACTION;
} }
if (isReleaseTool(stack) && (isStuckToTape(player) || isTouchingTape(level, player))) {
if (!level.isClientSide) {
consumeTapeUse(level, pos, state);
level.playSound(null, pos, SoundEvents.WOOL_BREAK, SoundSource.BLOCKS, 0.8F, 0.9F + level.random.nextFloat() * 0.2F);
}
return ItemInteractionResult.sidedSuccess(level.isClientSide);
}
if (!stack.isDamageableItem() || !stack.isDamaged() || stack.is(AntarchyTags.Items.DUCT_TAPE_BLACKLIST)) { if (!stack.isDamageableItem() || !stack.isDamaged() || stack.is(AntarchyTags.Items.DUCT_TAPE_BLACKLIST)) {
return ItemInteractionResult.PASS_TO_DEFAULT_BLOCK_INTERACTION; return ItemInteractionResult.PASS_TO_DEFAULT_BLOCK_INTERACTION;
} }
@@ -194,24 +149,6 @@ public class DuctTapeBlock extends Block {
return ItemInteractionResult.sidedSuccess(level.isClientSide); return ItemInteractionResult.sidedSuccess(level.isClientSide);
} }
@Override
protected void entityInside(BlockState state, Level level, BlockPos pos, Entity entity) {
if (level.isClientSide || entity.isSpectator() || !touchesTape(level, entity, pos, state)) {
return;
}
rememberStuckContact(level, pos, entity);
if (!(entity instanceof ItemEntity)) {
maybeConsumeTapeDecay(level, pos, state);
}
long gameTime = level.getGameTime();
String tapeKey = tapeKey(level, pos);
if (shouldPlayStickSound(entity.getUUID(), tapeKey, gameTime)) {
level.playSound(null, pos, AntarchySoundEvents.DUCT_TAPE_USE.get(), SoundSource.BLOCKS, 0.85F, 0.9F + level.random.nextFloat() * 0.15F);
}
applyStick(state, pos, entity);
}
@Override @Override
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) { protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
builder.add(USES, FACE, FACING); builder.add(USES, FACE, FACING);
@@ -244,12 +181,6 @@ public class DuctTapeBlock extends Block {
}; };
} }
private static boolean isReleaseTool(ItemStack stack) {
return stack.getItem() instanceof DiggerItem
|| stack.getItem() instanceof SwordItem
|| stack.getItem() instanceof ShearsItem;
}
private static void consumeTapeUse(Level level, BlockPos pos, BlockState state) { private static void consumeTapeUse(Level level, BlockPos pos, BlockState state) {
int remainingUses = state.getValue(USES) - 1; int remainingUses = state.getValue(USES) - 1;
if (remainingUses > 0) { if (remainingUses > 0) {
@@ -257,322 +188,6 @@ public class DuctTapeBlock extends Block {
return; return;
} }
releaseEntitiesForTape(level, pos);
TAPE_DECAY_TIMES.remove(tapeDecayKey(level, pos));
level.removeBlock(pos, false); level.removeBlock(pos, false);
} }
private static void removeTape(Level level, BlockPos pos, BlockState state) {
releaseEntitiesForTape(level, pos);
TAPE_DECAY_TIMES.remove(tapeDecayKey(level, pos));
level.removeBlock(pos, false);
}
private static void maybeConsumeTapeDecay(Level level, BlockPos pos, BlockState state) {
long gameTime = level.getGameTime();
String key = tapeDecayKey(level, pos);
Long lastConsumed = TAPE_DECAY_TIMES.get(key);
if (lastConsumed == null) {
TAPE_DECAY_TIMES.put(key, gameTime);
return;
}
if (gameTime - lastConsumed < TAPE_DECAY_INTERVAL_TICKS) {
return;
}
TAPE_DECAY_TIMES.put(key, gameTime);
consumeTapeUse(level, pos, state);
}
public static void tickStuckEntity(Entity entity) {
if (entity == null || entity.level().isClientSide() || isStickyBlacklist(entity)) {
return;
}
if (!entity.isAlive() || entity.isRemoved()) {
releaseEntity(entity);
return;
}
StuckContact contact = STUCK_ENTITIES.get(entity.getUUID());
if (contact != null && isContactValid(entity.level(), contact)) {
BlockPos pos = BlockPos.of(contact.blockPos());
BlockState state = entity.level().getBlockState(pos);
applyStick(state, pos, entity);
if (!(entity instanceof ItemEntity)) {
maybeConsumeTapeDecay(entity.level(), pos, state);
}
return;
}
if (contact != null) {
releaseEntity(entity);
}
if (!shouldSearchForTape(entity)) {
return;
}
TapeHit hit = findTouchedTape(entity.level(), entity);
if (hit == null) {
if (entity.isNoGravity()) {
entity.setNoGravity(false);
}
return;
}
rememberStuckContact(entity.level(), hit.pos(), entity);
applyStick(hit.state(), hit.pos(), entity);
if (!(entity instanceof ItemEntity)) {
maybeConsumeTapeDecay(entity.level(), hit.pos(), hit.state());
}
}
public static boolean isStuckToTape(Entity entity) {
if (entity == null || entity.level().isClientSide() || isStickyBlacklist(entity)) {
return false;
}
StuckContact contact = STUCK_ENTITIES.get(entity.getUUID());
return contact != null && isContactValid(entity.level(), contact);
}
public static boolean isTouchingTape(Entity entity) {
return entity != null && isTouchingTape(entity.level(), entity);
}
public static boolean isTouchingTape(Level level, Entity entity) {
if (isStickyBlacklist(entity)) {
return false;
}
return findTouchedTape(level, entity) != null;
}
public static boolean shouldTickStuckEntity(Entity entity) {
if (entity == null || entity.level().isClientSide() || isStickyBlacklist(entity)) {
return false;
}
if (!entity.isAlive() || entity.isRemoved()) {
return true;
}
StuckContact contact = STUCK_ENTITIES.get(entity.getUUID());
if (contact != null) {
return true;
}
return shouldSearchForTape(entity);
}
private static boolean isStickyBlacklist(Entity entity) {
return entity != null && entity.getType().is(AntarchyTags.Entities.DUCT_TAPE_STICK_BLACKLIST);
}
private static void applyStick(BlockState state, BlockPos pos, Entity entity) {
entity.setDeltaMovement(Vec3.ZERO);
entity.resetFallDistance();
entity.hasImpulse = true;
entity.setNoGravity(true);
Vec3 anchor = getTapeAnchor(state, pos, entity);
entity.setPos(anchor.x, anchor.y, anchor.z);
if (state.getValue(FACE) == AttachFace.FLOOR) {
entity.setOnGround(true);
}
}
private static void rememberStuckContact(Level level, BlockPos pos, Entity entity) {
UUID entityId = entity.getUUID();
String key = tapeKey(level, pos);
StuckContact existing = STUCK_ENTITIES.get(entityId);
if (existing != null && existing.tapeKey().equals(key)) {
return;
}
double offsetX = entity.getX() - pos.getX();
double offsetY = entity.getY() - pos.getY();
double offsetZ = entity.getZ() - pos.getZ();
STUCK_ENTITIES.put(entityId, new StuckContact(
key,
level.dimension().location().toString(),
pos.asLong(),
clamp(offsetX, 0.05D, 0.95D),
clamp(offsetY, 0.0D, 0.98D),
clamp(offsetZ, 0.05D, 0.95D)
));
}
private static Vec3 getTapeAnchor(BlockState state, BlockPos pos, Entity entity) {
StuckContact contact = STUCK_ENTITIES.get(entity.getUUID());
double offsetX = contact != null ? contact.offsetX() : 0.5D;
double offsetY = contact != null ? contact.offsetY() : 0.0D;
double offsetZ = contact != null ? contact.offsetZ() : 0.5D;
double wallOffset = entity instanceof Player ? 0.06D : -0.28D;
double centerX = pos.getX() + 0.5D;
double centerZ = pos.getZ() + 0.5D;
return switch (state.getValue(FACE)) {
case FLOOR -> new Vec3(
pos.getX() + offsetX,
pos.getY() + 0.01D,
pos.getZ() + offsetZ
);
case WALL -> switch (state.getValue(FACING)) {
case NORTH -> new Vec3(centerX, pos.getY() + offsetY, pos.getZ() - wallOffset);
case SOUTH -> new Vec3(centerX, pos.getY() + offsetY, pos.getZ() + 1.0D + wallOffset);
case EAST -> new Vec3(pos.getX() + 1.0D + wallOffset, pos.getY() + offsetY, centerZ);
case WEST -> new Vec3(pos.getX() - wallOffset, pos.getY() + offsetY, centerZ);
default -> new Vec3(centerX, pos.getY() + offsetY, centerZ);
};
default -> new Vec3(pos.getX() + offsetX, pos.getY() + 0.01D, pos.getZ() + offsetZ);
};
}
private static boolean isContactValid(Level level, StuckContact contact) {
if (!level.dimension().location().toString().equals(contact.dimensionId())) {
return false;
}
BlockPos pos = BlockPos.of(contact.blockPos());
BlockState state = level.getBlockState(pos);
return state.getBlock() instanceof DuctTapeBlock
&& tapeKey(level, pos).equals(contact.tapeKey());
}
private static boolean shouldSearchForTape(Entity entity) {
Vec3 velocity = entity.getDeltaMovement();
if (velocity.lengthSqr() < SEARCH_MOTION_THRESHOLD_SQR) {
return false;
}
return (entity.tickCount + entity.getId()) % SEARCH_INTERVAL_TICKS == 0;
}
private static TapeHit findTouchedTape(Level level, Entity entity) {
AABB box = entity.getBoundingBox().inflate(SEARCH_PAD_XZ, SEARCH_PAD_Y, SEARCH_PAD_XZ);
BlockPos min = BlockPos.containing(box.minX, box.minY, box.minZ);
BlockPos max = BlockPos.containing(box.maxX, box.maxY, box.maxZ);
if (!level.hasChunksAt(min, max)) {
return null;
}
BlockPos.MutableBlockPos cursor = new BlockPos.MutableBlockPos();
for (int x = min.getX(); x <= max.getX(); x++) {
for (int y = min.getY(); y <= max.getY(); y++) {
for (int z = min.getZ(); z <= max.getZ(); z++) {
cursor.set(x, y, z);
BlockState state = level.getBlockState(cursor);
if (state.isAir()) {
continue;
}
if (state.getBlock() instanceof DuctTapeBlock && touchesTape(level, entity, cursor, state)) {
return new TapeHit(cursor.immutable(), state);
}
}
}
}
return null;
}
private static boolean touchesTape(Level level, Entity entity, BlockPos pos, BlockState state) {
return entity.getBoundingBox().intersects(getStickyTriggerBounds(state, pos));
}
private static AABB getStickyTriggerBounds(BlockState state, BlockPos pos) {
AABB bounds = getShapeFor(state).bounds().move(pos);
return switch (state.getValue(FACE)) {
case FLOOR -> new AABB(
bounds.minX - TRIGGER_PAD,
pos.getY() - 0.02D,
bounds.minZ - TRIGGER_PAD,
bounds.maxX + TRIGGER_PAD,
pos.getY() + FLOOR_TRIGGER_HEIGHT,
bounds.maxZ + TRIGGER_PAD
);
case WALL -> {
yield switch (state.getValue(FACING)) {
case NORTH -> new AABB(bounds.minX - WALL_SIDE_PAD, bounds.minY - WALL_Y_PAD, bounds.minZ - WALL_TRIGGER_DEPTH, bounds.maxX + WALL_SIDE_PAD, bounds.maxY + WALL_Y_PAD, bounds.maxZ + WALL_INNER_INSET);
case SOUTH -> new AABB(bounds.minX - WALL_SIDE_PAD, bounds.minY - WALL_Y_PAD, bounds.minZ - WALL_INNER_INSET, bounds.maxX + WALL_SIDE_PAD, bounds.maxY + WALL_Y_PAD, bounds.maxZ + WALL_TRIGGER_DEPTH);
case EAST -> new AABB(bounds.minX - WALL_TRIGGER_DEPTH, bounds.minY - WALL_Y_PAD, bounds.minZ - WALL_SIDE_PAD, bounds.maxX + WALL_INNER_INSET, bounds.maxY + WALL_Y_PAD, bounds.maxZ + WALL_SIDE_PAD);
case WEST -> new AABB(bounds.minX - WALL_INNER_INSET, bounds.minY - WALL_Y_PAD, bounds.minZ - WALL_SIDE_PAD, bounds.maxX + WALL_TRIGGER_DEPTH, bounds.maxY + WALL_Y_PAD, bounds.maxZ + WALL_SIDE_PAD);
default -> bounds.inflate(TRIGGER_INFLATE_DEFAULT);
};
}
default -> bounds.inflate(TRIGGER_INFLATE_DEFAULT);
};
}
private static void releaseEntitiesForTape(Level level, BlockPos pos) {
String key = tapeKey(level, pos);
AABB searchBox = AABB.unitCubeFromLowerCorner(Vec3.atLowerCornerOf(pos)).inflate(2.0D);
STUCK_ENTITIES.entrySet().removeIf(entry -> {
StuckContact contact = entry.getValue();
if (!key.equals(contact.tapeKey())) {
return false;
}
level.getEntitiesOfClass(Entity.class, searchBox, candidate -> candidate.getUUID().equals(entry.getKey()))
.forEach(candidate -> candidate.setNoGravity(false));
STICK_SOUND_TIMES.remove(entry.getKey());
STICK_CONTACT_KEYS.remove(entry.getKey());
return true;
});
}
private static void releaseEntity(Entity entity) {
if (entity == null) {
return;
}
STUCK_ENTITIES.remove(entity.getUUID());
STICK_SOUND_TIMES.remove(entity.getUUID());
STICK_CONTACT_KEYS.remove(entity.getUUID());
entity.setNoGravity(false);
}
private static String tapeDecayKey(Level level, BlockPos pos) {
return level.dimension().location() + ":" + pos.asLong();
}
private static String tapeKey(Level level, BlockPos pos) {
return level.dimension().location() + ":" + pos.asLong();
}
private static boolean shouldPlayStickSound(UUID entityId, String tapeKey, long gameTime) {
String lastKey = STICK_CONTACT_KEYS.get(entityId);
Long lastContact = STICK_SOUND_TIMES.get(entityId);
if (lastKey == null || !tapeKey.equals(lastKey)) {
STICK_CONTACT_KEYS.put(entityId, tapeKey);
STICK_SOUND_TIMES.put(entityId, gameTime);
return true;
}
if (lastContact == null || gameTime - lastContact > STICK_SOUND_COOLDOWN_TICKS) {
STICK_SOUND_TIMES.put(entityId, gameTime);
return true;
}
return false;
}
private static double clamp(double value, double min, double max) {
return Math.max(min, Math.min(max, value));
}
private record StuckContact(
String tapeKey,
String dimensionId,
long blockPos,
double offsetX,
double offsetY,
double offsetZ
) {
}
private record TapeHit(BlockPos pos, BlockState state) {
}
} }
@@ -26,8 +26,7 @@ public interface PortalGunPortalCellAccess {
default boolean portalGun$putFaceRecord(PortalGunPortalFaceRecord record) { default boolean portalGun$putFaceRecord(PortalGunPortalFaceRecord record) {
PortalGunPortalFaceRecord existing = this.portalGun$getFaceRecord(record.face()); PortalGunPortalFaceRecord existing = this.portalGun$getFaceRecord(record.face());
if (existing != null if (existing != null
&& (!existing.ownerId().equals(record.ownerId()) && (!java.util.Objects.equals(existing.gunId(), record.gunId())
|| !java.util.Objects.equals(existing.channelName(), record.channelName())
|| existing.side() != record.side())) { || existing.side() != record.side())) {
return false; return false;
} }
@@ -63,7 +62,7 @@ public interface PortalGunPortalCellAccess {
if (record.ownerId() == null || record.portalId() == null) { if (record.ownerId() == null || record.portalId() == null) {
continue; continue;
} }
if (!PortalGunSavedData.isRegistered(level, record.ownerId(), record.gunId(), record.channelName(), record.side(), record.portalId())) { if (!PortalGunSavedData.isRegistered(level, record.gunId(), record.side(), record.portalId())) {
this.portalGun$removeFaceRecord(record.face(), record.portalId()); this.portalGun$removeFaceRecord(record.face(), record.portalId());
continue; continue;
} }
@@ -2,6 +2,7 @@ package com.craisinlord.antarchy.content.block.entity;
import com.craisinlord.antarchy.content.portalgun.PortalGunPlacement; import com.craisinlord.antarchy.content.portalgun.PortalGunPlacement;
import com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity; import com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity;
import com.craisinlord.antarchy.content.portalgun.PortalGunVariant;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
@@ -14,9 +15,8 @@ import net.minecraft.nbt.Tag;
public record PortalGunPortalFaceRecord( public record PortalGunPortalFaceRecord(
UUID ownerId, UUID ownerId,
String ownerIdentity,
UUID gunId, UUID gunId,
String channelName, PortalGunVariant variant,
UUID portalId, UUID portalId,
UUID linkedPortalId, UUID linkedPortalId,
PortalGunPortalEntity.PortalSide side, PortalGunPortalEntity.PortalSide side,
@@ -43,21 +43,20 @@ public record PortalGunPortalFaceRecord(
} }
public PortalGunPortalFaceRecord withPair(UUID linkedPortalId, int pairTime) { public PortalGunPortalFaceRecord withPair(UUID linkedPortalId, int pairTime) {
return new PortalGunPortalFaceRecord(this.ownerId, this.ownerIdentity, this.gunId, this.channelName, this.portalId, linkedPortalId, this.side, this.face, this.upAxis, this.masterPos, this.basePos, this.portalSpots, this.compensatedSpots, pairTime, this.portalWidth, this.portalHeight, this.master); return new PortalGunPortalFaceRecord(this.ownerId, this.gunId, this.variant, this.portalId, linkedPortalId, this.side, this.face, this.upAxis, this.masterPos, this.basePos, this.portalSpots, this.compensatedSpots, pairTime, this.portalWidth, this.portalHeight, this.master);
} }
public PortalGunPortalFaceRecord withMaster(boolean master) { public PortalGunPortalFaceRecord withMaster(boolean master) {
return new PortalGunPortalFaceRecord(this.ownerId, this.ownerIdentity, this.gunId, this.channelName, this.portalId, this.linkedPortalId, this.side, this.face, this.upAxis, this.masterPos, this.basePos, this.portalSpots, this.compensatedSpots, this.pairTime, this.portalWidth, this.portalHeight, master); return new PortalGunPortalFaceRecord(this.ownerId, this.gunId, this.variant, this.portalId, this.linkedPortalId, this.side, this.face, this.upAxis, this.masterPos, this.basePos, this.portalSpots, this.compensatedSpots, this.pairTime, this.portalWidth, this.portalHeight, master);
} }
public CompoundTag save() { public CompoundTag save() {
CompoundTag tag = new CompoundTag(); CompoundTag tag = new CompoundTag();
tag.putUUID("OwnerId", this.ownerId); tag.putUUID("OwnerId", this.ownerId);
tag.putString("OwnerIdentity", this.ownerIdentity == null ? "" : this.ownerIdentity);
if (this.gunId != null) { if (this.gunId != null) {
tag.putUUID("GunId", this.gunId); tag.putUUID("GunId", this.gunId);
} }
tag.putString("ChannelName", this.channelName == null ? "" : this.channelName); tag.putInt("Variant", this.variant.id());
tag.putUUID("PortalId", this.portalId); tag.putUUID("PortalId", this.portalId);
if (this.linkedPortalId != null) { if (this.linkedPortalId != null) {
tag.putUUID("LinkedPortalId", this.linkedPortalId); tag.putUUID("LinkedPortalId", this.linkedPortalId);
@@ -105,9 +104,8 @@ public record PortalGunPortalFaceRecord(
int height = net.minecraft.util.Mth.clamp(tag.getInt("PortalHeight"), 2, 16); int height = net.minecraft.util.Mth.clamp(tag.getInt("PortalHeight"), 2, 16);
return new PortalGunPortalFaceRecord( return new PortalGunPortalFaceRecord(
tag.getUUID("OwnerId"), tag.getUUID("OwnerId"),
tag.getString("OwnerIdentity"),
tag.hasUUID("GunId") ? tag.getUUID("GunId") : null, tag.hasUUID("GunId") ? tag.getUUID("GunId") : null,
tag.getString("ChannelName"), PortalGunVariant.byId(tag.getInt("Variant")),
tag.getUUID("PortalId"), tag.getUUID("PortalId"),
tag.hasUUID("LinkedPortalId") ? tag.getUUID("LinkedPortalId") : null, tag.hasUUID("LinkedPortalId") ? tag.getUUID("LinkedPortalId") : null,
side, side,
@@ -4,6 +4,7 @@ import com.craisinlord.antarchy.Antarchy;
import com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity; import com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity;
import com.craisinlord.antarchy.content.portalgun.PortalGunPlacement; import com.craisinlord.antarchy.content.portalgun.PortalGunPlacement;
import com.craisinlord.antarchy.content.portalgun.PortalGunSavedData; import com.craisinlord.antarchy.content.portalgun.PortalGunSavedData;
import com.craisinlord.antarchy.content.portalgun.PortalGunVariant;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.EnumMap; import java.util.EnumMap;
import java.util.HashSet; import java.util.HashSet;
@@ -35,9 +36,8 @@ import net.minecraft.world.phys.Vec3;
public class PortalGunPortalMasterBlockEntity extends BlockEntity implements PortalGunPortalCellAccess { public class PortalGunPortalMasterBlockEntity extends BlockEntity implements PortalGunPortalCellAccess {
private final Map<Direction, PortalGunPortalFaceRecord> faceRecords = new EnumMap<>(Direction.class); private final Map<Direction, PortalGunPortalFaceRecord> faceRecords = new EnumMap<>(Direction.class);
private UUID ownerId; private UUID ownerId;
private String ownerIdentity;
private UUID gunId; private UUID gunId;
private String channelName; private PortalGunVariant variant = PortalGunVariant.DEFAULT;
private UUID portalId; private UUID portalId;
private PortalGunPortalEntity.PortalSide side = PortalGunPortalEntity.PortalSide.BLUE; private PortalGunPortalEntity.PortalSide side = PortalGunPortalEntity.PortalSide.BLUE;
private Direction facing = Direction.NORTH; private Direction facing = Direction.NORTH;
@@ -57,8 +57,7 @@ public class PortalGunPortalMasterBlockEntity extends BlockEntity implements Por
public void configure( public void configure(
UUID ownerId, UUID ownerId,
UUID gunId, UUID gunId,
String ownerIdentity, PortalGunVariant variant,
String channelName,
UUID portalId, UUID portalId,
PortalGunPortalEntity.PortalSide side, PortalGunPortalEntity.PortalSide side,
Direction facing, Direction facing,
@@ -71,9 +70,8 @@ public class PortalGunPortalMasterBlockEntity extends BlockEntity implements Por
int portalHeight int portalHeight
) { ) {
this.ownerId = ownerId; this.ownerId = ownerId;
this.ownerIdentity = ownerIdentity;
this.gunId = gunId; this.gunId = gunId;
this.channelName = channelName; this.variant = variant == null ? PortalGunVariant.DEFAULT : variant;
this.portalId = portalId; this.portalId = portalId;
this.side = side; this.side = side;
this.facing = facing; this.facing = facing;
@@ -84,7 +82,7 @@ public class PortalGunPortalMasterBlockEntity extends BlockEntity implements Por
this.portalHeight = portalHeight; this.portalHeight = portalHeight;
this.compensatedSpots = new HashSet<>(compensatedSpots); this.compensatedSpots = new HashSet<>(compensatedSpots);
this.pairTime = pairTime; this.pairTime = pairTime;
this.portalGun$putFaceRecord(new PortalGunPortalFaceRecord(ownerId, ownerIdentity, gunId, channelName, portalId, this.linkedPortalId, side, facing, upAxis, this.worldPosition, this.basePos, List.of(this.portalSpots), this.compensatedSpots, pairTime, portalWidth, portalHeight, true)); this.portalGun$putFaceRecord(new PortalGunPortalFaceRecord(ownerId, gunId, this.variant, portalId, this.linkedPortalId, side, facing, upAxis, this.worldPosition, this.basePos, List.of(this.portalSpots), this.compensatedSpots, pairTime, portalWidth, portalHeight, true));
this.setChanged(); this.setChanged();
} }
@@ -105,30 +103,8 @@ public class PortalGunPortalMasterBlockEntity extends BlockEntity implements Por
return this.gunId; return this.gunId;
} }
public String getChannelName() { public PortalGunVariant getVariant() {
return this.channelName; return this.variant;
}
public void adoptGunId(UUID gunId) {
if (this.gunId == null && gunId != null) {
this.gunId = gunId;
this.setChanged();
}
}
public void adoptChannelIdentity(UUID gunId, String channelName) {
boolean changed = false;
if (this.gunId == null && gunId != null) {
this.gunId = gunId;
changed = true;
}
if (channelName != null && !channelName.isEmpty() && !channelName.equals(this.channelName)) {
this.channelName = channelName;
changed = true;
}
if (changed) {
this.setChanged();
}
} }
public PortalGunPortalEntity.PortalSide getSide() { public PortalGunPortalEntity.PortalSide getSide() {
@@ -228,12 +204,6 @@ public class PortalGunPortalMasterBlockEntity extends BlockEntity implements Por
level.removeBlock(pos, false); level.removeBlock(pos, false);
return; return;
} }
if (blockEntity.gunId == null && portal.getGunId() != null) {
blockEntity.adoptGunId(portal.getGunId());
} else if (portal.getGunId() == null && blockEntity.gunId != null) {
portal.adoptGunId(blockEntity.gunId);
}
blockEntity.adoptChannelIdentity(portal.getGunId(), portal.getChannelName());
blockEntity.ensureLinkedPortal(level, portal); blockEntity.ensureLinkedPortal(level, portal);
UUID linkedPortalId = portal.getLinkedPortalId(); UUID linkedPortalId = portal.getLinkedPortalId();
if ((linkedPortalId == null && blockEntity.linkedPortalId != null) if ((linkedPortalId == null && blockEntity.linkedPortalId != null)
@@ -282,7 +252,7 @@ public class PortalGunPortalMasterBlockEntity extends BlockEntity implements Por
if (this.portalId == null || this.ownerId == null) { if (this.portalId == null || this.ownerId == null) {
return null; return null;
} }
if (!PortalGunSavedData.isRegistered(level, this.ownerId, this.gunId, this.channelName, this.side, this.portalId)) { if (!PortalGunSavedData.isRegistered(level, this.gunId, this.side, this.portalId)) {
return null; return null;
} }
if (level.getEntity(this.portalId) instanceof PortalGunPortalEntity portal && !portal.isRemoved()) { if (level.getEntity(this.portalId) instanceof PortalGunPortalEntity portal && !portal.isRemoved()) {
@@ -300,18 +270,18 @@ public class PortalGunPortalMasterBlockEntity extends BlockEntity implements Por
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
PortalGunPortalEntity restored = new PortalGunPortalEntity((EntityType<? extends PortalGunPortalEntity>) rawType, level); PortalGunPortalEntity restored = new PortalGunPortalEntity((EntityType<? extends PortalGunPortalEntity>) rawType, level);
restored.setUUID(this.portalId); restored.setUUID(this.portalId);
restored.configure(this.ownerId, this.gunId, this.ownerIdentity == null ? String.valueOf(this.ownerId) : this.ownerIdentity, this.channelName == null ? String.valueOf(this.gunId) : this.channelName, this.side, placement); restored.configure(this.ownerId, this.gunId, this.variant, this.side, placement);
restored.restorePair(this.linkedPortalId, this.pairTime); restored.restorePair(this.linkedPortalId, this.pairTime);
Vec3 center = placement.center(); Vec3 center = placement.center();
restored.moveTo(center.x, center.y, center.z, placement.yaw(), 0.0F); restored.moveTo(center.x, center.y, center.z, placement.yaw(), 0.0F);
level.addFreshEntity(restored); level.addFreshEntity(restored);
PortalGunSavedData.setPortal(level, this.ownerId, this.gunId, this.channelName, this.side, restored.getUUID()); PortalGunSavedData.setPortal(level, this.gunId, this.side, restored.getUUID());
return restored; return restored;
} }
public static PortalGunPortalEntity restorePortalEntity(ServerLevel level, PortalGunPortalFaceRecord record) { public static PortalGunPortalEntity restorePortalEntity(ServerLevel level, PortalGunPortalFaceRecord record) {
if (record.ownerId() == null || record.portalId() == null if (record.ownerId() == null || record.portalId() == null
|| !PortalGunSavedData.isRegistered(level, record.ownerId(), record.gunId(), record.channelName(), record.side(), record.portalId())) { || !PortalGunSavedData.isRegistered(level, record.gunId(), record.side(), record.portalId())) {
return null; return null;
} }
if (level.getEntity(record.portalId()) instanceof PortalGunPortalEntity portal && !portal.isRemoved()) { if (level.getEntity(record.portalId()) instanceof PortalGunPortalEntity portal && !portal.isRemoved()) {
@@ -325,13 +295,13 @@ public class PortalGunPortalMasterBlockEntity extends BlockEntity implements Por
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
PortalGunPortalEntity restored = new PortalGunPortalEntity((EntityType<? extends PortalGunPortalEntity>) rawType, level); PortalGunPortalEntity restored = new PortalGunPortalEntity((EntityType<? extends PortalGunPortalEntity>) rawType, level);
restored.setUUID(record.portalId()); restored.setUUID(record.portalId());
restored.configure(record.ownerId(), record.gunId(), record.ownerIdentity(), record.channelName(), record.side(), placement); restored.configure(record.ownerId(), record.gunId(), record.variant(), record.side(), placement);
restored.restorePair(record.linkedPortalId(), record.pairTime()); restored.restorePair(record.linkedPortalId(), record.pairTime());
Vec3 center = placement.center(); Vec3 center = placement.center();
restored.moveTo(center.x, center.y, center.z, placement.yaw(), 0.0F); restored.moveTo(center.x, center.y, center.z, placement.yaw(), 0.0F);
level.addFreshEntity(restored); level.addFreshEntity(restored);
PortalGunSavedData.setPortal(level, record.ownerId(), record.gunId(), record.channelName(), record.side(), restored.getUUID()); PortalGunSavedData.setPortal(level, record.gunId(), record.side(), restored.getUUID());
PortalGunPortalEntity linked = PortalGunSavedData.findLoadedPortal(level, record.ownerId(), record.gunId(), record.channelName(), record.side() == PortalGunPortalEntity.PortalSide.BLUE ? PortalGunPortalEntity.PortalSide.ORANGE : PortalGunPortalEntity.PortalSide.BLUE); PortalGunPortalEntity linked = PortalGunSavedData.findLoadedPortal(level, record.gunId(), record.side() == PortalGunPortalEntity.PortalSide.BLUE ? PortalGunPortalEntity.PortalSide.ORANGE : PortalGunPortalEntity.PortalSide.BLUE);
if (linked != null && linked != restored && !linked.isRemoved()) { if (linked != null && linked != restored && !linked.isRemoved()) {
restored.linkTo(linked); restored.linkTo(linked);
linked.linkTo(restored); linked.linkTo(restored);
@@ -345,9 +315,7 @@ public class PortalGunPortalMasterBlockEntity extends BlockEntity implements Por
} }
PortalGunPortalEntity counterpart = PortalGunSavedData.findLoadedPortal( PortalGunPortalEntity counterpart = PortalGunSavedData.findLoadedPortal(
level, level,
this.ownerId,
this.gunId, this.gunId,
this.channelName,
this.side == PortalGunPortalEntity.PortalSide.BLUE ? PortalGunPortalEntity.PortalSide.ORANGE : PortalGunPortalEntity.PortalSide.BLUE this.side == PortalGunPortalEntity.PortalSide.BLUE ? PortalGunPortalEntity.PortalSide.ORANGE : PortalGunPortalEntity.PortalSide.BLUE
); );
if (counterpart == null || counterpart == portal || counterpart.isRemoved()) { if (counterpart == null || counterpart == portal || counterpart.isRemoved()) {
@@ -365,15 +333,10 @@ public class PortalGunPortalMasterBlockEntity extends BlockEntity implements Por
if (this.ownerId != null) { if (this.ownerId != null) {
tag.putUUID("OwnerId", this.ownerId); tag.putUUID("OwnerId", this.ownerId);
} }
if (this.ownerIdentity != null) {
tag.putString("OwnerIdentity", this.ownerIdentity);
}
if (this.gunId != null) { if (this.gunId != null) {
tag.putUUID("GunId", this.gunId); tag.putUUID("GunId", this.gunId);
} }
if (this.channelName != null) { tag.putInt("Variant", this.variant.id());
tag.putString("ChannelName", this.channelName);
}
if (this.portalId != null) { if (this.portalId != null) {
tag.putUUID("PortalId", this.portalId); tag.putUUID("PortalId", this.portalId);
} }
@@ -414,9 +377,8 @@ public class PortalGunPortalMasterBlockEntity extends BlockEntity implements Por
protected void loadAdditional(CompoundTag tag, HolderLookup.Provider registries) { protected void loadAdditional(CompoundTag tag, HolderLookup.Provider registries) {
super.loadAdditional(tag, registries); super.loadAdditional(tag, registries);
this.ownerId = tag.hasUUID("OwnerId") ? tag.getUUID("OwnerId") : null; this.ownerId = tag.hasUUID("OwnerId") ? tag.getUUID("OwnerId") : null;
this.ownerIdentity = tag.contains("OwnerIdentity") ? tag.getString("OwnerIdentity") : String.valueOf(this.ownerId);
this.gunId = tag.hasUUID("GunId") ? tag.getUUID("GunId") : null; this.gunId = tag.hasUUID("GunId") ? tag.getUUID("GunId") : null;
this.channelName = tag.contains("ChannelName") ? tag.getString("ChannelName") : this.gunId == null ? "" : "Random Channel #" + this.gunId.hashCode(); this.variant = PortalGunVariant.byId(tag.getInt("Variant"));
this.portalId = tag.hasUUID("PortalId") ? tag.getUUID("PortalId") : null; this.portalId = tag.hasUUID("PortalId") ? tag.getUUID("PortalId") : null;
this.linkedPortalId = tag.hasUUID("LinkedPortalId") ? tag.getUUID("LinkedPortalId") : null; this.linkedPortalId = tag.hasUUID("LinkedPortalId") ? tag.getUUID("LinkedPortalId") : null;
this.side = tag.getInt("Side") == PortalGunPortalEntity.PortalSide.ORANGE.ordinal() ? PortalGunPortalEntity.PortalSide.ORANGE : PortalGunPortalEntity.PortalSide.BLUE; this.side = tag.getInt("Side") == PortalGunPortalEntity.PortalSide.ORANGE.ordinal() ? PortalGunPortalEntity.PortalSide.ORANGE : PortalGunPortalEntity.PortalSide.BLUE;
@@ -443,7 +405,7 @@ public class PortalGunPortalMasterBlockEntity extends BlockEntity implements Por
this.compensatedSpots = compensatedSpots; this.compensatedSpots = compensatedSpots;
this.portalGun$loadFaceRecords(tag); this.portalGun$loadFaceRecords(tag);
if (this.faceRecords.isEmpty() && this.ownerId != null && this.portalId != null && this.portalSpots.length > 0) { if (this.faceRecords.isEmpty() && this.ownerId != null && this.portalId != null && this.portalSpots.length > 0) {
this.portalGun$putFaceRecord(new PortalGunPortalFaceRecord(this.ownerId, this.ownerIdentity, this.gunId, this.channelName, this.portalId, this.linkedPortalId, this.side, this.facing, this.upAxis, this.worldPosition, this.basePos, List.of(this.portalSpots), this.compensatedSpots, this.pairTime, this.portalWidth, this.portalHeight, true)); this.portalGun$putFaceRecord(new PortalGunPortalFaceRecord(this.ownerId, this.gunId, this.variant, this.portalId, this.linkedPortalId, this.side, this.facing, this.upAxis, this.worldPosition, this.basePos, List.of(this.portalSpots), this.compensatedSpots, this.pairTime, this.portalWidth, this.portalHeight, true));
} }
} }
@@ -0,0 +1,90 @@
package com.craisinlord.antarchy.content.client;
import com.craisinlord.antarchy.content.network.PortalGunTransitPayload;
import com.craisinlord.antarchy.content.gravity.AntarchyGravityApi;
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 = AntarchyGravityApi.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 = AntarchyGravityApi.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);
}
}
@@ -0,0 +1,94 @@
package com.craisinlord.antarchy.content.client;
import com.craisinlord.antarchy.content.item.PortalGunItem;
import com.craisinlord.antarchy.content.network.PortalGunIndicatorPayload;
import com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity;
import com.mojang.blaze3d.systems.RenderSystem;
import java.util.EnumSet;
import java.util.Set;
import java.util.UUID;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.ItemStack;
public final class PortalGunCrosshairRenderer {
private static final int WIDTH = 34;
private static final int HEIGHT = 33;
private static final int HALF_WIDTH = 17;
private static final float MISSING_BRIGHTNESS = 0.3F;
private static final float MISSING_ALPHA = 0.75F;
private static ClientLevel cachedLevel;
private static long cachedGameTime = Long.MIN_VALUE;
private static UUID cachedGunId;
private static Set<PortalGunPortalEntity.PortalSide> cachedPortals = Set.of();
private PortalGunCrosshairRenderer() {
}
public static void render(GuiGraphics guiGraphics) {
Minecraft minecraft = Minecraft.getInstance();
if (minecraft.player == null || minecraft.level == null || minecraft.screen != null || minecraft.options.hideGui
|| !minecraft.options.getCameraType().isFirstPerson()) {
return;
}
ItemStack held = minecraft.player.getMainHandItem();
if (!(held.getItem() instanceof PortalGunItem)) {
held = minecraft.player.getOffhandItem();
if (!(held.getItem() instanceof PortalGunItem)) {
return;
}
}
ResourceLocation crosshair = ((PortalGunItem) held.getItem()).getVariant().crosshairTexture();
UUID gunId = PortalGunItem.getGunId(held);
PortalGunIndicatorClientState.requestIfNeeded(minecraft, gunId);
Set<PortalGunPortalEntity.PortalSide> portals = portalsFor(minecraft, gunId);
PortalGunIndicatorPayload serverState = PortalGunIndicatorClientState.get(gunId);
boolean blueAvailable = serverState == null
? portals.contains(PortalGunPortalEntity.PortalSide.BLUE)
: serverState.blueAvailable();
boolean orangeAvailable = serverState == null
? portals.contains(PortalGunPortalEntity.PortalSide.ORANGE)
: serverState.orangeAvailable();
int left = (minecraft.getWindow().getGuiScaledWidth() - WIDTH) / 2;
int top = (minecraft.getWindow().getGuiScaledHeight() - HEIGHT) / 2;
RenderSystem.enableBlend();
RenderSystem.defaultBlendFunc();
drawHalf(guiGraphics, crosshair, left + 6, top, 0.0F, blueAvailable);
drawHalf(guiGraphics, crosshair, left + 11, top, HALF_WIDTH, orangeAvailable);
guiGraphics.setColor(1.0F, 1.0F, 1.0F, 1.0F);
RenderSystem.disableBlend();
}
private static void drawHalf(GuiGraphics guiGraphics, ResourceLocation crosshair, int x, int y, float u, boolean available) {
if (available) {
guiGraphics.setColor(1.0F, 1.0F, 1.0F, 1.0F);
} else {
guiGraphics.setColor(MISSING_BRIGHTNESS, MISSING_BRIGHTNESS, MISSING_BRIGHTNESS, MISSING_ALPHA);
}
guiGraphics.blit(crosshair, x, y, u, 0.0F, HALF_WIDTH, HEIGHT, WIDTH, HEIGHT);
}
private static Set<PortalGunPortalEntity.PortalSide> portalsFor(Minecraft minecraft, UUID gunId) {
if (gunId == null) {
return Set.of();
}
ClientLevel level = minecraft.level;
long gameTime = level.getGameTime();
if (cachedLevel != 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())) {
portals.add(portal.getPortalSide());
}
}
cachedLevel = level;
cachedGameTime = gameTime;
cachedGunId = gunId;
cachedPortals = Set.copyOf(portals);
}
return cachedPortals;
}
}
@@ -1,20 +0,0 @@
package com.craisinlord.antarchy.content.client;
public final class PortalGunGrabClientState {
private static boolean active;
private PortalGunGrabClientState() {
}
public static void update(boolean grabActive) {
active = grabActive;
}
public static boolean isActive() {
return active;
}
public static void clear() {
active = false;
}
}
@@ -2,20 +2,14 @@ package com.craisinlord.antarchy.content.client;
import com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity; import com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity;
import com.craisinlord.antarchy.content.portalgun.PortalGunTransformUtil; import com.craisinlord.antarchy.content.portalgun.PortalGunTransformUtil;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.Entity;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.Vec3;
public final class PortalGunRollClientState { public final class PortalGunRollClientState {
private static final double PORTAL_QUERY_RADIUS = 12.0D; private static final double PORTAL_QUERY_RADIUS = 12.0D;
private static final int TELEPORT_COOLDOWN_TICKS = 3;
private static final float ROLL_DECAY = 0.85F; private static final float ROLL_DECAY = 0.85F;
private static final float ROLL_EPSILON = 0.05F; private static final float ROLL_EPSILON = 0.05F;
private static final Map<UUID, Long> TELEPORT_COOLDOWNS = new HashMap<>();
private static float previousRollDegrees; private static float previousRollDegrees;
private static float rollDegrees; private static float rollDegrees;
@@ -34,10 +28,7 @@ public final class PortalGunRollClientState {
if (minecraft.level == null || entity == null) { if (minecraft.level == null || entity == null) {
return false; return false;
} }
for (PortalGunPortalEntity portal : minecraft.level.getEntitiesOfClass( for (PortalGunPortalEntity portal : com.craisinlord.antarchy.content.portalgun.PortalGunPortalRegistry.near(minecraft.level, entity.getBoundingBox().inflate(PORTAL_QUERY_RADIUS))) {
PortalGunPortalEntity.class,
entity.getBoundingBox().inflate(PORTAL_QUERY_RADIUS),
Entity::isAlive)) {
if (portal.intersectsEntityBounds(entity)) { if (portal.intersectsEntityBounds(entity)) {
return true; return true;
} }
@@ -53,7 +44,6 @@ public final class PortalGunRollClientState {
public static void clear() { public static void clear() {
previousRollDegrees = 0.0F; previousRollDegrees = 0.0F;
rollDegrees = 0.0F; rollDegrees = 0.0F;
TELEPORT_COOLDOWNS.clear();
} }
public static void tick(Minecraft minecraft) { public static void tick(Minecraft minecraft) {
@@ -61,74 +51,17 @@ public final class PortalGunRollClientState {
clear(); clear();
return; return;
} }
Entity cameraEntity = minecraft.getCameraEntity();
if (cameraEntity == null) {
return;
}
previousRollDegrees = rollDegrees; previousRollDegrees = rollDegrees;
rollDegrees *= ROLL_DECAY; rollDegrees *= ROLL_DECAY;
if (Math.abs(rollDegrees) < ROLL_EPSILON) { if (Math.abs(rollDegrees) < ROLL_EPSILON) {
rollDegrees = 0.0F; rollDegrees = 0.0F;
} }
long gameTime = minecraft.level.getGameTime();
TELEPORT_COOLDOWNS.entrySet().removeIf(entry -> entry.getValue() <= gameTime);
for (PortalGunPortalEntity portal : minecraft.level.getEntitiesOfClass(
PortalGunPortalEntity.class,
cameraEntity.getBoundingBox().inflate(PORTAL_QUERY_RADIUS),
Entity::isAlive)) {
PortalGunPortalEntity linkedPortal = portal.getLinkedPortal();
if (linkedPortal == null || !linkedPortal.isAlive()) {
continue;
}
if (!portal.shouldRenderFront(cameraEntity.position())) {
continue;
}
if (!canTeleport(cameraEntity, gameTime)) {
continue;
}
AABB currentBox = cameraEntity.getBoundingBox();
AABB previousBox = currentBox.move(cameraEntity.xo - cameraEntity.getX(), cameraEntity.yo - cameraEntity.getY(), cameraEntity.zo - cameraEntity.getZ());
PortalGunPortalEntity.PortalCrossing crossing = portal.resolveCrossing(cameraEntity, previousBox, currentBox);
if (crossing == null) {
continue;
}
applyPredictedTeleport(cameraEntity, portal, linkedPortal, crossing, gameTime);
break;
}
} }
private static boolean canTeleport(Entity entity, long gameTime) { public static void onTransit(PortalGunPortalEntity sourcePortal, PortalGunPortalEntity destinationPortal, Vec3 look) {
if (!entity.isAlive() || entity instanceof PortalGunPortalEntity || entity.isPassenger() || entity.isVehicle()) { Vec3 transformedLook = PortalGunTransformUtil.transformVector(sourcePortal, destinationPortal, look).normalize();
return false; Vec3 transformedUp = PortalGunTransformUtil.transformVector(sourcePortal, destinationPortal, PortalGunTransformUtil.upVectorFromLookAndRoll(look, rollDegrees)).normalize();
}
long cooldownUntil = TELEPORT_COOLDOWNS.getOrDefault(entity.getUUID(), 0L);
return cooldownUntil <= gameTime;
}
private static void applyPredictedTeleport(Entity entity, PortalGunPortalEntity sourcePortal, PortalGunPortalEntity destinationPortal, PortalGunPortalEntity.PortalCrossing crossing, long gameTime) {
Vec3 transformedLook = PortalGunTransformUtil.transformVector(sourcePortal, destinationPortal, entity.getLookAngle()).normalize();
Vec3 transformedUp = PortalGunTransformUtil.transformVector(sourcePortal, destinationPortal, PortalGunTransformUtil.upVectorFromLookAndRoll(entity.getLookAngle(), rollDegrees)).normalize();
Vec3 resolvedMovement = entity.position().subtract(entity.xo, entity.yo, entity.zo);
PortalGunPortalEntity.PortalTransitTransform transit = sourcePortal.previewTransit(entity, destinationPortal, crossing, resolvedMovement);
Vec3 transformedMotion = transit.movement();
transformedMotion = clampPortalMotion(transformedMotion);
rollDegrees = PortalGunTransformUtil.rollFromOrientation(transformedLook, transformedUp); rollDegrees = PortalGunTransformUtil.rollFromOrientation(transformedLook, transformedUp);
Vec3 exitPos = transit.exitPosition(); previousRollDegrees = rollDegrees;
entity.teleportTo(exitPos.x, exitPos.y, exitPos.z);
entity.setDeltaMovement(transformedMotion);
float verticalMotion = (float) transformedMotion.y;
entity.fallDistance = 0.1F * (verticalMotion / -0.1F * verticalMotion / -0.1F);
entity.setYRot(PortalGunTransformUtil.yawFromLook(transformedLook));
entity.setXRot(PortalGunTransformUtil.pitchFromLook(transformedLook));
entity.setPortalCooldown(TELEPORT_COOLDOWN_TICKS);
TELEPORT_COOLDOWNS.put(entity.getUUID(), gameTime + TELEPORT_COOLDOWN_TICKS);
}
private static Vec3 clampPortalMotion(Vec3 motion) {
return new Vec3(clampPortalMotionComponent(motion.x), clampPortalMotionComponent(motion.y), clampPortalMotionComponent(motion.z));
}
private static double clampPortalMotionComponent(double motion) {
return Math.abs(motion) > 0.99D ? motion / (Math.abs(motion) + 0.001D) : motion;
} }
} }
@@ -6,6 +6,7 @@ import net.minecraft.client.Options;
import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.ItemStack;
public final class PortalGunZoomClientState { public final class PortalGunZoomClientState {
private static final int MAX_PROGRESS = 5;
private static int progress; private static int progress;
private static boolean zooming; private static boolean zooming;
private static Double originalSensitivity; private static Double originalSensitivity;
@@ -34,7 +35,7 @@ public final class PortalGunZoomClientState {
zooming = false; zooming = false;
} }
progress = Math.max(0, Math.min(5, progress + (zooming ? 1 : -1))); progress = Math.max(0, Math.min(MAX_PROGRESS, progress + (zooming ? 1 : -1)));
if (originalSensitivity != null) { if (originalSensitivity != null) {
if (progress == 0) { if (progress == 0) {
restoreSensitivity(minecraft.options); restoreSensitivity(minecraft.options);
@@ -47,7 +48,13 @@ public final class PortalGunZoomClientState {
restoreSensitivity(options); restoreSensitivity(options);
return fov; return fov;
} }
double easedProgress = zooming ? progress - 1.0D + partialTick : progress + 1.0D - partialTick; double tickProgress = Math.max(0.0D, Math.min(1.0D, partialTick));
double easedProgress = progress == MAX_PROGRESS
? MAX_PROGRESS
: zooming
? progress - 1.0D + tickProgress
: progress + 1.0D - tickProgress;
easedProgress = Math.max(0.0D, Math.min(MAX_PROGRESS, easedProgress));
double zoomFactor = zoomFactor(easedProgress); double zoomFactor = zoomFactor(easedProgress);
if (originalSensitivity != null) { if (originalSensitivity != null) {
options.sensitivity().set(originalSensitivity * zoomFactor); options.sensitivity().set(originalSensitivity * zoomFactor);
@@ -56,7 +63,8 @@ public final class PortalGunZoomClientState {
} }
private static double zoomFactor(double progress) { private static double zoomFactor(double progress) {
return 0.1D + 0.9D * (1.0D - Math.sin(Math.toRadians(90.0D * Math.max(0.0D, Math.min(1.0D, progress / 5.0D))))); double normalizedProgress = Math.max(0.0D, Math.min(1.0D, progress / MAX_PROGRESS));
return 0.1D + 0.9D * (1.0D - Math.sin(Math.toRadians(90.0D * normalizedProgress)));
} }
private static double zoomFactor(int progress) { private static double zoomFactor(int progress) {
@@ -72,7 +72,7 @@ public final class ElythiaSkyRenderer {
float blue = (float) skyColor.z; float blue = (float) skyColor.z;
float rainLevel = level.getRainLevel(partialTick); float rainLevel = level.getRainLevel(partialTick);
float starBrightness = level.getStarBrightness(partialTick) * (1.0F - rainLevel); float starBrightness = level.getStarBrightness(partialTick) * (1.0F - rainLevel);
float timeOfDay = level.getTimeOfDay(partialTick); double timeDegrees = (level.getDayTime() + partialTick) * (360.0D / 24000.0D);
// Blend in a dark purple when it's night (starBrightness > 0) // Blend in a dark purple when it's night (starBrightness > 0)
float nightPurpleR = 0.055F; float nightPurpleR = 0.055F;
@@ -90,7 +90,7 @@ public final class ElythiaSkyRenderer {
RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F); RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F);
renderSunrise(poseStack, projectionMatrix, level, partialTick, rainLevel); renderSunrise(poseStack, projectionMatrix, level, partialTick, rainLevel);
renderCelestialBodies(poseStack.last().pose(), projectionMatrix, timeOfDay, rainLevel); renderCelestialBodies(poseStack.last().pose(), projectionMatrix, timeDegrees, rainLevel);
if (starBrightness > 0.1F) { if (starBrightness > 0.1F) {
if (coloredStarBuffer == null) { if (coloredStarBuffer == null) {
@@ -210,28 +210,31 @@ public final class ElythiaSkyRenderer {
RenderSystem.disableBlend(); RenderSystem.disableBlend();
} }
private static void renderCelestialBodies(Matrix4f modelViewMatrix, Matrix4f projectionMatrix, float timeOfDay, float rainLevel) { private static void renderCelestialBodies(Matrix4f modelViewMatrix, Matrix4f projectionMatrix, double timeDegrees, float rainLevel) {
float t = timeOfDay * 360.0F;
// Purple sun // Purple sun
float sun1Orbit = t; float sun1Orbit = orbitAngle(timeDegrees, 1.0D, 0.0D);
renderBodyGlow(modelViewMatrix, projectionMatrix, sun1Orbit, 0.0F, 0xD06BFF, rainLevel, 70.0F); renderBodyGlow(modelViewMatrix, projectionMatrix, sun1Orbit, 0.0F, 0xD06BFF, rainLevel, 70.0F);
renderTexturedBody(modelViewMatrix, projectionMatrix, sun1Orbit, 0.0F, 38.0F, 0xD06BFF, 1.0F, SUN_TEXTURE, false, 0, t * 0.8F); renderTexturedBody(modelViewMatrix, projectionMatrix, sun1Orbit, 0.0F, 38.0F, 0xD06BFF, 1.0F, SUN_TEXTURE, false, 0, orbitAngle(timeDegrees, 0.8D, 0.0D));
// Orange sub // Orange sub
float sun2Orbit = t * 0.73F + 54.0F; float sun2Orbit = orbitAngle(timeDegrees, 0.73D, 54.0D);
renderBodyGlow(modelViewMatrix, projectionMatrix, sun2Orbit, -14.0F, 0xFF9A33, rainLevel, 70.0F); renderBodyGlow(modelViewMatrix, projectionMatrix, sun2Orbit, -14.0F, 0xFF9A33, rainLevel, 70.0F);
renderTexturedBody(modelViewMatrix, projectionMatrix, sun2Orbit, -14.0F, 16.0F, 0xFF9A33, 1.0F, SUN_TEXTURE, false, 0, -t * 0.5F); renderTexturedBody(modelViewMatrix, projectionMatrix, sun2Orbit, -14.0F, 16.0F, 0xFF9A33, 1.0F, SUN_TEXTURE, false, 0, orbitAngle(timeDegrees, -0.5D, 0.0D));
// Dark green moon // Dark green moon
float moon2Orbit = t * 1.27F + 228.0F; float moon2Orbit = orbitAngle(timeDegrees, 1.27D, 228.0D);
renderBodyGlow(modelViewMatrix, projectionMatrix, moon2Orbit, 36.0F, 0x4FD84D, rainLevel, 78.0F); renderBodyGlow(modelViewMatrix, projectionMatrix, moon2Orbit, 36.0F, 0x4FD84D, rainLevel, 78.0F);
renderTexturedBody(modelViewMatrix, projectionMatrix, moon2Orbit, 36.0F, 14.0F, 0x4FD84D, 1.0F, MOON_TEXTURE, true, 1, t * 0.3F); renderTexturedBody(modelViewMatrix, projectionMatrix, moon2Orbit, 36.0F, 14.0F, 0x4FD84D, 1.0F, MOON_TEXTURE, true, 1, orbitAngle(timeDegrees, 0.3D, 0.0D));
// Light green moon // Light green moon
float moon3Orbit = t * 0.38F + 122.0F; float moon3Orbit = orbitAngle(timeDegrees, 0.38D, 122.0D);
renderBodyGlow(modelViewMatrix, projectionMatrix, moon3Orbit, -34.0F, 0xA3FF8F, rainLevel, 78.0F); renderBodyGlow(modelViewMatrix, projectionMatrix, moon3Orbit, -34.0F, 0xA3FF8F, rainLevel, 78.0F);
renderTexturedBody(modelViewMatrix, projectionMatrix, moon3Orbit, -34.0F, 12.0F, 0xA3FF8F, 1.0F, MOON_TEXTURE, true, 2, -t * 0.6F); renderTexturedBody(modelViewMatrix, projectionMatrix, moon3Orbit, -34.0F, 12.0F, 0xA3FF8F, 1.0F, MOON_TEXTURE, true, 2, orbitAngle(timeDegrees, -0.6D, 0.0D));
}
private static float orbitAngle(double timeDegrees, double speed, double offset) {
return (float) ((timeDegrees * speed + offset) % 360.0D);
} }
private static void renderBodyGlow( private static void renderBodyGlow(
@@ -15,8 +15,6 @@ import net.minecraft.world.effect.MobEffect;
import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.LivingEntity;
public final class GlimmeringLivingLayer<T extends LivingEntity, M extends EntityModel<T>> extends RenderLayer<T, M> { public final class GlimmeringLivingLayer<T extends LivingEntity, M extends EntityModel<T>> extends RenderLayer<T, M> {
// High-alpha, luminance-preserving tint drawn straight over the skin (no scale-up) so it
// reads as the body itself recolored spirit-blue rather than a separate ghost shell.
private static final int GLIMMERING_TINT = 0x4266CCFF; private static final int GLIMMERING_TINT = 0x4266CCFF;
public GlimmeringLivingLayer(RenderLayerParent<T, M> renderer) { public GlimmeringLivingLayer(RenderLayerParent<T, M> renderer) {
@@ -41,25 +41,25 @@ public class PortalGunPortalRenderer extends GeoEntityRenderer<PortalGunPortalEn
putAnimatedPortalVertex(consumer, pose, entity, portalScale, frameV, halfWidth, halfHeight, 1.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); putAnimatedPortalVertex(consumer, pose, entity, portalScale, frameV, halfWidth, -halfHeight, 1.0F, 1.0F);
} else { } else {
putPortalVertex(consumer, pose, entity, portalScale, -halfWidth, -halfHeight, 0.0F, 1.0F, false); putPortalVertex(consumer, pose, entity, portalScale, -halfWidth, -halfHeight, 0.0F, 1.0F, true);
putPortalVertex(consumer, pose, entity, portalScale, -halfWidth, halfHeight, 0.0F, 0.0F, false); putPortalVertex(consumer, pose, entity, portalScale, -halfWidth, halfHeight, 0.0F, 0.0F, true);
putPortalVertex(consumer, pose, entity, portalScale, halfWidth, halfHeight, 1.0F, 0.0F, false); putPortalVertex(consumer, pose, entity, portalScale, halfWidth, halfHeight, 1.0F, 0.0F, true);
putPortalVertex(consumer, pose, entity, portalScale, halfWidth, -halfHeight, 1.0F, 1.0F, false); putPortalVertex(consumer, pose, entity, portalScale, halfWidth, -halfHeight, 1.0F, 1.0F, true);
} }
VertexConsumer edge = bufferSource.getBuffer(RenderType.entityTranslucent(this.getTextureLocation(entity))); VertexConsumer edge = bufferSource.getBuffer(RenderType.entityTranslucent(PortalGunPortalRim.texture(entity)));
putPortalVertex(edge, pose, entity, portalScale, -halfWidth - 0.06F, -halfHeight - 0.07F, 0.0F, 1.0F, true); float rimWidth = halfWidth + PortalGunPortalRim.EXTRA_WIDTH;
putPortalVertex(edge, pose, entity, portalScale, -halfWidth - 0.06F, halfHeight + 0.07F, 0.0F, 0.0F, true); float rimHeight = halfHeight + PortalGunPortalRim.EXTRA_HEIGHT;
putPortalVertex(edge, pose, entity, portalScale, halfWidth + 0.06F, halfHeight + 0.07F, 1.0F, 0.0F, true); putPortalVertex(edge, pose, entity, portalScale, -rimWidth, -rimHeight, 0.0F, 1.0F, true);
putPortalVertex(edge, pose, entity, portalScale, halfWidth + 0.06F, -halfHeight - 0.07F, 1.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(); poseStack.popPose();
} }
private static ResourceLocation portalTexture(PortalGunPortalEntity entity) { private static ResourceLocation portalTexture(PortalGunPortalEntity entity) {
String color = entity.getPortalSide() == PortalGunPortalEntity.PortalSide.BLUE ? "blue" : "orange"; return isFancyPortalsEnabled()
String path = isFancyPortalsEnabled() ? entity.getVariant().vortexTexture(entity.getPortalSide())
? "textures/entity/portal_gun/portal_" + color + "_vortex.png" : PortalGunPortalRim.texture(entity);
: "textures/vfx/portal_gun_portal_" + color + ".png";
return ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, path);
} }
private static boolean isFancyPortalsEnabled() { private static boolean isFancyPortalsEnabled() {
@@ -84,10 +84,11 @@ public class PortalGunPortalRenderer extends GeoEntityRenderer<PortalGunPortalEn
Vec3 position = entity.getWidthVec().normalize().scale(horizontal * portalScale) Vec3 position = entity.getWidthVec().normalize().scale(horizontal * portalScale)
.add(entity.getUpVec().normalize().scale(vertical * portalScale)) .add(entity.getUpVec().normalize().scale(vertical * portalScale))
.add(entity.getNormalVec().normalize().scale(0.03125D)); .add(entity.getNormalVec().normalize().scale(0.03125D));
int red = channelTint ? entity.getChannelRed() : 255; int color = entity.getPortalColor();
int green = channelTint ? entity.getChannelGreen() : 255; int red = channelTint ? color >> 16 & 0xFF : 255;
int blue = channelTint ? entity.getChannelBlue() : 255; int green = channelTint ? color >> 8 & 0xFF : 255;
int alpha = channelTint ? 150 : 210; 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) consumer.addVertex(pose, (float) position.x, (float) position.y, (float) position.z)
.setColor(red, green, blue, alpha) .setColor(red, green, blue, alpha)
.setUv(u, v) .setUv(u, v)
@@ -0,0 +1,178 @@
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) {
if (minecraft.level == null || minecraft.player == null) {
return;
}
List<PortalGunPortalEntity> ownedPortals = collectOwnedPortals(minecraft);
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);
}
}
@@ -53,7 +53,7 @@ public final class PortalGunPortalSceneRenderer {
public static void render(Minecraft minecraft, Camera camera, float partialTick, Matrix4f viewMatrix, public static void render(Minecraft minecraft, Camera camera, float partialTick, Matrix4f viewMatrix,
Matrix4f projection, Matrix4f skyProjection, Frustum frustum, Matrix4f projection, Matrix4f skyProjection, Frustum frustum,
PortalTerrain terrain, Consumer<PoseStack> crossingEntities, String tracePair) { PortalTerrain terrain, Consumer<PoseStack> crossingEntities) {
LevelRenderer renderer = minecraft.levelRenderer; LevelRenderer renderer = minecraft.levelRenderer;
LevelRendererPortalViewAreaAccessor state = (LevelRendererPortalViewAreaAccessor) renderer; LevelRendererPortalViewAreaAccessor state = (LevelRendererPortalViewAreaAccessor) renderer;
LevelRendererPortalSceneInvoker scene = (LevelRendererPortalSceneInvoker) renderer; LevelRendererPortalSceneInvoker scene = (LevelRendererPortalSceneInvoker) renderer;
@@ -82,7 +82,7 @@ public final class PortalGunPortalSceneRenderer {
boolean foggy = minecraft.level.effects().isFoggyAt(cameraBlockPos.getX(), cameraBlockPos.getY()) boolean foggy = minecraft.level.effects().isFoggyAt(cameraBlockPos.getX(), cameraBlockPos.getY())
|| minecraft.gui.getBossOverlay().shouldCreateWorldFog(); || minecraft.gui.getBossOverlay().shouldCreateWorldFog();
try (PortalSceneRenderTrace.Phase ignored = PortalSceneRenderTrace.begin(tracePair, "background-and-sky", "sections=" + terrain.sectionCount())) { {
RenderSystem.setShaderGameTime(minecraft.level.getGameTime(), partialTick); RenderSystem.setShaderGameTime(minecraft.level.getGameTime(), partialTick);
minecraft.getBlockEntityRenderDispatcher().prepare(minecraft.level, camera, minecraft.hitResult); minecraft.getBlockEntityRenderDispatcher().prepare(minecraft.level, camera, minecraft.hitResult);
minecraft.getEntityRenderDispatcher().prepare(minecraft.level, camera, minecraft.crosshairPickEntity); minecraft.getEntityRenderDispatcher().prepare(minecraft.level, camera, minecraft.crosshairPickEntity);
@@ -99,7 +99,7 @@ public final class PortalGunPortalSceneRenderer {
RenderSystem.setProjectionMatrix(projection, VertexSorting.DISTANCE_TO_ORIGIN); RenderSystem.setProjectionMatrix(projection, VertexSorting.DISTANCE_TO_ORIGIN);
FogRenderer.setupFog(camera, FogRenderer.FogMode.FOG_TERRAIN, Math.max(renderDistance, 32.0F), foggy, partialTick); FogRenderer.setupFog(camera, FogRenderer.FogMode.FOG_TERRAIN, Math.max(renderDistance, 32.0F), foggy, partialTick);
try (PortalSceneRenderTrace.Phase ignored = PortalSceneRenderTrace.begin(tracePair, "opaque-terrain", "sections=" + terrain.sectionCount())) { {
RenderSystem.enableDepthTest(); RenderSystem.enableDepthTest();
RenderSystem.depthMask(true); RenderSystem.depthMask(true);
terrain.drawLayer(minecraft, RenderType.solid(), cameraPosition, viewMatrix, projection); terrain.drawLayer(minecraft, RenderType.solid(), cameraPosition, viewMatrix, projection);
@@ -118,7 +118,7 @@ public final class PortalGunPortalSceneRenderer {
PoseStack poseStack = new PoseStack(); PoseStack poseStack = new PoseStack();
MultiBufferSource.BufferSource buffers = bufferSource(); MultiBufferSource.BufferSource buffers = bufferSource();
try { try {
try (PortalSceneRenderTrace.Phase ignored = PortalSceneRenderTrace.begin(tracePair, "entities", "rendering")) { {
for (Entity entity : minecraft.level.entitiesForRendering()) { for (Entity entity : minecraft.level.entitiesForRendering()) {
if (!minecraft.getEntityRenderDispatcher().shouldRender(entity, frustum, cameraX, cameraY, cameraZ) if (!minecraft.getEntityRenderDispatcher().shouldRender(entity, frustum, cameraX, cameraY, cameraZ)
&& !entity.hasIndirectPassenger(minecraft.player)) { && !entity.hasIndirectPassenger(minecraft.player)) {
@@ -138,20 +138,20 @@ public final class PortalGunPortalSceneRenderer {
} }
buffers.endBatch(); buffers.endBatch();
try (PortalSceneRenderTrace.Phase ignored = PortalSceneRenderTrace.begin(tracePair, "block-entities", "rendering")) { {
terrain.forEachBlockEntity(minecraft, frustum, blockEntity -> terrain.forEachBlockEntity(minecraft, frustum, blockEntity ->
renderBlockEntity(minecraft, blockEntity, cameraX, cameraY, cameraZ, partialTick, poseStack, buffers)); renderBlockEntity(minecraft, blockEntity, cameraX, cameraY, cameraZ, partialTick, poseStack, buffers));
} }
buffers.endBatch(); buffers.endBatch();
try (PortalSceneRenderTrace.Phase ignored = PortalSceneRenderTrace.begin(tracePair, "translucent-and-particles", "rendering")) { {
terrain.drawLayer(minecraft, RenderType.translucent(), cameraPosition, viewMatrix, projection); terrain.drawLayer(minecraft, RenderType.translucent(), cameraPosition, viewMatrix, projection);
buffers.endBatch(); buffers.endBatch();
terrain.drawLayer(minecraft, RenderType.tripwire(), cameraPosition, viewMatrix, projection); terrain.drawLayer(minecraft, RenderType.tripwire(), cameraPosition, viewMatrix, projection);
minecraft.particleEngine.render(minecraft.gameRenderer.lightTexture(), camera, partialTick); minecraft.particleEngine.render(minecraft.gameRenderer.lightTexture(), camera, partialTick);
} }
try (PortalSceneRenderTrace.Phase ignored = PortalSceneRenderTrace.begin(tracePair, "weather-and-border", "rendering")) { {
RenderSystem.depthMask(false); RenderSystem.depthMask(false);
scene.antarchy$renderPortalWeather(minecraft.gameRenderer.lightTexture(), partialTick, cameraX, cameraY, cameraZ); scene.antarchy$renderPortalWeather(minecraft.gameRenderer.lightTexture(), partialTick, cameraX, cameraY, cameraZ);
scene.antarchy$renderPortalWorldBorder(camera); scene.antarchy$renderPortalWorldBorder(camera);
@@ -45,19 +45,17 @@ import org.joml.Vector4f;
import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL11;
public final class PortalGunPortalViewRenderer { public final class PortalGunPortalViewRenderer {
private static final int MAX_ROOT_VIEWS = 2;
private static final int MAX_VIEW_LEVELS = 2; private static final int MAX_VIEW_LEVELS = 2;
private static final int MAX_VIEWS_PER_FRAME = 4;
private static final double PORTAL_SEARCH_RADIUS = 128.0D; private static final double PORTAL_SEARCH_RADIUS = 128.0D;
private static final int PORTAL_TARGET_FILTER = 9729; private static final int PORTAL_TARGET_FILTER = 9729;
private static final double SURFACE_OFFSET = 0.04D; private static final double SURFACE_OFFSET = 0.04D;
private static final double EXIT_CLIP_OFFSET = 0.01D; 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 TextureTarget offscreenTarget;
private static ShaderInstance portalViewShader; private static ShaderInstance portalViewShader;
private static boolean renderingPortalView; private static boolean renderingPortalView;
private static boolean loggedRendererActivation;
private static boolean loggedMissingPortalViewShader; private static boolean loggedMissingPortalViewShader;
private static String lastPortalCandidateState = "";
private static long lastRenderFailureTick = Long.MIN_VALUE; private static long lastRenderFailureTick = Long.MIN_VALUE;
private PortalGunPortalViewRenderer() { private PortalGunPortalViewRenderer() {
@@ -65,7 +63,6 @@ public final class PortalGunPortalViewRenderer {
public static void setPortalViewShader(ShaderInstance shader) { public static void setPortalViewShader(ShaderInstance shader) {
portalViewShader = shader; portalViewShader = shader;
Antarchy.LOGGER.info("Portal gun view shader registered");
} }
public static void clearPortalViewShader() { public static void clearPortalViewShader() {
@@ -77,7 +74,6 @@ public final class PortalGunPortalViewRenderer {
} }
public static void releaseTargets() { public static void releaseTargets() {
PortalSceneRenderTrace.reset();
PortalGunPortalVisibilityQueries.clear(); PortalGunPortalVisibilityQueries.clear();
PortalGunPortalSectionViews.clear(); PortalGunPortalSectionViews.clear();
if (offscreenTarget != null) { if (offscreenTarget != null) {
@@ -87,18 +83,35 @@ public final class PortalGunPortalViewRenderer {
} }
public static void render(Camera camera, Matrix4f poseMatrix, Matrix4f projectionMatrix, DeltaTracker tickCounter) { public static void render(Camera camera, Matrix4f poseMatrix, Matrix4f projectionMatrix, DeltaTracker tickCounter) {
if (!isEnabled() || renderingPortalView) { if (renderingPortalView) {
return; return;
} }
if (isEnabled()) {
renderViews(camera, poseMatrix, projectionMatrix, tickCounter);
}
renderHiddenOutlines(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(); PortalRenderWatchdog.frame();
Minecraft minecraft = Minecraft.getInstance(); Minecraft minecraft = Minecraft.getInstance();
if (minecraft.level == null || minecraft.gameRenderer == null || camera == null || poseMatrix == null || projectionMatrix == null || tickCounter == null) { if (minecraft.level == null || minecraft.gameRenderer == null || camera == null || poseMatrix == null || projectionMatrix == null || tickCounter == null) {
return; return;
} }
if (!loggedRendererActivation) {
loggedRendererActivation = true;
Antarchy.LOGGER.info("Portal gun view renderer hook active (terrain: {})", SodiumCompat.isLoaded() ? "sodium" : "vanilla");
}
Entity cameraEntity = minecraft.getCameraEntity(); Entity cameraEntity = minecraft.getCameraEntity();
if (cameraEntity == null) { if (cameraEntity == null) {
return; return;
@@ -127,10 +140,10 @@ public final class PortalGunPortalViewRenderer {
} }
} }
candidates.sort((left, right) -> Integer.compare(right.clip().area(), left.clip().area())); candidates.sort((left, right) -> Integer.compare(right.clip().area(), left.clip().area()));
if (candidates.size() > MAX_ROOT_VIEWS) { int maxRootViews = AntarchySettings.portalGunMaxLiveViews();
candidates = candidates.subList(0, MAX_ROOT_VIEWS); if (candidates.size() > maxRootViews) {
candidates = candidates.subList(0, maxRootViews);
} }
logCandidateState(portals, candidates);
if (candidates.isEmpty()) { if (candidates.isEmpty()) {
return; return;
} }
@@ -147,7 +160,7 @@ public final class PortalGunPortalViewRenderer {
Vector3f rootLook = cameraToWorld.transformDirection(new Vector3f(0.0F, 0.0F, -1.0F)).normalize(); 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(); 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); RenderContext rootContext = new RenderContext(cameraPos, new Vec3(rootLook), new Vec3(rootUp), rootViewMatrix);
ViewBudget budget = new ViewBudget(MAX_VIEWS_PER_FRAME); ViewBudget budget = new ViewBudget(maxRootViews * 2);
PortalRenderWatchdog.portalViewDrawn(); PortalRenderWatchdog.portalViewDrawn();
renderingPortalView = true; renderingPortalView = true;
RenderSystem.backupProjectionMatrix(); RenderSystem.backupProjectionMatrix();
@@ -202,7 +215,6 @@ public final class PortalGunPortalViewRenderer {
if (!PortalTerrain.canView(minecraft, exit.center())) { if (!PortalTerrain.canView(minecraft, exit.center())) {
return; return;
} }
String pairKey = source.getUUID() + "->" + destination.getUUID();
boolean root = parentLevel == 0; boolean root = parentLevel == 0;
boolean visible = !root || PortalGunPortalVisibilityQueries.shouldRender(source.getUUID(), destination.getUUID()); boolean visible = !root || PortalGunPortalVisibilityQueries.shouldRender(source.getUUID(), destination.getUUID());
RenderTarget target = minecraft.getMainRenderTarget(); RenderTarget target = minecraft.getMainRenderTarget();
@@ -224,14 +236,21 @@ public final class PortalGunPortalViewRenderer {
Matrix4f clippedProjection = applyPortalClipPlane(rootProjection, transformed, destination); Matrix4f clippedProjection = applyPortalClipPlane(rootProjection, transformed, destination);
RenderSystem.enableScissor(clip.x(), clip.y(), clip.width(), clip.height()); RenderSystem.enableScissor(clip.x(), clip.y(), clip.width(), clip.height());
renderScene(minecraft, cameraEntity, partialTick, transformed, rootProjection, clippedProjection, renderScene(minecraft, cameraEntity, partialTick, transformed, rootProjection, clippedProjection,
source, destination, clip, target.width, target.height, parentLevel + 1, pairKey); source, destination, clip, target.width, target.height, parentLevel + 1);
if (parentLevel + 1 < MAX_VIEW_LEVELS) { if (parentLevel + 1 < MAX_VIEW_LEVELS) {
renderNestedViews(minecraft, cameraEntity, partialTick, transformed, rootProjection, clippedProjection, renderNestedViews(minecraft, cameraEntity, partialTick, transformed, rootProjection, clippedProjection,
destination, parentLevel + 1, clip, budget, target.width, target.height); destination, parentLevel + 1, clip, budget, target.width, target.height);
} }
} finally { } finally {
PortalStencilTarget.popAperture(parentLevel, try {
() -> restorePortalApertureDepth(context.cameraPos(), context.viewMatrix(), parentProjection, source, partialTick)); 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) { if (parentClip == null) {
RenderSystem.disableScissor(); RenderSystem.disableScissor();
} else { } else {
@@ -275,7 +294,6 @@ public final class PortalGunPortalViewRenderer {
TextureTarget target = ensureOffscreenTarget(mainTarget.width, mainTarget.height); TextureTarget target = ensureOffscreenTarget(mainTarget.width, mainTarget.height);
RenderContext transformed = transformContext(context, source, destination); RenderContext transformed = transformContext(context, source, destination);
Matrix4f clippedProjection = applyPortalClipPlane(rootProjection, transformed, destination); Matrix4f clippedProjection = applyPortalClipPlane(rootProjection, transformed, destination);
String pairKey = source.getUUID() + "->" + destination.getUUID();
MinecraftMainRenderTargetAccessor targetAccessor = (MinecraftMainRenderTargetAccessor) minecraft; MinecraftMainRenderTargetAccessor targetAccessor = (MinecraftMainRenderTargetAccessor) minecraft;
targetAccessor.antarchy$setMainRenderTarget(target); targetAccessor.antarchy$setMainRenderTarget(target);
try { try {
@@ -285,7 +303,7 @@ public final class PortalGunPortalViewRenderer {
target.bindWrite(true); target.bindWrite(true);
RenderSystem.enableScissor(clip.x(), clip.y(), clip.width(), clip.height()); RenderSystem.enableScissor(clip.x(), clip.y(), clip.width(), clip.height());
renderScene(minecraft, cameraEntity, partialTick, transformed, rootProjection, clippedProjection, renderScene(minecraft, cameraEntity, partialTick, transformed, rootProjection, clippedProjection,
source, destination, clip, target.width, target.height, 1, pairKey); source, destination, clip, target.width, target.height, 1);
} finally { } finally {
RenderSystem.disableScissor(); RenderSystem.disableScissor();
targetAccessor.antarchy$setMainRenderTarget(mainTarget); targetAccessor.antarchy$setMainRenderTarget(mainTarget);
@@ -298,7 +316,7 @@ public final class PortalGunPortalViewRenderer {
private static void renderScene(Minecraft minecraft, Entity cameraEntity, float partialTick, RenderContext transformed, private static void renderScene(Minecraft minecraft, Entity cameraEntity, float partialTick, RenderContext transformed,
Matrix4f rootProjection, Matrix4f clippedProjection, Matrix4f rootProjection, Matrix4f clippedProjection,
PortalGunPortalEntity source, PortalGunPortalEntity destination, PortalGunPortalEntity source, PortalGunPortalEntity destination,
ScreenClip clip, int width, int height, int level, String pairKey) { ScreenClip clip, int width, int height, int level) {
PortalGunWorldPortalShape exit = destination.getWorldPortalShape(); PortalGunWorldPortalShape exit = destination.getWorldPortalShape();
PortalCamera portalCamera = new PortalCamera(); PortalCamera portalCamera = new PortalCamera();
portalCamera.setup(minecraft.level, cameraEntity, partialTick, transformed.cameraPos(), transformed.look(), transformed.up()); portalCamera.setup(minecraft.level, cameraEntity, partialTick, transformed.cameraPos(), transformed.look(), transformed.up());
@@ -308,42 +326,26 @@ public final class PortalGunPortalViewRenderer {
source.getWorldPortalShape(), exit, transformed.cameraPos(), transformed.look(), transformed.up(), source.getWorldPortalShape(), exit, transformed.cameraPos(), transformed.look(), transformed.up(),
transformed.viewMatrix(), clippedProjection, level, false, destination.getUUID())); transformed.viewMatrix(), clippedProjection, level, false, destination.getUUID()));
try { try {
PortalTerrain terrain; PortalTerrain terrain = PortalTerrain.prepare(minecraft, destination.getUUID(), exit.center(), exit.normal(), frustum);
try (PortalSceneRenderTrace.Phase ignored = PortalSceneRenderTrace.begin(pairKey, "section-visibility", "level=" + level)) {
terrain = PortalTerrain.prepare(minecraft, destination.getUUID(), exit.center(), exit.normal(), frustum);
}
PortalGunPortalSceneRenderer.render(minecraft, portalCamera, partialTick, transformed.viewMatrix(), clippedProjection, PortalGunPortalSceneRenderer.render(minecraft, portalCamera, partialTick, transformed.viewMatrix(), clippedProjection,
rootProjection, frustum, terrain, rootProjection, frustum, terrain,
poseStack -> renderCrossingEntities(minecraft, transformed.cameraPos(), partialTick, source, destination, poseStack), poseStack -> renderCrossingEntities(minecraft, transformed.cameraPos(), partialTick, source, destination, poseStack));
pairKey);
} finally { } finally {
PortalGunPortalRenderState.popPortalView(false); PortalGunPortalRenderState.popPortalView(false);
} }
} }
private static List<PortalGunPortalEntity> collectPortals(Minecraft minecraft, Vec3 cameraPos) { private static List<PortalGunPortalEntity> collectPortals(Minecraft minecraft, Vec3 cameraPos) {
List<PortalGunPortalEntity> portals = minecraft.level.getEntitiesOfClass(PortalGunPortalEntity.class, List<PortalGunPortalEntity> portals = new ArrayList<>();
new AABB(cameraPos, cameraPos).inflate(PORTAL_SEARCH_RADIUS), Entity::isAlive); for (PortalGunPortalEntity portal : com.craisinlord.antarchy.content.portalgun.PortalGunPortalRegistry.all(minecraft.level)) {
portals.removeIf(portal -> portal.distanceToSqr(cameraPos) > PORTAL_SEARCH_RADIUS * PORTAL_SEARCH_RADIUS); 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))); portals.sort((left, right) -> Double.compare(left.distanceToSqr(cameraPos), right.distanceToSqr(cameraPos)));
return portals; return portals;
} }
private static void logCandidateState(List<PortalGunPortalEntity> portals, List<RootCandidate> candidates) {
String state = portals.size() + "|" + candidates.stream()
.map(candidate -> candidate.portal().getUUID() + "->" + candidate.linked().getUUID())
.sorted()
.toList();
if (state.equals(lastPortalCandidateState)) {
return;
}
lastPortalCandidateState = state;
if (!candidates.isEmpty()) {
PortalSceneRenderTrace.arm(state);
}
Antarchy.LOGGER.info("Portal gun view candidates changed portals={} selected={}", portals.size(), candidates.size());
}
private static void logRenderFailure(Minecraft minecraft, RuntimeException exception) { private static void logRenderFailure(Minecraft minecraft, RuntimeException exception) {
long gameTime = minecraft.level == null ? Long.MIN_VALUE : minecraft.level.getGameTime(); long gameTime = minecraft.level == null ? Long.MIN_VALUE : minecraft.level.getGameTime();
if (lastRenderFailureTick == Long.MIN_VALUE || gameTime == Long.MIN_VALUE || gameTime - lastRenderFailureTick >= 100L) { if (lastRenderFailureTick == Long.MIN_VALUE || gameTime == Long.MIN_VALUE || gameTime - lastRenderFailureTick >= 100L) {
@@ -382,8 +384,7 @@ public final class PortalGunPortalViewRenderer {
private static ScreenClip computeScreenClip(Vec3 cameraPos, Matrix4f viewMatrix, Matrix4f projectionMatrix, private static ScreenClip computeScreenClip(Vec3 cameraPos, Matrix4f viewMatrix, Matrix4f projectionMatrix,
PortalGunPortalEntity portal, float partialTick, int width, int height) { PortalGunPortalEntity portal, float partialTick, int width, int height) {
List<Vector4f> clipVertices = projectPortalPolygon(new Matrix4f(projectionMatrix).mul(viewMatrix), cameraPos, List<Vector4f> clipVertices = projectPortalPolygon(new Matrix4f(projectionMatrix).mul(viewMatrix), cameraPos, portal, partialTick);
portal.getWorldPortalShape().scaleAperture(portal.getPortalVisualScale(partialTick)));
if (clipVertices.size() < 3) { if (clipVertices.size() < 3) {
return null; return null;
} }
@@ -456,8 +457,8 @@ public final class PortalGunPortalViewRenderer {
return nearPlane ? vertex.z + vertex.w : vertex.w - 0.001F; return nearPlane ? vertex.z + vertex.w : vertex.w - 0.001F;
} }
private static List<Vector4f> projectPortalPolygon(Matrix4f combined, Vec3 cameraPos, PortalGunWorldPortalShape shape) { private static List<Vector4f> projectPortalPolygon(Matrix4f combined, Vec3 cameraPos, PortalGunPortalEntity portal, float partialTick) {
Vec3[] corners = shape.getCorners(SURFACE_OFFSET); Vec3[] corners = apertureOutline(portal, partialTick);
List<Vector4f> polygon = new ArrayList<>(corners.length); List<Vector4f> polygon = new ArrayList<>(corners.length);
for (Vec3 corner : corners) { for (Vec3 corner : corners) {
Vec3 relative = corner.subtract(cameraPos); Vec3 relative = corner.subtract(cameraPos);
@@ -467,10 +468,17 @@ public final class PortalGunPortalViewRenderer {
return clipPolygon(polygon, true); 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, private static MeshData buildAperture(Vec3 cameraPos, Matrix4f viewMatrix, Matrix4f projectionMatrix,
PortalGunPortalEntity portal, float partialTick, boolean withScreenUv) { PortalGunPortalEntity portal, float partialTick, boolean withScreenUv) {
PortalGunWorldPortalShape shape = portal.getWorldPortalShape().scaleAperture(portal.getPortalVisualScale(partialTick)); List<Vector4f> polygon = projectPortalPolygon(new Matrix4f(projectionMatrix).mul(viewMatrix), cameraPos, portal, partialTick);
List<Vector4f> polygon = projectPortalPolygon(new Matrix4f(projectionMatrix).mul(viewMatrix), cameraPos, shape);
if (polygon.size() < 3) { if (polygon.size() < 3) {
return null; return null;
} }
@@ -22,7 +22,6 @@ public final class PortalGunPortalVisibilityQueries {
state.visible = GL15.glGetQueryObjecti(state.query, GL15.GL_QUERY_RESULT) != 0; state.visible = GL15.glGetQueryObjecti(state.query, GL15.GL_QUERY_RESULT) != 0;
state.hasResult = true; state.hasResult = true;
state.inFlight = false; state.inFlight = false;
PortalSceneRenderTrace.event(source + "->" + destination, "visibility-query-result", "visible=" + state.visible);
} }
return !state.hasResult || state.visible; return !state.hasResult || state.visible;
} }
@@ -4,6 +4,7 @@ import com.craisinlord.antarchy.Antarchy;
import com.craisinlord.antarchy.content.portalgun.PortalGunProjectileEntity; import com.craisinlord.antarchy.content.portalgun.PortalGunProjectileEntity;
import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer; import com.mojang.blaze3d.vertex.VertexConsumer;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.MultiBufferSource; import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.entity.EntityRenderer; import net.minecraft.client.renderer.entity.EntityRenderer;
@@ -11,11 +12,25 @@ import net.minecraft.client.renderer.entity.EntityRendererProvider;
import net.minecraft.client.renderer.texture.OverlayTexture; import net.minecraft.client.renderer.texture.OverlayTexture;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.Mth;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.HumanoidArm;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.phys.Vec3;
import org.joml.Matrix4f; import org.joml.Matrix4f;
public class PortalGunProjectileRenderer extends EntityRenderer<PortalGunProjectileEntity> { public class PortalGunProjectileRenderer extends EntityRenderer<PortalGunProjectileEntity> {
private static final ResourceLocation BLUE_TEXTURE = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "textures/vfx/portal_gun_portal_blue.png"); private static final ResourceLocation SHOT_TEXTURE = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "textures/vfx/portal_gun_shot.png");
private static final ResourceLocation ORANGE_TEXTURE = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "textures/vfx/portal_gun_portal_orange.png"); private static final float CORE_SIZE = 0.16F;
private static final float GLOW_SIZE = 0.34F;
private static final float STREAK_WIDTH = 0.045F;
private static final double STREAK_LENGTH = 2.2D;
private static final float MUZZLE_TICKS = 4.0F;
private static final double MUZZLE_FORWARD = 0.55D;
private static final double MUZZLE_SIDE = 0.32D;
private static final double MUZZLE_DOWN = 0.28D;
private static final double HIDDEN_DISTANCE = 0.9D;
private static final double FADE_DISTANCE = 1.8D;
public PortalGunProjectileRenderer(EntityRendererProvider.Context context) { public PortalGunProjectileRenderer(EntityRendererProvider.Context context) {
super(context); super(context);
@@ -23,37 +38,103 @@ public class PortalGunProjectileRenderer extends EntityRenderer<PortalGunProject
@Override @Override
public void render(PortalGunProjectileEntity entity, float entityYaw, float partialTick, PoseStack poseStack, MultiBufferSource bufferSource, int packedLight) { public void render(PortalGunProjectileEntity entity, float entityYaw, float partialTick, PoseStack poseStack, MultiBufferSource bufferSource, int packedLight) {
poseStack.pushPose(); Vec3 entityPos = entity.getPosition(partialTick);
poseStack.mulPose(this.entityRenderDispatcher.cameraOrientation()); Vec3 renderPos = this.muzzleBlendedPosition(entity, entityPos, partialTick);
float age = entity.tickCount + partialTick; Vec3 cameraPos = this.entityRenderDispatcher.camera.getPosition();
float syncedDistance = entity.getSyncedDistance() + partialTick; double cameraDistance = renderPos.distanceTo(cameraPos);
float pulse = 0.8F + 0.2F * (float) Math.sin(age * 0.9F); if (cameraDistance < HIDDEN_DISTANCE) {
float travelPulse = 0.82F + 0.18F * (float) Math.sin(syncedDistance * 0.55F); return;
float trailStretch = 1.1F + (float) Math.min(entity.getSyncedVelocity().length() * 0.42D, 1.8D); }
float baseScale = entity.getPortalSide() == com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity.PortalSide.BLUE ? 0.44F : 0.48F; float fade = (float) Mth.clamp((cameraDistance - HIDDEN_DISTANCE) / (FADE_DISTANCE - HIDDEN_DISTANCE), 0.0D, 1.0D);
ResourceLocation texture = entity.getPortalSide() == com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity.PortalSide.BLUE ? BLUE_TEXTURE : ORANGE_TEXTURE; int color = entity.getVariant().color(entity.getPortalSide());
float red = entity.getPortalSide() == com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity.PortalSide.BLUE ? 0.55F : 1.0F; float red = (color >> 16 & 0xFF) / 255.0F;
float green = entity.getPortalSide() == com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity.PortalSide.BLUE ? 0.82F : 0.56F; float green = (color >> 8 & 0xFF) / 255.0F;
float blue = entity.getPortalSide() == com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity.PortalSide.BLUE ? 1.0F : 0.18F; float blue = (color & 0xFF) / 255.0F;
poseStack.scale(baseScale * trailStretch, baseScale * travelPulse, baseScale * travelPulse); poseStack.pushPose();
Vec3 offset = renderPos.subtract(entityPos);
poseStack.translate(offset.x, offset.y, offset.z);
this.renderStreak(entity, renderPos, cameraPos, poseStack, bufferSource, red, green, blue, fade, partialTick);
poseStack.mulPose(this.entityRenderDispatcher.cameraOrientation());
PoseStack.Pose pose = poseStack.last(); PoseStack.Pose pose = poseStack.last();
Matrix4f matrix = pose.pose(); Matrix4f matrix = pose.pose();
VertexConsumer core = bufferSource.getBuffer(RenderType.entityTranslucent(texture)); VertexConsumer glow = bufferSource.getBuffer(RenderType.entityTranslucentEmissive(SHOT_TEXTURE));
addQuad(core, pose, matrix, red, green, blue, 0.92F, packedLight, -0.9F, -0.5F, 0.9F, 0.5F); addQuad(glow, pose, matrix, red, green, blue, 0.55F * fade, GLOW_SIZE);
VertexConsumer glow = bufferSource.getBuffer(RenderType.entityTranslucentEmissive(texture)); addQuad(glow, pose, matrix, 1.0F, 1.0F, 1.0F, 0.9F * fade, CORE_SIZE);
addQuad(glow, pose, matrix, 1.0F, 1.0F, 1.0F, 0.8F * pulse, 0xF000F0, -1.15F, -0.7F, 1.15F, 0.7F);
poseStack.mulPose(com.mojang.math.Axis.ZP.rotationDegrees(90.0F));
pose = poseStack.last();
matrix = pose.pose();
core = bufferSource.getBuffer(RenderType.entityTranslucent(texture));
addQuad(core, pose, matrix, red, green, blue, 0.74F, packedLight, -0.45F, -0.95F, 0.45F, 0.95F);
glow = bufferSource.getBuffer(RenderType.entityTranslucentEmissive(texture));
addQuad(glow, pose, matrix, 1.0F, 1.0F, 1.0F, 0.52F * pulse, 0xF000F0, -0.62F, -1.15F, 0.62F, 1.15F);
poseStack.popPose(); poseStack.popPose();
} }
private void renderStreak(PortalGunProjectileEntity entity, Vec3 renderPos, Vec3 cameraPos, PoseStack poseStack, MultiBufferSource bufferSource,
float red, float green, float blue, float fade, float partialTick) {
Vec3 velocity = entity.getSyncedVelocity();
if (velocity.lengthSqr() < 1.0E-6D) {
velocity = entity.getDeltaMovement();
}
if (velocity.lengthSqr() < 1.0E-6D) {
return;
}
Vec3 direction = velocity.normalize();
Vec3 toCamera = cameraPos.subtract(renderPos);
Vec3 side = direction.cross(toCamera);
if (side.lengthSqr() < 1.0E-6D) {
return;
}
side = side.normalize().scale(STREAK_WIDTH);
double travelled = entity.getSyncedSpawnPosition().distanceTo(renderPos);
double length = Math.min(STREAK_LENGTH, Math.max(0.0D, travelled - HIDDEN_DISTANCE));
if (length <= 0.05D) {
return;
}
Vec3 tail = direction.scale(-length);
Matrix4f matrix = poseStack.last().pose();
VertexConsumer streak = bufferSource.getBuffer(RenderType.lightning());
float headAlpha = 0.7F * fade;
Vec3 tailLeft = tail.add(side.scale(0.3D));
Vec3 tailRight = tail.subtract(side.scale(0.3D));
Vec3 headLeft = side;
Vec3 headRight = side.scale(-1.0D);
addStreakVertex(streak, matrix, headLeft, red, green, blue, headAlpha);
addStreakVertex(streak, matrix, tailLeft, red, green, blue, 0.0F);
addStreakVertex(streak, matrix, tailRight, red, green, blue, 0.0F);
addStreakVertex(streak, matrix, headRight, red, green, blue, headAlpha);
addStreakVertex(streak, matrix, headRight, red, green, blue, headAlpha);
addStreakVertex(streak, matrix, tailRight, red, green, blue, 0.0F);
addStreakVertex(streak, matrix, tailLeft, red, green, blue, 0.0F);
addStreakVertex(streak, matrix, headLeft, red, green, blue, headAlpha);
}
private static void addStreakVertex(VertexConsumer consumer, Matrix4f matrix, Vec3 position, float red, float green, float blue, float alpha) {
consumer.addVertex(matrix, (float) position.x, (float) position.y, (float) position.z).setColor(red, green, blue, alpha);
}
private Vec3 muzzleBlendedPosition(PortalGunProjectileEntity entity, Vec3 entityPos, float partialTick) {
float age = entity.tickCount + partialTick;
if (age >= MUZZLE_TICKS) {
return entityPos;
}
Entity shooter = Minecraft.getInstance().level == null ? null : Minecraft.getInstance().level.getEntity(entity.getShooterId());
if (!(shooter instanceof LivingEntity livingShooter)) {
return entityPos;
}
Vec3 eye = livingShooter.getEyePosition(partialTick);
Vec3 look = livingShooter.getViewVector(partialTick);
Vec3 right = look.cross(new Vec3(0.0D, 1.0D, 0.0D));
right = right.lengthSqr() < 1.0E-6D ? new Vec3(1.0D, 0.0D, 0.0D) : right.normalize();
Vec3 down = right.cross(look).normalize().scale(-1.0D);
boolean rightHanded = livingShooter.getMainArm() == HumanoidArm.RIGHT;
boolean offhand = !(livingShooter.getMainHandItem().getItem() instanceof com.craisinlord.antarchy.content.item.PortalGunItem)
&& livingShooter.getOffhandItem().getItem() instanceof com.craisinlord.antarchy.content.item.PortalGunItem;
double sideSign = rightHanded != offhand ? 1.0D : -1.0D;
Vec3 muzzle = eye.add(look.scale(MUZZLE_FORWARD)).add(right.scale(MUZZLE_SIDE * sideSign)).add(down.scale(MUZZLE_DOWN));
double blend = Mth.clamp(age / MUZZLE_TICKS, 0.0F, 1.0F);
return muzzle.lerp(entityPos, blend * blend);
}
@Override
public boolean shouldRender(PortalGunProjectileEntity entity, net.minecraft.client.renderer.culling.Frustum frustum, double cameraX, double cameraY, double cameraZ) {
return true;
}
@Override @Override
protected int getBlockLightLevel(PortalGunProjectileEntity entity, BlockPos pos) { protected int getBlockLightLevel(PortalGunProjectileEntity entity, BlockPos pos) {
return 15; return 15;
@@ -61,22 +142,22 @@ public class PortalGunProjectileRenderer extends EntityRenderer<PortalGunProject
@Override @Override
public ResourceLocation getTextureLocation(PortalGunProjectileEntity entity) { public ResourceLocation getTextureLocation(PortalGunProjectileEntity entity) {
return entity.getPortalSide() == com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity.PortalSide.BLUE ? BLUE_TEXTURE : ORANGE_TEXTURE; return SHOT_TEXTURE;
} }
private static void addQuad(VertexConsumer consumer, PoseStack.Pose pose, Matrix4f matrix, float red, float green, float blue, float alpha, int packedLight, float minX, float minY, float maxX, float maxY) { private static void addQuad(VertexConsumer consumer, PoseStack.Pose pose, Matrix4f matrix, float red, float green, float blue, float alpha, float halfSize) {
addVertex(consumer, pose, matrix, minX, minY, 0.0F, 1.0F, red, green, blue, alpha, packedLight); addVertex(consumer, pose, matrix, -halfSize, -halfSize, 0.0F, 1.0F, red, green, blue, alpha);
addVertex(consumer, pose, matrix, maxX, minY, 1.0F, 1.0F, red, green, blue, alpha, packedLight); addVertex(consumer, pose, matrix, halfSize, -halfSize, 1.0F, 1.0F, red, green, blue, alpha);
addVertex(consumer, pose, matrix, maxX, maxY, 1.0F, 0.0F, red, green, blue, alpha, packedLight); addVertex(consumer, pose, matrix, halfSize, halfSize, 1.0F, 0.0F, red, green, blue, alpha);
addVertex(consumer, pose, matrix, minX, maxY, 0.0F, 0.0F, red, green, blue, alpha, packedLight); addVertex(consumer, pose, matrix, -halfSize, halfSize, 0.0F, 0.0F, red, green, blue, alpha);
} }
private static void addVertex(VertexConsumer consumer, PoseStack.Pose pose, Matrix4f matrix, float x, float y, float u, float v, float red, float green, float blue, float alpha, int packedLight) { private static void addVertex(VertexConsumer consumer, PoseStack.Pose pose, Matrix4f matrix, float x, float y, float u, float v, float red, float green, float blue, float alpha) {
consumer.addVertex(matrix, x, y, 0.0F) consumer.addVertex(matrix, x, y, 0.0F)
.setColor(red, green, blue, alpha) .setColor(red, green, blue, alpha)
.setUv(u, v) .setUv(u, v)
.setOverlay(OverlayTexture.NO_OVERLAY) .setOverlay(OverlayTexture.NO_OVERLAY)
.setLight(packedLight) .setLight(0xF000F0)
.setNormal(pose, 0.0F, 0.0F, 1.0F); .setNormal(pose, 0.0F, 0.0F, 1.0F);
} }
} }
@@ -1,91 +0,0 @@
package com.craisinlord.antarchy.content.client.renderer;
import com.craisinlord.antarchy.Antarchy;
import java.util.HashSet;
import java.util.Set;
public final class PortalSceneRenderTrace {
private static final String DEBUG_PROPERTY = "antarchy.portalGun.debugRender";
private static final long SLOW_PHASE_NANOS = 100_000_000L;
private static final Set<String> LOGGED_PHASES = new HashSet<>();
private static long generation;
private static String armedKey = "";
private PortalSceneRenderTrace() {
}
public static synchronized void arm(String key) {
if (!armedKey.equals(key)) {
armedKey = key;
generation++;
LOGGED_PHASES.clear();
}
}
public static synchronized void reset() {
armedKey = "";
LOGGED_PHASES.clear();
generation++;
}
public static Phase begin(String pair, String phase, String detail) {
boolean verbose = Boolean.getBoolean(DEBUG_PROPERTY);
long currentGeneration;
boolean logStart;
synchronized (PortalSceneRenderTrace.class) {
currentGeneration = generation;
logStart = LOGGED_PHASES.add(currentGeneration + ":" + pair + ":" + phase);
}
if (logStart) {
Antarchy.LOGGER.info("Portal scene trace begin generation={} pair={} phase={} detail={}", currentGeneration, pair, phase, detail);
}
return new Phase(currentGeneration, pair, phase, logStart, verbose, System.nanoTime());
}
public static void event(String pair, String event, String detail) {
if (!Boolean.getBoolean(DEBUG_PROPERTY)) {
return;
}
long currentGeneration;
boolean shouldLog;
synchronized (PortalSceneRenderTrace.class) {
currentGeneration = generation;
shouldLog = LOGGED_PHASES.add(currentGeneration + ":" + pair + ":event:" + event);
}
if (shouldLog) {
Antarchy.LOGGER.info("Portal scene trace event generation={} pair={} event={} detail={}", currentGeneration, pair, event, detail);
}
}
public static final class Phase implements AutoCloseable {
private final long generation;
private final String pair;
private final String phase;
private final boolean logStart;
private final boolean verbose;
private final long startNanos;
private boolean closed;
private Phase(long generation, String pair, String phase, boolean logStart, boolean verbose, long startNanos) {
this.generation = generation;
this.pair = pair;
this.phase = phase;
this.logStart = logStart;
this.verbose = verbose;
this.startNanos = startNanos;
}
@Override
public void close() {
if (this.closed) {
return;
}
this.closed = true;
long durationNanos = System.nanoTime() - this.startNanos;
if (this.logStart && (this.verbose || durationNanos >= SLOW_PHASE_NANOS)) {
Antarchy.LOGGER.info("Portal scene trace complete generation={} pair={} phase={} durationMs={}",
this.generation, this.pair, this.phase, durationNanos / 1_000_000.0D);
}
}
}
}
@@ -41,7 +41,7 @@ public final class PortalStencilTarget {
ensureMainStencilAttachment(target); ensureMainStencilAttachment(target);
} catch (IllegalStateException exception) { } catch (IllegalStateException exception) {
UNSUPPORTED_MAIN_TARGETS.put(target, target.frameBufferId); UNSUPPORTED_MAIN_TARGETS.put(target, target.frameBufferId);
Antarchy.LOGGER.warn("Portal gun direct stencil is unavailable; using offscreen portal rendering", exception); Antarchy.LOGGER.debug("Portal gun direct stencil is unavailable ({}); using offscreen portal rendering", exception.getMessage());
} }
} }
} }
@@ -150,7 +150,6 @@ public final class PortalStencilTarget {
throw new IllegalStateException("Main render target is incomplete with stencil attachment: 0x" + Integer.toHexString(status)); 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)); MAIN_STENCIL_ATTACHMENTS.put(target, new Attachment(target.frameBufferId, renderbuffer, target.width, target.height));
PortalSceneRenderTrace.event("main-target", "stencil-attached", "framebuffer=" + target.frameBufferId + " size=" + target.width + "x" + target.height);
} catch (RuntimeException | Error exception) { } catch (RuntimeException | Error exception) {
GL30.glDeleteRenderbuffers(renderbuffer); GL30.glDeleteRenderbuffers(renderbuffer);
throw exception; throw exception;
@@ -101,8 +101,6 @@ public final class RoyalIndicatorRenderer {
} }
private static void judgmentFlare(VertexConsumer out, PoseStack.Pose pose, float radius, float rotation) { private static void judgmentFlare(VertexConsumer out, PoseStack.Pose pose, float radius, float rotation) {
// The old mark was almost edge-on from normal gameplay camera angles. Add a vertical,
// unmistakable judgment sigil while keeping the rotating ring as its core.
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
double angle = rotation + Math.PI * 0.5D * i; double angle = rotation + Math.PI * 0.5D * i;
float x = (float) Math.cos(angle) * radius; float x = (float) Math.cos(angle) * radius;
@@ -4,7 +4,6 @@ import com.craisinlord.antarchy.content.AntarchyObjects;
import com.craisinlord.antarchy.content.entity.glimmer.GlimmerVariant; import com.craisinlord.antarchy.content.entity.glimmer.GlimmerVariant;
import com.craisinlord.antarchy.content.item.GiantFryingPanToolHelper; import com.craisinlord.antarchy.content.item.GiantFryingPanToolHelper;
import com.craisinlord.antarchy.content.item.GlimmerBottleItem; import com.craisinlord.antarchy.content.item.GlimmerBottleItem;
import com.craisinlord.antarchy.content.item.PortalGunItem;
import com.craisinlord.antarchy.content.item.royal.RoyalGearHelper; import com.craisinlord.antarchy.content.item.royal.RoyalGearHelper;
import com.craisinlord.antarchy.content.item.ultimate.UltimateGearHelper; import com.craisinlord.antarchy.content.item.ultimate.UltimateGearHelper;
import java.util.HashMap; import java.util.HashMap;
@@ -427,6 +426,8 @@ public final class CreativeTabContents {
entry("growth_ray", "combat"), entry("growth_ray", "combat"),
entry("gravity_gun", "combat"), entry("gravity_gun", "combat"),
entry("portal_gun", "combat"), entry("portal_gun", "combat"),
entry("portal_gun_atlas", "combat"),
entry("portal_gun_p_body", "combat"),
entry("eye_of_the_storm", "combat"), entry("eye_of_the_storm", "combat"),
entry("water_cannon", "combat"), entry("water_cannon", "combat"),
entry("attitude_adjuster", "combat"), entry("attitude_adjuster", "combat"),
@@ -641,14 +642,6 @@ public final class CreativeTabContents {
} }
continue; continue;
} }
if ("portal_gun".equals(entry.path())) {
Item portalGun = item(entry.path());
output.accept(portalGun);
output.accept(PortalGunItem.createGlobalVariant(portalGun, "Chell"));
output.accept(PortalGunItem.createGlobalVariant(portalGun, "Atlas"));
output.accept(PortalGunItem.createGlobalVariant(portalGun, "P-body"));
continue;
}
output.accept(item(entry.path())); output.accept(item(entry.path()));
} }
for (String potionPath : ANTARCHY_POTION_FAMILIES) { for (String potionPath : ANTARCHY_POTION_FAMILIES) {
@@ -678,14 +671,6 @@ public final class CreativeTabContents {
output.insertAfter(vanillaItem(entry.insertAfterPath()), item(entry.path())); output.insertAfter(vanillaItem(entry.insertAfterPath()), item(entry.path()));
continue; continue;
} }
if ("portal_gun".equals(entry.path())) {
Item portalGun = item(entry.path());
output.accept(portalGun);
output.accept(PortalGunItem.createGlobalVariant(portalGun, "Chell"));
output.accept(PortalGunItem.createGlobalVariant(portalGun, "Atlas"));
output.accept(PortalGunItem.createGlobalVariant(portalGun, "P-body"));
continue;
}
if ("combat".equals(tabPath)) { if ("combat".equals(tabPath)) {
if (isUltimateArmor(entry.path())) { if (isUltimateArmor(entry.path())) {
output.accept(UltimateGearHelper.createUltimateArmorStack(item(entry.path()), holders)); output.accept(UltimateGearHelper.createUltimateArmorStack(item(entry.path()), holders));
@@ -115,7 +115,6 @@ public class HushProjectileEntity extends ThrowableItemProjectile {
return; return;
} }
// Smoothly steer toward the target each tick
Vec3 desired = toTarget.normalize().scale(TRAVEL_SPEED); Vec3 desired = toTarget.normalize().scale(TRAVEL_SPEED);
this.setDeltaMovement(this.getDeltaMovement().lerp(desired, 0.1D)); this.setDeltaMovement(this.getDeltaMovement().lerp(desired, 0.1D));
} }
@@ -167,7 +167,7 @@ public class MantisEntity extends Monster implements GeoEntity {
MantisEntity.class, MantisEntity.class,
new AABB(pos).inflate(64.0D), new AABB(pos).inflate(64.0D),
Entity::isAlive Entity::isAlive
).size() < 3; ).size() < 4;
} }
@Override @Override
@@ -284,9 +284,10 @@ public final class AntTeleportHelper {
// The bounded search can fail near rough or sparsely-generated terrain. // The bounded search can fail near rough or sparsely-generated terrain.
// Retry once with a wider radius before giving up. // Retry once with a wider radius before giving up.
loadArrivalSearchArea(destination, preferredPos, EXPANDED_ARRIVAL_SEARCH_RADIUS); loadArrivalSearchArea(destination, preferredPos, EXPANDED_ARRIVAL_SEARCH_RADIUS);
return findSafeArrivalPositionInYRange( Vec3 expandedSafePos = findSafeArrivalPositionInYRange(
player, destination, preferredPos, yRange[0], yRange[1], player, destination, preferredPos, yRange[0], yRange[1],
EXPANDED_ARRIVAL_SEARCH_RADIUS, EXPANDED_ARRIVAL_VERTICAL_SEARCH); EXPANDED_ARRIVAL_SEARCH_RADIUS, EXPANDED_ARRIVAL_VERTICAL_SEARCH);
return expandedSafePos != null ? expandedSafePos : createEmergencyArrivalPlatform(destination, preferredPos, yRange[0], yRange[1]);
} }
Set<BlockPos> candidates = new LinkedHashSet<>(); Set<BlockPos> candidates = new LinkedHashSet<>();
@@ -323,6 +324,46 @@ public final class AntTeleportHelper {
} }
} }
return createEmergencyArrivalPlatform(destination, preferredPos, destination.getMinBuildHeight() + 1, destination.getMaxBuildHeight() - 3);
}
@Nullable
private static Vec3 createEmergencyArrivalPlatform(ServerLevel destination, BlockPos preferredPos, int minY, int maxY) {
int y = Math.clamp(preferredPos.getY(), minY, maxY);
int centerX = preferredPos.getX();
int centerZ = preferredPos.getZ();
for (int radius = 0; radius <= EXPANDED_ARRIVAL_SEARCH_RADIUS; radius++) {
for (int xOff = -radius; xOff <= radius; xOff++) {
for (int zOff = -radius; zOff <= radius; zOff++) {
if (radius > 0 && Math.abs(xOff) != radius && Math.abs(zOff) != radius) continue;
int x = centerX + xOff;
int z = centerZ + zOff;
BlockPos floor = new BlockPos(x, y - 1, z);
boolean withinBorder = true;
for (int dx = -2; dx <= 2 && withinBorder; dx++) {
for (int dz = -2; dz <= 2; dz++) {
BlockPos check = floor.offset(dx, 0, dz);
if (!destination.getWorldBorder().isWithinBounds(check) || !destination.hasChunkAt(check.above(3))) {
withinBorder = false;
break;
}
}
}
if (!withinBorder) continue;
for (int dx = -2; dx <= 2; dx++) {
for (int dz = -2; dz <= 2; dz++) {
BlockPos pad = floor.offset(dx, 0, dz);
destination.setBlock(pad, Blocks.COBBLESTONE.defaultBlockState(), Block.UPDATE_ALL);
for (int dy = 1; dy <= 3; dy++) {
destination.setBlock(pad.above(dy), Blocks.AIR.defaultBlockState(), Block.UPDATE_ALL);
}
}
}
Vec3 safePos = new Vec3(x + 0.5D, y, z + 0.5D);
if (isValidArrivalPosition(destination, safePos)) return safePos;
}
}
}
return null; return null;
} }
@@ -141,9 +141,9 @@ public class BasiliskEntity extends Monster implements GeoEntity {
} }
return level.getDifficulty() != Difficulty.PEACEFUL return level.getDifficulty() != Difficulty.PEACEFUL
&& pos.getY() >= 60 && pos.getY() <= 90 && pos.getY() >= 60 && pos.getY() <= 100
&& level.getMaxLocalRawBrightness(pos) <= AntarchySettings.basiliskSpawnMaxLightLevel() && level.getMaxLocalRawBrightness(pos) <= AntarchySettings.basiliskSpawnMaxLightLevel()
&& level.getEntitiesOfClass(BasiliskEntity.class, new AABB(pos).inflate(40.0D), Entity::isAlive).isEmpty() && level.getEntitiesOfClass(BasiliskEntity.class, new AABB(pos).inflate(36.0D), Entity::isAlive).isEmpty()
&& Monster.checkMonsterSpawnRules(entityType, level, spawnReason, pos, random); && Monster.checkMonsterSpawnRules(entityType, level, spawnReason, pos, random);
} }
@@ -277,12 +277,10 @@ public class BrutalflyEntity extends Monster implements GeoEntity {
this.cocoonHitCooldownTicks--; this.cocoonHitCooldownTicks--;
} }
// Pin position to hang from anchor
if (this.anchorPos != null) { if (this.anchorPos != null) {
this.setPos(this.anchorPos.getX() + 0.5, this.anchorPos.getY() - 1.5, this.anchorPos.getZ() + 0.5); this.setPos(this.anchorPos.getX() + 0.5, this.anchorPos.getY() - 1.5, this.anchorPos.getZ() + 0.5);
this.setDeltaMovement(Vec3.ZERO); this.setDeltaMovement(Vec3.ZERO);
// Hatch immediately if anchor log was broken
if (!this.isHatching && !this.level().getBlockState(this.anchorPos).is(BlockTags.LOGS)) { if (!this.isHatching && !this.level().getBlockState(this.anchorPos).is(BlockTags.LOGS)) {
this.startHatching(); this.startHatching();
return; return;
@@ -303,7 +301,6 @@ public class BrutalflyEntity extends Monster implements GeoEntity {
return; return;
} }
// Count down hit animation
if (this.cocoonAnimTicks > 0) { if (this.cocoonAnimTicks > 0) {
this.cocoonAnimTicks--; this.cocoonAnimTicks--;
if (this.cocoonAnimTicks <= 0) { if (this.cocoonAnimTicks <= 0) {
@@ -312,7 +309,6 @@ public class BrutalflyEntity extends Monster implements GeoEntity {
return; return;
} }
// Occasionally play idle animation
if (this.getAnimationState() == ANIM_COCOON_IDLE) { if (this.getAnimationState() == ANIM_COCOON_IDLE) {
// Will be reset to COCOON after idle ticks counted down // Will be reset to COCOON after idle ticks counted down
} else if (this.tickCount % 200 == 17 && this.random.nextFloat() < 0.4F) { } else if (this.tickCount % 200 == 17 && this.random.nextFloat() < 0.4F) {
@@ -7,12 +7,14 @@ import com.craisinlord.antarchy.content.block.entity.PortalGunPortalFaceRecord;
import com.craisinlord.antarchy.content.block.entity.PortalGunPortalCellAccess; import com.craisinlord.antarchy.content.block.entity.PortalGunPortalCellAccess;
import com.craisinlord.antarchy.content.client.model.ResourceBackedGeoItemModel; import com.craisinlord.antarchy.content.client.model.ResourceBackedGeoItemModel;
import com.craisinlord.antarchy.content.client.renderer.AnimatedHeldItemRenderer; import com.craisinlord.antarchy.content.client.renderer.AnimatedHeldItemRenderer;
import com.craisinlord.antarchy.content.gravity.AntarchyGravityApi;
import com.craisinlord.antarchy.content.network.PortalGunPrimaryPayload; import com.craisinlord.antarchy.content.network.PortalGunPrimaryPayload;
import com.craisinlord.antarchy.content.portalgun.PortalGunBlackHoleEntity; import com.craisinlord.antarchy.content.portalgun.PortalGunBlackHoleEntity;
import com.craisinlord.antarchy.content.portalgun.PortalGunPlacement; import com.craisinlord.antarchy.content.portalgun.PortalGunPlacement;
import com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity; import com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity;
import com.craisinlord.antarchy.content.portalgun.PortalGunProjectileEntity; import com.craisinlord.antarchy.content.portalgun.PortalGunProjectileEntity;
import com.craisinlord.antarchy.content.portalgun.PortalGunSavedData; import com.craisinlord.antarchy.content.portalgun.PortalGunSavedData;
import com.craisinlord.antarchy.content.portalgun.PortalGunVariant;
import com.craisinlord.antarchy.content.portalgun.PortalGunResetManager; import com.craisinlord.antarchy.content.portalgun.PortalGunResetManager;
import com.craisinlord.antarchy.config.AntarchySettings; import com.craisinlord.antarchy.config.AntarchySettings;
import java.util.HashSet; import java.util.HashSet;
@@ -20,7 +22,6 @@ import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.UUID; import java.util.UUID;
import java.nio.charset.StandardCharsets;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Supplier; import java.util.function.Supplier;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
@@ -73,23 +74,19 @@ public class PortalGunItem extends Item implements GeoItem {
private static final String LAST_SIDE_TAG = "antarchy.portal_gun_last_side"; private static final String LAST_SIDE_TAG = "antarchy.portal_gun_last_side";
private static final String MOON_SIDE_TAG = "antarchy.portal_gun_moon_side"; private static final String MOON_SIDE_TAG = "antarchy.portal_gun_moon_side";
public static final String GUN_ID_TAG = "antarchy.portal_gun_id"; public static final String GUN_ID_TAG = "antarchy.portal_gun_id";
public static final String CHANNEL_NAME_TAG = "antarchy.portal_gun_channel";
public static final String OWNER_ID_TAG = "antarchy.portal_gun_owner"; public static final String OWNER_ID_TAG = "antarchy.portal_gun_owner";
public static final String OWNER_NAME_TAG = "antarchy.portal_gun_owner_name";
public static final UUID GLOBAL_OWNER_ID = UUID.nameUUIDFromBytes("Global".getBytes(StandardCharsets.UTF_8));
public static final String PORTAL_WIDTH_TAG = "width"; public static final String PORTAL_WIDTH_TAG = "width";
public static final String PORTAL_HEIGHT_TAG = "height"; public static final String PORTAL_HEIGHT_TAG = "height";
public static final String GRAB_STRENGTH_TAG = "grabStrength";
private static final int MAX_PORTAL_WIDTH = 16; private static final int MAX_PORTAL_WIDTH = 16;
private static final int MAX_PORTAL_HEIGHT = 16; private static final int MAX_PORTAL_HEIGHT = 16;
private static final ResourceLocation MODEL_LOCATION = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "geo/portal_gun.geo.json"); private static final ResourceLocation MODEL_LOCATION = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "geo/portal_gun.geo.json");
private static final ResourceLocation TEXTURE_LOCATION = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "textures/models/item/portal_gun.png");
private static final ResourceLocation ANIMATION_LOCATION = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "animations/portal_gun.animation.json"); private static final ResourceLocation ANIMATION_LOCATION = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "animations/portal_gun.animation.json");
private final Supplier<? extends EntityType<? extends PortalGunPortalEntity>> portalType; private final Supplier<? extends EntityType<? extends PortalGunPortalEntity>> portalType;
private final Supplier<? extends EntityType<? extends PortalGunBlackHoleEntity>> blackHoleType; private final Supplier<? extends EntityType<? extends PortalGunBlackHoleEntity>> blackHoleType;
private final Supplier<? extends EntityType<? extends PortalGunProjectileEntity>> projectileType; private final Supplier<? extends EntityType<? extends PortalGunProjectileEntity>> projectileType;
private final Supplier<? extends Block> portalMasterBlock; private final Supplier<? extends Block> portalMasterBlock;
private final Supplier<? extends Block> portalBaseBlock; private final Supplier<? extends Block> portalBaseBlock;
private final PortalGunVariant variant;
private final AnimatableInstanceCache geoCache = GeckoLibUtil.createInstanceCache(this); private final AnimatableInstanceCache geoCache = GeckoLibUtil.createInstanceCache(this);
public PortalGunItem( public PortalGunItem(
@@ -98,9 +95,11 @@ public class PortalGunItem extends Item implements GeoItem {
Supplier<? extends EntityType<? extends PortalGunBlackHoleEntity>> blackHoleType, Supplier<? extends EntityType<? extends PortalGunBlackHoleEntity>> blackHoleType,
Supplier<? extends EntityType<? extends PortalGunProjectileEntity>> projectileType, Supplier<? extends EntityType<? extends PortalGunProjectileEntity>> projectileType,
Supplier<? extends Block> portalMasterBlock, Supplier<? extends Block> portalMasterBlock,
Supplier<? extends Block> portalBaseBlock Supplier<? extends Block> portalBaseBlock,
PortalGunVariant variant
) { ) {
super(properties); super(properties);
this.variant = variant;
this.portalType = portalType; this.portalType = portalType;
this.blackHoleType = blackHoleType; this.blackHoleType = blackHoleType;
this.projectileType = projectileType; this.projectileType = projectileType;
@@ -109,6 +108,10 @@ public class PortalGunItem extends Item implements GeoItem {
GeoItem.registerSyncedAnimatable(this); GeoItem.registerSyncedAnimatable(this);
} }
public PortalGunVariant getVariant() {
return this.variant;
}
@Override @Override
public InteractionResultHolder<ItemStack> use(Level level, Player player, InteractionHand usedHand) { public InteractionResultHolder<ItemStack> use(Level level, Player player, InteractionHand usedHand) {
ItemStack stack = player.getItemInHand(usedHand); ItemStack stack = player.getItemInHand(usedHand);
@@ -134,23 +137,21 @@ public class PortalGunItem extends Item implements GeoItem {
} }
public void resetPortals(ServerPlayer player, ItemStack stack) { public void resetPortals(ServerPlayer player, ItemStack stack) {
UUID gunId = this.ensureGunId(stack, player.getUUID(), player.getGameProfile().getName()); UUID gunId = this.ensureGunId(stack, player.getUUID());
UUID ownerId = getPortalOwnerId(stack, player.getUUID()); PortalGunSavedData.clearAllPortals(player.serverLevel(), gunId);
PortalGunSavedData.clearAllPortals(player.serverLevel(), ownerId, gunId, getChannelName(stack));
this.setLastSide(stack, null); this.setLastSide(stack, null);
this.setMoonSide(stack, null); this.setMoonSide(stack, null);
player.serverLevel().playSound(null, player.getX(), player.getY(), player.getZ(), PortalGunPortalEntity.sound("portal_fizzle"), SoundSource.PLAYERS, 0.6F, 1.0F); player.serverLevel().playSound(null, player.getX(), player.getY(), player.getZ(), PortalGunPortalEntity.sound("portal_fizzle"), SoundSource.PLAYERS, 0.6F, 1.0F);
} }
public void resetPortalSide(ServerPlayer player, ItemStack stack, PortalGunPortalEntity.PortalSide side) { public void resetPortalSide(ServerPlayer player, ItemStack stack, PortalGunPortalEntity.PortalSide side) {
UUID gunId = this.ensureGunId(stack, player.getUUID(), player.getGameProfile().getName()); UUID gunId = this.ensureGunId(stack, player.getUUID());
UUID ownerId = getPortalOwnerId(stack, player.getUUID()); PortalGunSavedData.clearPortalSide(player.serverLevel(), gunId, side);
PortalGunSavedData.clearPortalSide(player.serverLevel(), ownerId, gunId, getChannelName(stack), side);
player.serverLevel().playSound(null, player.getX(), player.getY(), player.getZ(), PortalGunPortalEntity.sound("portal_fizzle"), SoundSource.PLAYERS, 0.6F, 1.0F); player.serverLevel().playSound(null, player.getX(), player.getY(), player.getZ(), PortalGunPortalEntity.sound("portal_fizzle"), SoundSource.PLAYERS, 0.6F, 1.0F);
} }
public void fireMobPortal(ServerLevel level, LivingEntity shooter, ItemStack stack, PortalGunPortalEntity.PortalSide side) { public void fireMobPortal(ServerLevel level, LivingEntity shooter, ItemStack stack, PortalGunPortalEntity.PortalSide side) {
this.ensureGunId(stack, shooter.getUUID(), shooter.getName().getString()); this.ensureGunId(stack, shooter.getUUID());
String firePath = side == PortalGunPortalEntity.PortalSide.BLUE ? "portal_gun_fire_blue" : "portal_gun_fire_orange"; String firePath = side == PortalGunPortalEntity.PortalSide.BLUE ? "portal_gun_fire_blue" : "portal_gun_fire_orange";
level.playSound(null, shooter.getX(), shooter.getY(), shooter.getZ(), PortalGunPortalEntity.sound(firePath), shooter.getSoundSource(), 0.65F, side == PortalGunPortalEntity.PortalSide.BLUE ? 1.15F : 0.88F); level.playSound(null, shooter.getX(), shooter.getY(), shooter.getZ(), PortalGunPortalEntity.sound(firePath), shooter.getSoundSource(), 0.65F, side == PortalGunPortalEntity.PortalSide.BLUE ? 1.15F : 0.88F);
this.spawnProjectile(level, shooter, stack, side); this.spawnProjectile(level, shooter, stack, side);
@@ -163,7 +164,7 @@ public class PortalGunItem extends Item implements GeoItem {
} }
} }
this.triggerFireAnimation(level, player, stack); this.triggerFireAnimation(level, player, stack);
this.ensureGunId(stack, player.getUUID(), player.getGameProfile().getName()); this.ensureGunId(stack, player.getUUID());
stack.hurtAndBreak(1, player, offhand ? EquipmentSlot.OFFHAND : EquipmentSlot.MAINHAND); stack.hurtAndBreak(1, player, offhand ? EquipmentSlot.OFFHAND : EquipmentSlot.MAINHAND);
player.awardStat(Stats.ITEM_USED.get(this)); player.awardStat(Stats.ITEM_USED.get(this));
player.getCooldowns().addCooldown(this, 6); player.getCooldowns().addCooldown(this, 6);
@@ -175,10 +176,8 @@ public class PortalGunItem extends Item implements GeoItem {
public void handlePortalImpact(ServerLevel level, LivingEntity shooter, ItemStack stack, UUID gunId, PortalGunPortalEntity.PortalSide side, BlockHitResult hitResult, Vec3 impactPos, int portalWidth, int portalHeight) { public void handlePortalImpact(ServerLevel level, LivingEntity shooter, ItemStack stack, UUID gunId, PortalGunPortalEntity.PortalSide side, BlockHitResult hitResult, Vec3 impactPos, int portalWidth, int portalHeight) {
UUID portalOwnerId = getPortalOwnerId(stack, shooter.getUUID()); UUID portalOwnerId = getPortalOwnerId(stack, shooter.getUUID());
String portalOwnerIdentity = getPortalOwnerIdentity(stack, shooter.getUUID()); PortalGunPortalEntity existing = this.findPlacedPortal(level, gunId, side);
String channelName = getChannelName(stack); PortalGunPlacement placement = this.findPlacement(level, shooter, gunId, side, hitResult, existing, portalWidth, portalHeight);
PortalGunPortalEntity existing = this.findPlacedPortal(level, portalOwnerId, gunId, channelName, side);
PortalGunPlacement placement = this.findPlacement(level, shooter, portalOwnerId, channelName, side, hitResult, existing, portalWidth, portalHeight);
if (placement == null) { if (placement == null) {
Antarchy.LOGGER.debug("Portal gun placement failed owner={} side={} hitBlock={} hitFace={} hitLocation={}", shooter.getUUID(), side, hitResult.getBlockPos(), hitResult.getDirection(), hitResult.getLocation()); Antarchy.LOGGER.debug("Portal gun placement failed owner={} side={} hitBlock={} hitFace={} hitLocation={}", shooter.getUUID(), side, hitResult.getBlockPos(), hitResult.getDirection(), hitResult.getLocation());
level.playSound(null, impactPos.x, impactPos.y, impactPos.z, PortalGunPortalEntity.sound("portal_gun_invalid_surface"), SoundSource.PLAYERS, 0.55F, 1.0F); level.playSound(null, impactPos.x, impactPos.y, impactPos.z, PortalGunPortalEntity.sound("portal_gun_invalid_surface"), SoundSource.PLAYERS, 0.55F, 1.0F);
@@ -204,16 +203,16 @@ public class PortalGunItem extends Item implements GeoItem {
return; return;
} }
PortalGunPortalEntity portal = new PortalGunPortalEntity(this.portalType.get(), level); PortalGunPortalEntity portal = new PortalGunPortalEntity(this.portalType.get(), level);
portal.configure(portalOwnerId, gunId, portalOwnerIdentity, channelName, side, placement); portal.configure(portalOwnerId, gunId, this.variant, side, placement);
portal.moveTo(placement.center().x, placement.center().y, placement.center().z, placement.yaw(), 0.0F); portal.moveTo(placement.center().x, placement.center().y, placement.center().z, placement.yaw(), 0.0F);
if (!this.placePortalBlocks(level, portalOwnerId, gunId, portalOwnerIdentity, channelName, side, portal, placement)) { if (!this.placePortalBlocks(level, portalOwnerId, gunId, side, portal, placement)) {
Antarchy.LOGGER.debug("Portal gun block placement failed owner={} side={} portal={} center={} facing={} up={} master={} base={}", shooter.getUUID(), side, portal.getUUID(), placement.center(), placement.facing(), placement.upAxis(), placement.masterPos(), placement.basePos()); Antarchy.LOGGER.debug("Portal gun block placement failed owner={} side={} portal={} center={} facing={} up={} master={} base={}", shooter.getUUID(), side, portal.getUUID(), placement.center(), placement.facing(), placement.upAxis(), placement.masterPos(), placement.basePos());
level.playSound(null, impactPos.x, impactPos.y, impactPos.z, PortalGunPortalEntity.sound("portal_gun_invalid_surface"), SoundSource.PLAYERS, 0.55F, 1.0F); level.playSound(null, impactPos.x, impactPos.y, impactPos.z, PortalGunPortalEntity.sound("portal_gun_invalid_surface"), SoundSource.PLAYERS, 0.55F, 1.0F);
return; return;
} }
level.addFreshEntity(portal); level.addFreshEntity(portal);
PortalGunSavedData.setPortal(level, portalOwnerId, gunId, channelName, side, portal.getUUID()); PortalGunSavedData.setPortal(level, gunId, side, portal.getUUID());
PortalGunPortalEntity other = this.findCounterpart(level, portalOwnerId, gunId, channelName, side); PortalGunPortalEntity other = this.findCounterpart(level, gunId, side);
if (other != null) { if (other != null) {
portal.linkTo(other); portal.linkTo(other);
other.linkTo(portal); other.linkTo(portal);
@@ -261,8 +260,7 @@ public class PortalGunItem extends Item implements GeoItem {
if (!level.dimensionType().hasSkyLight() || !level.isNight() || player.getXRot() > -40.0F) { if (!level.dimensionType().hasSkyLight() || !level.isNight() || player.getXRot() > -40.0F) {
return false; return false;
} }
UUID ownerId = getPortalOwnerId(stack, player.getUUID()); PortalGunPortalEntity existing = this.findPlacedPortal(level, this.ensureGunId(stack, player.getUUID()), side);
PortalGunPortalEntity existing = this.findPlacedPortal(level, ownerId, this.ensureGunId(stack, player.getUUID(), player.getGameProfile().getName()), getChannelName(stack), side);
if (existing != null) { if (existing != null) {
existing.discard(); existing.discard();
} }
@@ -273,7 +271,7 @@ public class PortalGunItem extends Item implements GeoItem {
player.awardStat(Stats.ITEM_USED.get(this)); player.awardStat(Stats.ITEM_USED.get(this));
player.getCooldowns().addCooldown(this, 6); player.getCooldowns().addCooldown(this, 6);
level.playSound(null, player.getX(), player.getY(), player.getZ(), PortalGunPortalEntity.sound(side == PortalGunPortalEntity.PortalSide.BLUE ? "portal_gun_fire_blue" : "portal_gun_fire_orange"), SoundSource.PLAYERS, 0.65F, side == PortalGunPortalEntity.PortalSide.BLUE ? 1.15F : 0.88F); level.playSound(null, player.getX(), player.getY(), player.getZ(), PortalGunPortalEntity.sound(side == PortalGunPortalEntity.PortalSide.BLUE ? "portal_gun_fire_blue" : "portal_gun_fire_orange"), SoundSource.PLAYERS, 0.65F, side == PortalGunPortalEntity.PortalSide.BLUE ? 1.15F : 0.88F);
this.spawnShotTrail(level, player, player.getEyePosition().add(player.getLookAngle().scale(64.0D)), side); this.spawnShotTrail(level, player, player.getEyePosition().add(this.worldLook(player).scale(64.0D)), side);
return true; return true;
} }
@@ -301,10 +299,10 @@ public class PortalGunItem extends Item implements GeoItem {
} }
private void spawnProjectile(ServerLevel level, LivingEntity shooter, ItemStack stack, PortalGunPortalEntity.PortalSide side) { private void spawnProjectile(ServerLevel level, LivingEntity shooter, ItemStack stack, PortalGunPortalEntity.PortalSide side) {
UUID gunId = this.ensureGunId(stack, shooter.getUUID(), shooter.getName().getString()); UUID gunId = this.ensureGunId(stack, shooter.getUUID());
PortalGunProjectileEntity projectile = new PortalGunProjectileEntity(this.projectileType.get(), shooter, level); PortalGunProjectileEntity projectile = new PortalGunProjectileEntity(this.projectileType.get(), shooter, level);
projectile.configure(side, gunId, stack); projectile.configure(side, gunId, stack);
Vec3 look = shooter.getLookAngle(); Vec3 look = this.worldLook(shooter);
Vec3 start = shooter.getEyePosition(); Vec3 start = shooter.getEyePosition();
double launchSpeed = PortalGunProjectileEntity.MIN_LAUNCH_SPEED + level.getRandom().nextDouble() * PortalGunProjectileEntity.LAUNCH_SPEED_VARIANCE; double launchSpeed = PortalGunProjectileEntity.MIN_LAUNCH_SPEED + level.getRandom().nextDouble() * PortalGunProjectileEntity.LAUNCH_SPEED_VARIANCE;
projectile.setProjectileSpeed(launchSpeed); projectile.setProjectileSpeed(launchSpeed);
@@ -324,14 +322,9 @@ public class PortalGunItem extends Item implements GeoItem {
return Mth.clamp(data == null || !data.copyTag().contains(PORTAL_HEIGHT_TAG) ? 2 : data.copyTag().getInt(PORTAL_HEIGHT_TAG), 2, MAX_PORTAL_HEIGHT); return Mth.clamp(data == null || !data.copyTag().contains(PORTAL_HEIGHT_TAG) ? 2 : data.copyTag().getInt(PORTAL_HEIGHT_TAG), 2, MAX_PORTAL_HEIGHT);
} }
public static int getGrabStrength(ItemStack stack) { private PortalGunPortalEntity findCounterpart(ServerLevel level, UUID gunId, PortalGunPortalEntity.PortalSide side) {
net.minecraft.world.item.component.CustomData data = stack.get(DataComponents.CUSTOM_DATA);
return Mth.clamp(data == null || !data.copyTag().contains(GRAB_STRENGTH_TAG) ? 4 : data.copyTag().getInt(GRAB_STRENGTH_TAG), 1, 4);
}
private PortalGunPortalEntity findCounterpart(ServerLevel level, UUID owner, UUID gunId, String channelName, PortalGunPortalEntity.PortalSide side) {
PortalGunPortalEntity.PortalSide otherSide = side == PortalGunPortalEntity.PortalSide.BLUE ? PortalGunPortalEntity.PortalSide.ORANGE : PortalGunPortalEntity.PortalSide.BLUE; PortalGunPortalEntity.PortalSide otherSide = side == PortalGunPortalEntity.PortalSide.BLUE ? PortalGunPortalEntity.PortalSide.ORANGE : PortalGunPortalEntity.PortalSide.BLUE;
Optional<UUID> otherId = PortalGunSavedData.getPortalId(level.getServer(), owner, gunId, channelName, otherSide, level.dimension().location()); Optional<UUID> otherId = PortalGunSavedData.getPortalId(level.getServer(), gunId, otherSide, level.dimension().location());
if (otherId.isEmpty()) { if (otherId.isEmpty()) {
return null; return null;
} }
@@ -339,8 +332,8 @@ public class PortalGunItem extends Item implements GeoItem {
return entity instanceof PortalGunPortalEntity portal ? portal : null; return entity instanceof PortalGunPortalEntity portal ? portal : null;
} }
private PortalGunPortalEntity findPlacedPortal(ServerLevel level, UUID owner, UUID gunId, String channelName, PortalGunPortalEntity.PortalSide side) { private PortalGunPortalEntity findPlacedPortal(ServerLevel level, UUID gunId, PortalGunPortalEntity.PortalSide side) {
Optional<UUID> portalId = PortalGunSavedData.getPortalId(level.getServer(), owner, gunId, channelName, side, level.dimension().location()); Optional<UUID> portalId = PortalGunSavedData.getPortalId(level.getServer(), gunId, side, level.dimension().location());
if (portalId.isEmpty()) { if (portalId.isEmpty()) {
return null; return null;
} }
@@ -354,11 +347,19 @@ public class PortalGunItem extends Item implements GeoItem {
private BlockHitResult raycast(Level level, Player player) { private BlockHitResult raycast(Level level, Player player) {
Vec3 start = player.getEyePosition(); Vec3 start = player.getEyePosition();
Vec3 end = start.add(player.getLookAngle().scale(64.0D)); Vec3 end = start.add(this.worldLook(player).scale(64.0D));
return level.clip(new ClipContext(start, end, ClipContext.Block.COLLIDER, ClipContext.Fluid.NONE, player)); return level.clip(new ClipContext(start, end, ClipContext.Block.COLLIDER, ClipContext.Fluid.NONE, player));
} }
private PortalGunPlacement findPlacement(Level level, LivingEntity shooter, UUID ownerId, String channelName, PortalGunPortalEntity.PortalSide side, BlockHitResult hitResult, PortalGunPortalEntity replacingPortal, int requestedWidth, int requestedHeight) { private Vec3 worldLook(LivingEntity entity) {
Vec3 look = entity.getLookAngle();
return AntarchyGravityApi.isGravityInverted(entity)
? com.craisinlord.antarchy.content.gravity.AntarchyGravityRotationUtil.vecPlayerToWorld(
look, AntarchyGravityApi.getGravityDirection(entity))
: look;
}
private PortalGunPlacement findPlacement(Level level, LivingEntity shooter, UUID gunId, PortalGunPortalEntity.PortalSide side, BlockHitResult hitResult, PortalGunPortalEntity replacingPortal, int requestedWidth, int requestedHeight) {
int maxWidth = Mth.clamp(requestedWidth, 1, 16); int maxWidth = Mth.clamp(requestedWidth, 1, 16);
int maxHeight = Mth.clamp(requestedHeight, 2, 16); int maxHeight = Mth.clamp(requestedHeight, 2, 16);
boolean canResize = AntarchySettings.portalGunCanPortalsResizeWhenCreated(); boolean canResize = AntarchySettings.portalGunCanPortalsResizeWhenCreated();
@@ -367,7 +368,7 @@ public class PortalGunItem extends Item implements GeoItem {
if (!canResize && (width != maxWidth || height != maxHeight)) { if (!canResize && (width != maxWidth || height != maxHeight)) {
continue; continue;
} }
PortalGunPlacement placement = this.findPlacementForSize(level, shooter, ownerId, channelName, side, hitResult, replacingPortal, width, height); PortalGunPlacement placement = this.findPlacementForSize(level, shooter, gunId, side, hitResult, replacingPortal, width, height);
if (placement != null) { if (placement != null) {
return placement; return placement;
} }
@@ -376,7 +377,7 @@ public class PortalGunItem extends Item implements GeoItem {
return null; return null;
} }
private PortalGunPlacement findPlacementForSize(Level level, LivingEntity shooter, UUID ownerId, String channelName, PortalGunPortalEntity.PortalSide side, BlockHitResult hitResult, PortalGunPortalEntity replacingPortal, int width, int height) { private PortalGunPlacement findPlacementForSize(Level level, LivingEntity shooter, UUID gunId, PortalGunPortalEntity.PortalSide side, BlockHitResult hitResult, PortalGunPortalEntity replacingPortal, int width, int height) {
Direction facing = hitResult.getDirection(); Direction facing = hitResult.getDirection();
Direction heightAxis = this.resolveUpAxis(shooter, facing); Direction heightAxis = this.resolveUpAxis(shooter, facing);
Direction widthAxis = PortalGunPlacement.widthAxis(facing, heightAxis); Direction widthAxis = PortalGunPlacement.widthAxis(facing, heightAxis);
@@ -410,7 +411,7 @@ public class PortalGunItem extends Item implements GeoItem {
BlockState supportState = level.getBlockState(supportPos); BlockState supportState = level.getBlockState(supportPos);
BlockState portalState = level.getBlockState(portalPos); BlockState portalState = level.getBlockState(portalPos);
if (!supportState.isFaceSturdy(level, supportPos, facing) if (!supportState.isFaceSturdy(level, supportPos, facing)
|| !this.isPortalSpaceAvailable(level, portalPos, portalState, ownerId, channelName, side, facing)) { || !this.isPortalSpaceAvailable(level, portalPos, portalState, gunId, side, facing)) {
valid = false; valid = false;
break; break;
} }
@@ -505,13 +506,13 @@ public class PortalGunItem extends Item implements GeoItem {
return horizontal; return horizontal;
} }
private boolean isPortalSpaceAvailable(Level level, BlockPos pos, BlockState state, UUID ownerId, String channelName, PortalGunPortalEntity.PortalSide side, Direction face) { private boolean isPortalSpaceAvailable(Level level, BlockPos pos, BlockState state, UUID gunId, PortalGunPortalEntity.PortalSide side, Direction face) {
BlockEntity blockEntity = level.getBlockEntity(pos); BlockEntity blockEntity = level.getBlockEntity(pos);
if (blockEntity instanceof PortalGunPortalCellAccess portalCell) { if (blockEntity instanceof PortalGunPortalCellAccess portalCell) {
PortalGunPortalFaceRecord record = portalCell.portalGun$getFaceRecord(face); PortalGunPortalFaceRecord record = portalCell.portalGun$getFaceRecord(face);
if (record == null) { if (record == null) {
if (blockEntity instanceof PortalGunPortalMasterBlockEntity master && master.getPortalId() != null && master.getFacing() == face) { if (blockEntity instanceof PortalGunPortalMasterBlockEntity master && master.getPortalId() != null && master.getFacing() == face) {
return ownerId != null && ownerId.equals(master.getOwnerId()) && channelName.equals(master.getChannelName()) && master.getSide() == side; return gunId != null && gunId.equals(master.getGunId()) && master.getSide() == side;
} }
if (blockEntity instanceof PortalGunPortalBaseBlockEntity base && base.getPortalId() != null) { if (blockEntity instanceof PortalGunPortalBaseBlockEntity base && base.getPortalId() != null) {
BlockEntity masterEntity = level.getBlockEntity(base.getMasterPos()); BlockEntity masterEntity = level.getBlockEntity(base.getMasterPos());
@@ -519,23 +520,21 @@ public class PortalGunItem extends Item implements GeoItem {
return false; return false;
} }
return master.getFacing() != face return master.getFacing() != face
|| (ownerId != null || (gunId != null
&& ownerId.equals(base.getOwnerId()) && gunId.equals(master.getGunId())
&& channelName.equals(master.getChannelName())
&& base.getSide() == side && base.getSide() == side
&& master.getSide() == side); && master.getSide() == side);
} }
return true; return true;
} }
return ownerId != null return gunId != null
&& ownerId.equals(record.ownerId()) && gunId.equals(record.gunId())
&& channelName.equals(record.channelName())
&& record.side() == side; && record.side() == side;
} }
return state.getCollisionShape(level, pos).isEmpty(); return state.getCollisionShape(level, pos).isEmpty();
} }
private boolean placePortalBlocks(ServerLevel level, UUID ownerId, UUID gunId, String ownerIdentity, String channelName, PortalGunPortalEntity.PortalSide side, PortalGunPortalEntity portal, PortalGunPlacement placement) { private boolean placePortalBlocks(ServerLevel level, UUID ownerId, UUID gunId, PortalGunPortalEntity.PortalSide side, PortalGunPortalEntity portal, PortalGunPlacement placement) {
int pairTime = level.getServer().getTickCount(); int pairTime = level.getServer().getTickCount();
for (int i = 0; i < placement.portalSpots().length; i++) { for (int i = 0; i < placement.portalSpots().length; i++) {
BlockPos spot = placement.portalSpots()[i]; BlockPos spot = placement.portalSpots()[i];
@@ -557,9 +556,8 @@ public class PortalGunItem extends Item implements GeoItem {
if (!(existing instanceof PortalGunPortalCellAccess cell) if (!(existing instanceof PortalGunPortalCellAccess cell)
|| !cell.portalGun$putFaceRecord(new PortalGunPortalFaceRecord( || !cell.portalGun$putFaceRecord(new PortalGunPortalFaceRecord(
ownerId, ownerId,
ownerIdentity,
gunId, gunId,
channelName, this.variant,
portal.getUUID(), portal.getUUID(),
portal.getLinkedPortalId(), portal.getLinkedPortalId(),
side, side,
@@ -578,7 +576,7 @@ public class PortalGunItem extends Item implements GeoItem {
} }
if (masterRole && existing instanceof PortalGunPortalMasterBlockEntity master if (masterRole && existing instanceof PortalGunPortalMasterBlockEntity master
&& (master.getPortalId() == null || master.getPortalId().equals(portal.getUUID()))) { && (master.getPortalId() == null || master.getPortalId().equals(portal.getUUID()))) {
master.configure(ownerId, gunId, ownerIdentity, channelName, portal.getUUID(), side, placement.facing(), placement.upAxis(), placement.basePos(), placement.portalSpots(), placement.compensatedSpots(), pairTime, placement.width(), placement.height()); master.configure(ownerId, gunId, this.variant, portal.getUUID(), side, placement.facing(), placement.upAxis(), placement.basePos(), placement.portalSpots(), placement.compensatedSpots(), pairTime, placement.width(), placement.height());
} else if (!masterRole && existing instanceof PortalGunPortalBaseBlockEntity base } else if (!masterRole && existing instanceof PortalGunPortalBaseBlockEntity base
&& (base.getPortalId() == null || base.getPortalId().equals(portal.getUUID()))) { && (base.getPortalId() == null || base.getPortalId().equals(portal.getUUID()))) {
base.configure(ownerId, portal.getUUID(), side, placement.masterPos()); base.configure(ownerId, portal.getUUID(), side, placement.masterPos());
@@ -656,12 +654,8 @@ public class PortalGunItem extends Item implements GeoItem {
} else { } else {
gunId = UUID.randomUUID(); gunId = UUID.randomUUID();
} }
String channelName = "Random Channel #" + gunId.hashCode();
net.minecraft.world.item.component.CustomData.update(DataComponents.CUSTOM_DATA, stack, tag -> { net.minecraft.world.item.component.CustomData.update(DataComponents.CUSTOM_DATA, stack, tag -> {
tag.putUUID(GUN_ID_TAG, gunId); tag.putUUID(GUN_ID_TAG, gunId);
if (!tag.contains(CHANNEL_NAME_TAG)) {
tag.putString(CHANNEL_NAME_TAG, channelName);
}
if (!tag.contains(LAST_SIDE_TAG)) { if (!tag.contains(LAST_SIDE_TAG)) {
tag.putString(LAST_SIDE_TAG, PortalGunPortalEntity.PortalSide.BLUE.name()); tag.putString(LAST_SIDE_TAG, PortalGunPortalEntity.PortalSide.BLUE.name());
} }
@@ -669,30 +663,16 @@ public class PortalGunItem extends Item implements GeoItem {
return gunId; return gunId;
} }
private UUID ensureGunId(ItemStack stack, UUID ownerId, String ownerName) { private UUID ensureGunId(ItemStack stack, UUID ownerId) {
UUID gunId = this.ensureGunId(stack); UUID gunId = this.ensureGunId(stack);
net.minecraft.world.item.component.CustomData.update(DataComponents.CUSTOM_DATA, stack, tag -> { net.minecraft.world.item.component.CustomData.update(DataComponents.CUSTOM_DATA, stack, tag -> {
if (!tag.contains(OWNER_ID_TAG)) { if (!tag.contains(OWNER_ID_TAG)) {
tag.putString(OWNER_ID_TAG, ownerId.toString()); tag.putString(OWNER_ID_TAG, ownerId.toString());
tag.putString(OWNER_NAME_TAG, ownerName);
} }
}); });
return gunId; return gunId;
} }
public static String getChannelName(ItemStack stack) {
net.minecraft.world.item.component.CustomData customData = stack.get(DataComponents.CUSTOM_DATA);
if (customData == null) {
return "";
}
return customData.copyTag().getString(CHANNEL_NAME_TAG);
}
public static String getPortalOwnerName(ItemStack stack) {
net.minecraft.world.item.component.CustomData customData = stack.get(DataComponents.CUSTOM_DATA);
return customData == null ? "" : customData.copyTag().getString(OWNER_NAME_TAG);
}
public static UUID ensureGunIdentity(ItemStack stack) { public static UUID ensureGunIdentity(ItemStack stack) {
if (stack.isEmpty() || !(stack.getItem() instanceof PortalGunItem item)) { if (stack.isEmpty() || !(stack.getItem() instanceof PortalGunItem item)) {
return null; return null;
@@ -704,7 +684,7 @@ public class PortalGunItem extends Item implements GeoItem {
if (stack.isEmpty() || !(stack.getItem() instanceof PortalGunItem item)) { if (stack.isEmpty() || !(stack.getItem() instanceof PortalGunItem item)) {
return null; return null;
} }
return item.ensureGunId(stack, owner.getUUID(), owner.getGameProfile().getName()); return item.ensureGunId(stack, owner.getUUID());
} }
public static UUID getPortalOwnerId(ItemStack stack, UUID fallback) { public static UUID getPortalOwnerId(ItemStack stack, UUID fallback) {
@@ -713,9 +693,6 @@ public class PortalGunItem extends Item implements GeoItem {
return fallback; return fallback;
} }
String owner = customData.copyTag().getString(OWNER_ID_TAG); String owner = customData.copyTag().getString(OWNER_ID_TAG);
if ("Global".equals(owner)) {
return GLOBAL_OWNER_ID;
}
try { try {
return owner.isEmpty() ? fallback : UUID.fromString(owner); return owner.isEmpty() ? fallback : UUID.fromString(owner);
} catch (IllegalArgumentException ignored) { } catch (IllegalArgumentException ignored) {
@@ -723,32 +700,6 @@ public class PortalGunItem extends Item implements GeoItem {
} }
} }
public static String getPortalOwnerIdentity(ItemStack stack, UUID fallback) {
net.minecraft.world.item.component.CustomData customData = stack.get(DataComponents.CUSTOM_DATA);
if (customData == null) {
return String.valueOf(fallback);
}
String owner = customData.copyTag().getString(OWNER_ID_TAG);
return owner.isEmpty() ? String.valueOf(fallback) : owner;
}
public static ItemStack createGlobalVariant(Item item, String channelName) {
ItemStack stack = new ItemStack(item);
UUID gunId = UUID.nameUUIDFromBytes(("Global\u0000" + channelName).getBytes(StandardCharsets.UTF_8));
net.minecraft.world.item.component.CustomData.update(DataComponents.CUSTOM_DATA, stack, tag -> {
tag.putUUID(GUN_ID_TAG, gunId);
tag.putString(OWNER_ID_TAG, "Global");
tag.putString(OWNER_NAME_TAG, "Global");
tag.putString(CHANNEL_NAME_TAG, channelName);
tag.putInt(PORTAL_WIDTH_TAG, 1);
tag.putInt(PORTAL_HEIGHT_TAG, 2);
tag.putInt(GRAB_STRENGTH_TAG, 4);
tag.putString(LAST_SIDE_TAG, PortalGunPortalEntity.PortalSide.BLUE.name());
tag.putBoolean("lastFired", true);
});
return stack;
}
private boolean matchesGunId(ItemStack stack, UUID gunId) { private boolean matchesGunId(ItemStack stack, UUID gunId) {
if (stack.isEmpty() || stack.getItem() != this) { if (stack.isEmpty() || stack.getItem() != this) {
return false; return false;
@@ -767,21 +718,16 @@ public class PortalGunItem extends Item implements GeoItem {
return UseAnim.NONE; return UseAnim.NONE;
} }
private Component portalColorName(PortalGunPortalEntity.PortalSide side) {
return Component.translatable(this.variant.colorTranslationKey(side))
.withStyle(style -> style.withColor(net.minecraft.network.chat.TextColor.fromRgb(this.variant.color(side))));
}
@Override @Override
public void appendHoverText(ItemStack stack, Item.TooltipContext context, List<Component> tooltipComponents, TooltipFlag tooltipFlag) { public void appendHoverText(ItemStack stack, Item.TooltipContext context, List<Component> tooltipComponents, TooltipFlag tooltipFlag) {
tooltipComponents.add(Component.translatable("tooltip.antarchy.portal_gun.primary").withStyle(ChatFormatting.GRAY)); tooltipComponents.add(Component.translatable("tooltip.antarchy.portal_gun.primary", this.portalColorName(PortalGunPortalEntity.PortalSide.BLUE)).withStyle(ChatFormatting.GRAY));
tooltipComponents.add(Component.translatable("tooltip.antarchy.portal_gun.secondary").withStyle(ChatFormatting.GRAY)); tooltipComponents.add(Component.translatable("tooltip.antarchy.portal_gun.secondary", this.portalColorName(PortalGunPortalEntity.PortalSide.ORANGE)).withStyle(ChatFormatting.GRAY));
String ownerName = getPortalOwnerName(stack);
if (!ownerName.isEmpty()) {
tooltipComponents.add(Component.translatable("tooltip.antarchy.portal_gun.owner", ownerName).withStyle(ChatFormatting.GRAY));
}
String channelName = getChannelName(stack);
if (!channelName.isEmpty()) {
tooltipComponents.add(Component.translatable("tooltip.antarchy.portal_gun.channel", channelName).withStyle(ChatFormatting.GRAY));
}
tooltipComponents.add(Component.translatable("tooltip.antarchy.portal_gun.reset").withStyle(ChatFormatting.DARK_GRAY)); tooltipComponents.add(Component.translatable("tooltip.antarchy.portal_gun.reset").withStyle(ChatFormatting.DARK_GRAY));
tooltipComponents.add(Component.translatable("tooltip.antarchy.portal_gun.size", getPortalWidth(stack), getPortalHeight(stack)).withStyle(ChatFormatting.GRAY));
tooltipComponents.add(Component.translatable("tooltip.antarchy.portal_gun.grab_strength", getGrabStrength(stack)).withStyle(ChatFormatting.GRAY));
} }
@Override @Override
@@ -809,7 +755,7 @@ public class PortalGunItem extends Item implements GeoItem {
@Override @Override
public net.minecraft.client.renderer.BlockEntityWithoutLevelRenderer getGeoItemRenderer() { public net.minecraft.client.renderer.BlockEntityWithoutLevelRenderer getGeoItemRenderer() {
if (this.renderer == null) { if (this.renderer == null) {
this.renderer = new AnimatedHeldItemRenderer<>(new ResourceBackedGeoItemModel<>(MODEL_LOCATION, TEXTURE_LOCATION, ANIMATION_LOCATION)); this.renderer = new AnimatedHeldItemRenderer<>(new ResourceBackedGeoItemModel<>(MODEL_LOCATION, PortalGunItem.this.variant.modelTexture(), ANIMATION_LOCATION));
} }
return this.renderer; return this.renderer;
} }
@@ -1,17 +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;
public record PortalGunGrabPayload() implements CustomPacketPayload {
public static final Type<PortalGunGrabPayload> TYPE = new Type<>(ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "portal_gun_grab"));
public static final StreamCodec<ByteBuf, PortalGunGrabPayload> STREAM_CODEC = StreamCodec.unit(new PortalGunGrabPayload());
@Override
public Type<PortalGunGrabPayload> type() {
return TYPE;
}
}
@@ -1,20 +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 PortalGunGrabStatePayload(boolean active) implements CustomPacketPayload {
public static final Type<PortalGunGrabStatePayload> TYPE = new Type<>(ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "portal_gun_grab_state"));
public static final StreamCodec<ByteBuf, PortalGunGrabStatePayload> STREAM_CODEC = StreamCodec.composite(
ByteBufCodecs.BOOL, PortalGunGrabStatePayload::active, PortalGunGrabStatePayload::new
);
@Override
public Type<PortalGunGrabStatePayload> type() {
return TYPE;
}
}
@@ -0,0 +1,43 @@
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;
}
}
@@ -43,9 +43,6 @@ public final class PortalGunCollisionHelper {
return collided; return collided;
} }
PortalGunWorldPortalShape shape = portal.getWorldPortalShape(); PortalGunWorldPortalShape shape = portal.getWorldPortalShape();
if (canWalkIntoFloorPortal(startBox, movement, portal, shape)) {
return movement;
}
Vec3 normal = portal.getNormalVec().normalize(); Vec3 normal = portal.getNormalVec().normalize();
double desiredNormal = movement.dot(normal); double desiredNormal = movement.dot(normal);
double collidedNormal = collided.dot(normal); double collidedNormal = collided.dot(normal);
@@ -60,29 +57,6 @@ public final class PortalGunCollisionHelper {
return applyBorderCollision(entity, startBox, adjusted, portal); return applyBorderCollision(entity, startBox, adjusted, portal);
} }
private static boolean canWalkIntoFloorPortal(AABB startBox, Vec3 movement, PortalGunPortalEntity portal, PortalGunWorldPortalShape shape) {
Vec3 normal = portal.getNormalVec().normalize();
if (Math.abs(normal.y) < 0.9D) {
return false;
}
AABB endBox = startBox.move(movement);
return overlapsPortalOpeningFromSurface(startBox, shape) || overlapsPortalOpeningFromSurface(endBox, shape);
}
private static boolean overlapsPortalOpeningFromSurface(AABB box, PortalGunWorldPortalShape shape) {
if (!shape.intersectsPortalColumn(box, EDGE_PADDING, EDGE_PADDING, PLANE_TOLERANCE)) {
return false;
}
double minDepth = Double.POSITIVE_INFINITY;
double maxDepth = Double.NEGATIVE_INFINITY;
for (Vec3 corner : corners(box)) {
double depth = shape.localCoords(corner).depth();
minDepth = Math.min(minDepth, depth);
maxDepth = Math.max(maxDepth, depth);
}
return maxDepth > 0.0D && minDepth >= -PLANE_TOLERANCE;
}
private static boolean canUsePortalPassThrough(Entity entity, AABB startBox, Vec3 movement, PortalGunPortalEntity portal, PortalGunWorldPortalShape shape) { private static boolean canUsePortalPassThrough(Entity entity, AABB startBox, Vec3 movement, PortalGunPortalEntity portal, PortalGunWorldPortalShape shape) {
AABB endBox = startBox.move(movement); AABB endBox = startBox.move(movement);
if (!portal.getPortalInsides(entity).intersects(startBox) if (!portal.getPortalInsides(entity).intersects(startBox)
@@ -1,280 +0,0 @@
package com.craisinlord.antarchy.content.portalgun;
import com.craisinlord.antarchy.content.item.PortalGunItem;
import com.craisinlord.antarchy.content.AntarchyTags;
import com.craisinlord.antarchy.Antarchy;
import com.craisinlord.antarchy.config.AntarchySettings;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.core.BlockPos;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
import net.minecraft.world.level.block.state.properties.DoubleBlockHalf;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.world.entity.item.FallingBlockEntity;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.MoverType;
import net.minecraft.world.entity.projectile.AbstractHurtingProjectile;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.tags.BlockTags;
import net.minecraft.world.level.block.RenderShape;
import net.minecraft.world.level.ClipContext;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.EntityHitResult;
import net.minecraft.world.phys.HitResult;
import net.minecraft.world.phys.Vec3;
import java.util.function.BiConsumer;
import com.craisinlord.antarchy.content.network.PortalGunGrabStatePayload;
public final class PortalGunGrabManager {
private static final double REACH = 5.0D;
private static final double BASE_HOLD_DISTANCE = 3.5D;
private static final double MAX_HOLD_DISTANCE = 6.0D;
private static final Map<UUID, GrabState> GRABS = new HashMap<>();
private static BiConsumer<ServerPlayer, PortalGunGrabStatePayload> stateSender = (player, payload) -> {};
private PortalGunGrabManager() {
}
public static void setStateSender(BiConsumer<ServerPlayer, PortalGunGrabStatePayload> sender) {
stateSender = sender;
}
public static void toggle(ServerPlayer player) {
GrabState active = GRABS.remove(player.getUUID());
if (active != null) {
release(active);
stateSender.accept(player, new PortalGunGrabStatePayload(false));
return;
}
ItemStack gunStack = getHeldPortalGun(player);
if (gunStack.isEmpty()) {
return;
}
int grabStrength = PortalGunItem.getGrabStrength(gunStack);
Entity target = findTarget(player, grabStrength);
if (target == null) {
target = findBlockTarget(player, grabStrength);
if (target == null) {
return;
}
}
for (Map.Entry<UUID, GrabState> entry : new HashMap<>(GRABS).entrySet()) {
if (entry.getValue().target() == target) {
GRABS.remove(entry.getKey());
release(entry.getValue());
ServerPlayer previousHolder = player.serverLevel().getServer().getPlayerList().getPlayer(entry.getKey());
if (previousHolder != null) {
stateSender.accept(previousHolder, new PortalGunGrabStatePayload(false));
}
}
}
double holdDistance = BASE_HOLD_DISTANCE;
if (target.getBbWidth() > 2.0F) {
holdDistance = Math.min(MAX_HOLD_DISTANCE, holdDistance + target.getBbWidth() - 2.0D);
}
GrabState state = new GrabState(player.getUUID(), player.serverLevel(), target, holdDistance, target.isNoGravity());
GRABS.put(player.getUUID(), state);
target.setNoGravity(true);
stateSender.accept(player, new PortalGunGrabStatePayload(true));
}
public static void tick(MinecraftServer server) {
for (Map.Entry<UUID, GrabState> entry : new HashMap<>(GRABS).entrySet()) {
GrabState state = entry.getValue();
ServerPlayer player = server.getPlayerList().getPlayer(state.playerId());
Entity target = state.target();
if (player == null || !player.isAlive() || player.serverLevel() != state.level() || !isHoldingPortalGun(player)
|| !target.isAlive() || target.isRemoved() || target.level() != state.level()
|| target.distanceTo(player) > state.holdDistance() + 6.25D) {
GRABS.remove(entry.getKey());
release(state);
if (player != null) {
stateSender.accept(player, new PortalGunGrabStatePayload(false));
}
continue;
}
Vec3 holdPosition = player.getEyePosition(1.0F).add(player.getLookAngle().scale(state.holdDistance()));
if (target.position().distanceTo(holdPosition) > 5.0D) {
target.teleportTo(player.getX(), player.getY(), player.getZ());
}
Vec3 velocity = holdPosition.subtract(target.getX(), target.getBoundingBox().getCenter().y, target.getZ());
target.setDeltaMovement(velocity);
target.move(MoverType.SELF, velocity);
velocity = holdPosition.subtract(target.getX(), target.getBoundingBox().getCenter().y, target.getZ());
target.setDeltaMovement(velocity);
target.setNoGravity(true);
if (target instanceof FallingBlockEntity fallingBlock) {
fallingBlock.time = 2;
}
if (target instanceof AbstractHurtingProjectile projectile) {
projectile.accelerationPower = 0.0D;
}
target.fallDistance = (float) -(velocity.y * velocity.y);
target.setOnGround(false);
target.hasImpulse = true;
target.hurtMarked = true;
}
}
public static void clear(MinecraftServer server) {
for (GrabState state : GRABS.values()) {
release(state);
ServerPlayer player = server.getPlayerList().getPlayer(state.playerId());
if (player != null) {
stateSender.accept(player, new PortalGunGrabStatePayload(false));
}
}
GRABS.clear();
}
private static Entity findTarget(ServerPlayer player, int grabStrength) {
Vec3 start = player.getEyePosition();
Vec3 look = player.getLookAngle();
Vec3 end = start.add(look.scale(REACH));
BlockHitResult blockHit = player.serverLevel().clip(new ClipContext(start, end, ClipContext.Block.COLLIDER, ClipContext.Fluid.NONE, player));
double maxDistance = blockHit.getType() == HitResult.Type.BLOCK ? start.distanceTo(blockHit.getLocation()) : REACH;
AABB searchBounds = player.getBoundingBox().expandTowards(look.scale(maxDistance)).inflate(1.0D);
EntityHitResult closest = null;
double closestDistance = maxDistance * maxDistance;
for (Entity candidate : player.serverLevel().getEntities(player, searchBounds, entity -> canGrab(entity, grabStrength))) {
var hit = candidate.getBoundingBox().inflate(candidate.getPickRadius()).clip(start, end);
if (hit.isEmpty()) {
continue;
}
double distance = start.distanceToSqr(hit.get());
if (distance < closestDistance) {
closestDistance = distance;
closest = new EntityHitResult(candidate, hit.get());
}
}
return closest == null ? null : closest.getEntity();
}
private static Entity findBlockTarget(ServerPlayer player, int grabStrength) {
if (grabStrength < 2) {
return null;
}
Vec3 start = player.getEyePosition();
Vec3 end = start.add(player.getLookAngle().scale(REACH));
BlockHitResult hit = player.serverLevel().clip(new ClipContext(start, end, ClipContext.Block.OUTLINE, ClipContext.Fluid.NONE, player));
if (hit.getType() != HitResult.Type.BLOCK) {
return null;
}
BlockPos pos = hit.getBlockPos();
ServerLevel level = player.serverLevel();
BlockState state = level.getBlockState(pos);
if (state.isAir() || state.is(AntarchyTags.Blocks.GRAVITY_GUN_BLACKLIST)) {
return null;
}
BlockPos basePos = pos;
BlockPos upperPos = null;
if (state.hasProperty(BlockStateProperties.DOUBLE_BLOCK_HALF)) {
basePos = state.getValue(BlockStateProperties.DOUBLE_BLOCK_HALF) == DoubleBlockHalf.UPPER ? pos.below() : pos;
BlockState baseState = level.getBlockState(basePos);
BlockPos candidateUpper = basePos.above();
BlockState upperState = level.getBlockState(candidateUpper);
if (!baseState.is(state.getBlock()) || !baseState.hasProperty(BlockStateProperties.DOUBLE_BLOCK_HALF)
|| baseState.getValue(BlockStateProperties.DOUBLE_BLOCK_HALF) != DoubleBlockHalf.LOWER
|| !upperState.is(state.getBlock()) || !upperState.hasProperty(BlockStateProperties.DOUBLE_BLOCK_HALF)
|| upperState.getValue(BlockStateProperties.DOUBLE_BLOCK_HALF) != DoubleBlockHalf.UPPER) {
return null;
}
state = baseState;
upperPos = candidateUpper;
}
if (state.getDestroySpeed(level, basePos) < 0.0F) {
return null;
}
if (AntarchySettings.portalGunEntityGrabWeightBase() == 0
&& !Antarchy.MODID.equals(BuiltInRegistries.BLOCK.getKey(state.getBlock()).getNamespace())) {
return null;
}
BlockEntity blockEntity = level.getBlockEntity(basePos);
if (blockEntity != null && grabStrength < 3) {
return null;
}
if (!state.isSolidRender(level, basePos)
&& state.getRenderShape() != RenderShape.ENTITYBLOCK_ANIMATED
&& !state.is(Blocks.SNOW)
&& !state.is(Blocks.GLOWSTONE)
&& !state.is(Blocks.CAKE)
&& !state.is(Blocks.TNT)
&& !state.is(Blocks.ICE)
&& !state.is(BlockTags.SLABS)
&& !state.is(Blocks.ANVIL)
&& !state.is(BlockTags.STAIRS)) {
return null;
}
CompoundTag blockEntityData = blockEntity == null ? null : blockEntity.saveWithId(level.registryAccess());
if (blockEntity != null) {
level.removeBlockEntity(basePos);
}
if (upperPos != null) {
level.removeBlock(upperPos, false);
}
FallingBlockEntity fallingBlock = FallingBlockEntity.fall(level, basePos, state);
if (fallingBlock == null) {
return null;
}
if (blockEntityData != null) {
fallingBlock.blockData = blockEntityData;
}
fallingBlock.setDeltaMovement(Vec3.ZERO);
fallingBlock.setNoGravity(true);
fallingBlock.hurtMarked = true;
return fallingBlock;
}
private static boolean canGrab(Entity entity, int grabStrength) {
if (!entity.isAlive() || entity instanceof Player || entity instanceof PortalGunPortalEntity || entity.isPassenger()) {
return false;
}
double volume = entity.getBbWidth() * entity.getBbHeight() * entity.getBbWidth();
double strengthScale = switch (grabStrength) {
case 1 -> 1.0D;
case 2 -> 1.5D;
case 3 -> 2.25D;
default -> 3.0D;
};
int grabWeightBase = AntarchySettings.portalGunEntityGrabWeightBase();
boolean modEntityExempt = grabWeightBase == 0
&& Antarchy.MODID.equals(BuiltInRegistries.ENTITY_TYPE.getKey(entity.getType()).getNamespace());
return (modEntityExempt || Math.cbrt(volume) < grabWeightBase * strengthScale / 100.0D) && entity.isPickable();
}
private static ItemStack getHeldPortalGun(ServerPlayer player) {
if (player.getMainHandItem().getItem() instanceof PortalGunItem) {
return player.getMainHandItem();
}
if (player.getOffhandItem().getItem() instanceof PortalGunItem) {
return player.getOffhandItem();
}
return ItemStack.EMPTY;
}
private static boolean isHoldingPortalGun(ServerPlayer player) {
return !getHeldPortalGun(player).isEmpty();
}
private static void release(GrabState state) {
Entity target = state.target();
if (target.isAlive() && !target.isRemoved()) {
target.setNoGravity(state.hadNoGravity());
target.hasImpulse = true;
target.hurtMarked = true;
}
}
private record GrabState(UUID playerId, ServerLevel level, Entity target, double holdDistance, boolean hadNoGravity) {
}
}
@@ -27,20 +27,15 @@ public final class PortalGunIndicatorSync {
if (stack.isEmpty()) { if (stack.isEmpty()) {
return; return;
} }
UUID ownerId = PortalGunItem.getPortalOwnerId(stack, player.getUUID());
boolean blue = PortalGunSavedData.getPortalId( boolean blue = PortalGunSavedData.getPortalId(
player.serverLevel().getServer(), player.serverLevel().getServer(),
ownerId,
request.gunId(), request.gunId(),
PortalGunItem.getChannelName(stack),
PortalGunPortalEntity.PortalSide.BLUE, PortalGunPortalEntity.PortalSide.BLUE,
player.serverLevel().dimension().location() player.serverLevel().dimension().location()
).isPresent(); ).isPresent();
boolean orange = PortalGunSavedData.getPortalId( boolean orange = PortalGunSavedData.getPortalId(
player.serverLevel().getServer(), player.serverLevel().getServer(),
ownerId,
request.gunId(), request.gunId(),
PortalGunItem.getChannelName(stack),
PortalGunPortalEntity.PortalSide.ORANGE, PortalGunPortalEntity.PortalSide.ORANGE,
player.serverLevel().dimension().location() player.serverLevel().dimension().location()
).isPresent(); ).isPresent();
@@ -1,22 +1,26 @@
package com.craisinlord.antarchy.content.portalgun; package com.craisinlord.antarchy.content.portalgun;
import com.craisinlord.antarchy.Antarchy; import com.craisinlord.antarchy.Antarchy;
import com.craisinlord.antarchy.config.AntarchySettings;
import com.craisinlord.antarchy.content.gravity.AntarchyGravityApi;
import com.craisinlord.antarchy.content.block.entity.PortalGunPortalBaseBlockEntity; import com.craisinlord.antarchy.content.block.entity.PortalGunPortalBaseBlockEntity;
import com.craisinlord.antarchy.content.block.entity.PortalGunPortalCellAccess; import com.craisinlord.antarchy.content.block.entity.PortalGunPortalCellAccess;
import com.craisinlord.antarchy.content.block.entity.PortalGunPortalFaceRecord; import com.craisinlord.antarchy.content.block.entity.PortalGunPortalFaceRecord;
import com.craisinlord.antarchy.content.block.entity.PortalGunPortalMasterBlockEntity; import com.craisinlord.antarchy.content.block.entity.PortalGunPortalMasterBlockEntity;
import com.craisinlord.antarchy.mixins.AbstractArrowAccessor; import com.craisinlord.antarchy.mixins.AbstractArrowAccessor;
import com.craisinlord.antarchy.mixins.FallingBlockEntityAccessor; import com.craisinlord.antarchy.mixins.FallingBlockEntityAccessor;
import com.craisinlord.antarchy.mixins.ServerGamePacketListenerTeleportAccessor;
import com.craisinlord.antarchy.mixins.gravity.ChunkMapAccessor;
import com.craisinlord.antarchy.mixins.gravity.ServerEntityAccessor;
import com.craisinlord.antarchy.mixins.gravity.TrackedEntityAccessor;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.Random;
import java.util.Set; import java.util.Set;
import java.util.UUID; import java.util.UUID;
import java.awt.Color;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction; import net.minecraft.core.Direction;
import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.core.registries.BuiltInRegistries;
@@ -40,11 +44,13 @@ import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.player.Player;
import net.minecraft.world.entity.projectile.AbstractArrow; import net.minecraft.world.entity.projectile.AbstractArrow;
import net.minecraft.world.entity.projectile.Projectile;
import net.minecraft.world.entity.item.FallingBlockEntity; import net.minecraft.world.entity.item.FallingBlockEntity;
import net.minecraft.world.level.Level; import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.Vec3;
import software.bernie.geckolib.animatable.GeoEntity; import software.bernie.geckolib.animatable.GeoEntity;
import software.bernie.geckolib.animatable.instance.AnimatableInstanceCache; import software.bernie.geckolib.animatable.instance.AnimatableInstanceCache;
@@ -64,14 +70,11 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity {
private static final EntityDataAccessor<Integer> SIDE = SynchedEntityData.defineId(PortalGunPortalEntity.class, EntityDataSerializers.INT); private static final EntityDataAccessor<Integer> SIDE = SynchedEntityData.defineId(PortalGunPortalEntity.class, EntityDataSerializers.INT);
private static final EntityDataAccessor<Optional<UUID>> OWNER_ID = SynchedEntityData.defineId(PortalGunPortalEntity.class, EntityDataSerializers.OPTIONAL_UUID); private static final EntityDataAccessor<Optional<UUID>> OWNER_ID = SynchedEntityData.defineId(PortalGunPortalEntity.class, EntityDataSerializers.OPTIONAL_UUID);
private static final EntityDataAccessor<Optional<UUID>> GUN_ID = SynchedEntityData.defineId(PortalGunPortalEntity.class, EntityDataSerializers.OPTIONAL_UUID); private static final EntityDataAccessor<Optional<UUID>> GUN_ID = SynchedEntityData.defineId(PortalGunPortalEntity.class, EntityDataSerializers.OPTIONAL_UUID);
private static final EntityDataAccessor<String> CHANNEL_NAME = SynchedEntityData.defineId(PortalGunPortalEntity.class, EntityDataSerializers.STRING);
private static final EntityDataAccessor<Integer> FACING = SynchedEntityData.defineId(PortalGunPortalEntity.class, EntityDataSerializers.INT); private static final EntityDataAccessor<Integer> FACING = SynchedEntityData.defineId(PortalGunPortalEntity.class, EntityDataSerializers.INT);
private static final EntityDataAccessor<Integer> UP_AXIS = SynchedEntityData.defineId(PortalGunPortalEntity.class, EntityDataSerializers.INT); private static final EntityDataAccessor<Integer> UP_AXIS = SynchedEntityData.defineId(PortalGunPortalEntity.class, EntityDataSerializers.INT);
private static final EntityDataAccessor<Optional<UUID>> LINKED_PORTAL = SynchedEntityData.defineId(PortalGunPortalEntity.class, EntityDataSerializers.OPTIONAL_UUID); private static final EntityDataAccessor<Optional<UUID>> LINKED_PORTAL = SynchedEntityData.defineId(PortalGunPortalEntity.class, EntityDataSerializers.OPTIONAL_UUID);
private static final EntityDataAccessor<Integer> LINKED_PORTAL_ENTITY_ID = SynchedEntityData.defineId(PortalGunPortalEntity.class, EntityDataSerializers.INT); private static final EntityDataAccessor<Integer> LINKED_PORTAL_ENTITY_ID = SynchedEntityData.defineId(PortalGunPortalEntity.class, EntityDataSerializers.INT);
private static final EntityDataAccessor<Integer> CHANNEL_RED = SynchedEntityData.defineId(PortalGunPortalEntity.class, EntityDataSerializers.INT); private static final EntityDataAccessor<Integer> VARIANT = SynchedEntityData.defineId(PortalGunPortalEntity.class, EntityDataSerializers.INT);
private static final EntityDataAccessor<Integer> CHANNEL_GREEN = SynchedEntityData.defineId(PortalGunPortalEntity.class, EntityDataSerializers.INT);
private static final EntityDataAccessor<Integer> CHANNEL_BLUE = SynchedEntityData.defineId(PortalGunPortalEntity.class, EntityDataSerializers.INT);
private static final EntityDataAccessor<Integer> PORTAL_WIDTH = SynchedEntityData.defineId(PortalGunPortalEntity.class, EntityDataSerializers.INT); private static final EntityDataAccessor<Integer> PORTAL_WIDTH = SynchedEntityData.defineId(PortalGunPortalEntity.class, EntityDataSerializers.INT);
private static final EntityDataAccessor<Integer> PORTAL_HEIGHT = SynchedEntityData.defineId(PortalGunPortalEntity.class, EntityDataSerializers.INT); private static final EntityDataAccessor<Integer> PORTAL_HEIGHT = SynchedEntityData.defineId(PortalGunPortalEntity.class, EntityDataSerializers.INT);
private static final EntityDataAccessor<Integer> OPENING_ANIMATION_ID = SynchedEntityData.defineId(PortalGunPortalEntity.class, EntityDataSerializers.INT); private static final EntityDataAccessor<Integer> OPENING_ANIMATION_ID = SynchedEntityData.defineId(PortalGunPortalEntity.class, EntityDataSerializers.INT);
@@ -81,14 +84,14 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity {
public static final int TELEPORT_COOLDOWN_TICKS = 3; public static final int TELEPORT_COOLDOWN_TICKS = 3;
private static final String DEBUG_TRAVERSAL_PROPERTY = "antarchy.portalGun.debugTraversal"; private static final String DEBUG_TRAVERSAL_PROPERTY = "antarchy.portalGun.debugTraversal";
private static final double PORTAL_INSIDE_SHIFT = 0.05D; private static final double PORTAL_INSIDE_SHIFT = 0.05D;
private static final Set<String> LOGGED_CROSSING_PATHS = new HashSet<>();
private static final double HALF_DEPTH = 0.35D; private static final double HALF_DEPTH = 0.35D;
private static final double MAX_CLIENT_TRANSIT_START_ERROR_SQR = 1.0D;
private static final double MAX_CLIENT_TRANSIT_MOVEMENT_SQR = 100.0D;
private static final double PROJECTILE_PLANE_TOLERANCE = 0.1D;
private final AnimatableInstanceCache geoCache = GeckoLibUtil.createInstanceCache(this); private final AnimatableInstanceCache geoCache = GeckoLibUtil.createInstanceCache(this);
private final Map<UUID, Integer> teleportCooldowns = new HashMap<>(); private final Map<UUID, Integer> teleportCooldowns = new HashMap<>();
private UUID ownerId; private UUID ownerId;
private String ownerIdentity;
private UUID gunId; private UUID gunId;
private String channelName;
private UUID linkedPortalId; private UUID linkedPortalId;
private int ageTicks; private int ageTicks;
private int lastOpeningAnimationId; private int lastOpeningAnimationId;
@@ -98,6 +101,12 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity {
private BlockPos basePos = BlockPos.ZERO; private BlockPos basePos = BlockPos.ZERO;
private BlockPos[] portalSpots = new BlockPos[] {BlockPos.ZERO, BlockPos.ZERO}; private BlockPos[] portalSpots = new BlockPos[] {BlockPos.ZERO, BlockPos.ZERO};
private Set<BlockPos> compensatedSpots = Set.of(); private Set<BlockPos> compensatedSpots = Set.of();
private PortalGunWorldPortalShape cachedShape;
private Vec3 cachedShapePosition;
private int cachedShapeFacing;
private int cachedShapeUpAxis;
private int cachedShapeWidth;
private int cachedShapeHeight;
public PortalGunPortalEntity(EntityType<? extends PortalGunPortalEntity> entityType, Level level) { public PortalGunPortalEntity(EntityType<? extends PortalGunPortalEntity> entityType, Level level) {
super(entityType, level); super(entityType, level);
@@ -111,36 +120,27 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity {
builder.define(SIDE, PortalSide.BLUE.ordinal()); builder.define(SIDE, PortalSide.BLUE.ordinal());
builder.define(OWNER_ID, Optional.empty()); builder.define(OWNER_ID, Optional.empty());
builder.define(GUN_ID, Optional.empty()); builder.define(GUN_ID, Optional.empty());
builder.define(CHANNEL_NAME, "");
builder.define(FACING, Direction.NORTH.get3DDataValue()); builder.define(FACING, Direction.NORTH.get3DDataValue());
builder.define(UP_AXIS, Direction.UP.get3DDataValue()); builder.define(UP_AXIS, Direction.UP.get3DDataValue());
builder.define(LINKED_PORTAL, Optional.empty()); builder.define(LINKED_PORTAL, Optional.empty());
builder.define(LINKED_PORTAL_ENTITY_ID, -1); builder.define(LINKED_PORTAL_ENTITY_ID, -1);
builder.define(CHANNEL_RED, 58); builder.define(VARIANT, PortalGunVariant.DEFAULT.id());
builder.define(CHANNEL_GREEN, 166);
builder.define(CHANNEL_BLUE, 255);
builder.define(PORTAL_WIDTH, 1); builder.define(PORTAL_WIDTH, 1);
builder.define(PORTAL_HEIGHT, 2); builder.define(PORTAL_HEIGHT, 2);
builder.define(OPENING_ANIMATION_ID, 0); builder.define(OPENING_ANIMATION_ID, 0);
} }
public void configure(UUID ownerId, UUID gunId, String ownerIdentity, String channelName, PortalSide side, PortalGunPlacement placement) { public void configure(UUID ownerId, UUID gunId, PortalGunVariant variant, PortalSide side, PortalGunPlacement placement) {
this.ownerId = ownerId; this.ownerId = ownerId;
this.ownerIdentity = ownerIdentity;
this.gunId = gunId; this.gunId = gunId;
this.channelName = channelName;
this.entityData.set(OWNER_ID, Optional.ofNullable(ownerId)); this.entityData.set(OWNER_ID, Optional.ofNullable(ownerId));
this.entityData.set(GUN_ID, Optional.ofNullable(gunId)); this.entityData.set(GUN_ID, Optional.ofNullable(gunId));
this.entityData.set(CHANNEL_NAME, channelName == null ? "" : channelName); this.entityData.set(VARIANT, (variant == null ? PortalGunVariant.DEFAULT : variant).id());
this.entityData.set(SIDE, side.ordinal()); this.entityData.set(SIDE, side.ordinal());
this.entityData.set(FACING, placement.facing().get3DDataValue()); this.entityData.set(FACING, placement.facing().get3DDataValue());
this.entityData.set(UP_AXIS, placement.upAxis().get3DDataValue()); this.entityData.set(UP_AXIS, placement.upAxis().get3DDataValue());
this.entityData.set(PORTAL_WIDTH, placement.width()); this.entityData.set(PORTAL_WIDTH, placement.width());
this.entityData.set(PORTAL_HEIGHT, placement.height()); this.entityData.set(PORTAL_HEIGHT, placement.height());
int[] channelColor = generateChannelColor(ownerIdentity, channelName, side);
this.entityData.set(CHANNEL_RED, channelColor[0]);
this.entityData.set(CHANNEL_GREEN, channelColor[1]);
this.entityData.set(CHANNEL_BLUE, channelColor[2]);
this.supportOrigin = placement.supportOrigin().immutable(); this.supportOrigin = placement.supportOrigin().immutable();
this.masterPos = placement.masterPos().immutable(); this.masterPos = placement.masterPos().immutable();
this.basePos = placement.basePos().immutable(); this.basePos = placement.basePos().immutable();
@@ -191,50 +191,12 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity {
return this.entityData.get(GUN_ID).orElse(this.gunId); return this.entityData.get(GUN_ID).orElse(this.gunId);
} }
public String getChannelName() { public PortalGunVariant getVariant() {
String synchronizedName = this.entityData.get(CHANNEL_NAME); return PortalGunVariant.byId(this.entityData.get(VARIANT));
return synchronizedName.isEmpty() ? this.channelName : synchronizedName;
} }
public void adoptChannelIdentity(UUID gunId, String channelName) { public int getPortalColor() {
if (this.gunId == null && gunId != null) { return this.getVariant().color(this.getPortalSide());
this.gunId = gunId;
this.entityData.set(GUN_ID, Optional.of(gunId));
}
if (channelName != null && !channelName.isEmpty() && !channelName.equals(this.channelName)) {
this.channelName = channelName;
this.entityData.set(CHANNEL_NAME, channelName);
}
if (this.level() instanceof ServerLevel serverLevel && serverLevel.hasChunkAt(this.masterPos)
&& serverLevel.getBlockEntity(this.masterPos) instanceof PortalGunPortalMasterBlockEntity master
&& this.getUUID().equals(master.getPortalId())) {
master.adoptChannelIdentity(gunId, channelName);
}
}
public void adoptGunId(UUID gunId) {
if (this.gunId != null || gunId == null) {
return;
}
this.gunId = gunId;
this.entityData.set(GUN_ID, Optional.of(gunId));
if (this.level() instanceof ServerLevel serverLevel && serverLevel.hasChunkAt(this.masterPos)
&& serverLevel.getBlockEntity(this.masterPos) instanceof com.craisinlord.antarchy.content.block.entity.PortalGunPortalMasterBlockEntity master
&& this.getUUID().equals(master.getPortalId())) {
master.adoptGunId(gunId);
}
}
public int getChannelRed() {
return this.entityData.get(CHANNEL_RED);
}
public int getChannelGreen() {
return this.entityData.get(CHANNEL_GREEN);
}
public int getChannelBlue() {
return this.entityData.get(CHANNEL_BLUE);
} }
public BlockPos getBasePos() { public BlockPos getBasePos() {
@@ -278,17 +240,10 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity {
if (linkedId == null) { if (linkedId == null) {
return null; return null;
} }
Entity linkedEntity;
if (this.level() instanceof ServerLevel serverLevel) { if (this.level() instanceof ServerLevel serverLevel) {
linkedEntity = serverLevel.getEntity(linkedId); return serverLevel.getEntity(linkedId) instanceof PortalGunPortalEntity portal && !portal.isRemoved() ? portal : null;
} else {
int linkedEntityId = this.entityData.get(LINKED_PORTAL_ENTITY_ID);
if (linkedEntityId < 0) {
return null;
}
linkedEntity = this.level().getEntity(linkedEntityId);
} }
return linkedEntity instanceof PortalGunPortalEntity portal && !portal.isRemoved() ? portal : null; return PortalGunPortalRegistry.find(this.level(), linkedId);
} }
public Vec3 getNormalVec() { public Vec3 getNormalVec() {
@@ -304,7 +259,23 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity {
} }
public PortalGunWorldPortalShape getWorldPortalShape() { public PortalGunWorldPortalShape getWorldPortalShape() {
return new PortalGunWorldPortalShape(this.position(), this.getNormalVec().normalize(), this.getUpVec().normalize(), this.getWidthVec().normalize(), this.getPortalWidth() / 2.0D, this.getPortalHeight() / 2.0D, HALF_DEPTH); Vec3 position = this.position();
int facing = this.entityData.get(FACING);
int upAxis = this.entityData.get(UP_AXIS);
int width = this.getPortalWidth();
int height = this.getPortalHeight();
PortalGunWorldPortalShape shape = this.cachedShape;
if (shape == null || !position.equals(this.cachedShapePosition) || facing != this.cachedShapeFacing
|| upAxis != this.cachedShapeUpAxis || width != this.cachedShapeWidth || height != this.cachedShapeHeight) {
shape = new PortalGunWorldPortalShape(position, this.getNormalVec().normalize(), this.getUpVec().normalize(), this.getWidthVec().normalize(), width / 2.0D, height / 2.0D, HALF_DEPTH);
this.cachedShape = shape;
this.cachedShapePosition = position;
this.cachedShapeFacing = facing;
this.cachedShapeUpAxis = upAxis;
this.cachedShapeWidth = width;
this.cachedShapeHeight = height;
}
return shape;
} }
public AABB getPlane() { public AABB getPlane() {
@@ -354,72 +325,66 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity {
return entity instanceof LivingEntity living ? living.getEyeHeight(living.getPose()) : entity.getEyeHeight(); return entity instanceof LivingEntity living ? living.getEyeHeight(living.getPose()) : entity.getEyeHeight();
} }
public PortalCrossing resolveCrossing(Entity entity, AABB previousBox, AABB currentBox) { public PortalCrossing resolveCrossing(Entity entity, AABB previousBox, AABB currentBox, Vec3 attemptedMovement) {
Vec3 previousProbe = this.teleportProbePosition(entity, previousBox); PortalGunWorldPortalShape shape = this.getWorldPortalShape();
Vec3 currentProbe = this.teleportProbePosition(entity, currentBox); if (!PortalGunTransitMath.crossesWithBody(shape, previousBox, currentBox, attemptedMovement)) {
if (entity instanceof Player) { return null;
PortalGunWorldPortalShape shape = this.getWorldPortalShape();
Vec3 center = currentBox.getCenter();
PortalGunWorldPortalShape.PortalLocalCoords localCenter = shape.localCoords(center);
double previousDepth = shape.localCoords(previousBox.getCenter()).depth();
double currentDepth = localCenter.depth();
double halfX = (currentBox.maxX - currentBox.minX) * 0.5D;
double halfY = (currentBox.maxY - currentBox.minY) * 0.5D;
double halfZ = (currentBox.maxZ - currentBox.minZ) * 0.5D;
double depthExtent = halfX * Math.abs(shape.normal().x) + halfY * Math.abs(shape.normal().y) + halfZ * Math.abs(shape.normal().z);
if (previousDepth <= 0.0D || currentDepth >= previousDepth - 1.0E-6D
|| currentDepth - depthExtent > 0.02D || currentDepth < -shape.halfDepth()) {
return null;
}
double widthExtent = halfX * Math.abs(shape.right().x) + halfY * Math.abs(shape.right().y) + halfZ * Math.abs(shape.right().z);
double heightExtent = halfX * Math.abs(shape.up().x) + halfY * Math.abs(shape.up().y) + halfZ * Math.abs(shape.up().z);
if (Math.abs(localCenter.horizontal()) + widthExtent > shape.halfWidth() + 1.0E-3D
|| Math.abs(localCenter.vertical()) + heightExtent > shape.halfHeight() + 1.0E-3D) {
return null;
}
return new PortalCrossing(currentProbe, Math.max(0.05D, shape.localCoords(currentProbe).depth() + 0.05D));
} }
return this.crossesPortal(previousProbe, currentProbe) ? new PortalCrossing(currentProbe, 0.0D) : null; return new PortalCrossing(this.teleportProbePosition(entity, currentBox));
} }
public Vec3 resolveCrossingProbe(Entity entity) { public PortalTransit computeTransit(PortalGunPortalEntity destination, AABB box, Vec3 velocity, Vec3 look) {
AABB currentBox = entity.getBoundingBox(); PortalGunWorldPortalShape source = this.getWorldPortalShape();
AABB previousBox = currentBox.move(entity.xo - entity.getX(), entity.yo - entity.getY(), entity.zo - entity.getZ()); PortalGunWorldPortalShape target = destination.getWorldPortalShape();
PortalCrossing crossing = this.resolveCrossing(entity, previousBox, currentBox); AABB exitBox = PortalGunTransitMath.exitBox(source, target, box);
return crossing == null ? null : crossing.probe(); Vec3 exitVelocity = PortalGunTransitMath.exitVelocity(source, target, velocity,
AntarchySettings.portalGunMinFloorExitSpeed(), AntarchySettings.portalGunMaxExitSpeed());
Vec3 exitLook = PortalGunTransitMath.transformVector(source, target, look).normalize();
return new PortalTransit(exitBox, PortalGunTransitMath.feetPosition(exitBox), exitVelocity, exitLook);
} }
public PortalTransitTransform previewTransit(Entity entity, PortalGunPortalEntity destination, PortalCrossing crossing, Vec3 movement) { public PortalTransit computeTransit(Entity entity, PortalGunPortalEntity destination, AABB box, Vec3 velocity, Vec3 look) {
Vec3 sourcePlaneCenter = this.position().add(this.getNormalVec().normalize().scale(crossing.normalOffset())); if (!AntarchyGravityApi.isGravityInverted(entity)) {
Vec3 destinationPlaneCenter = destination.position().add(destination.getNormalVec().normalize().scale(crossing.normalOffset())); return computeTransit(destination, box, velocity, look);
Vec3 relativeProbe = crossing.probe().subtract(sourcePlaneCenter); }
Vec3 transformedProbe = PortalGunTransformUtil.transformPosition(this, destination, relativeProbe); PortalGunWorldPortalShape source = this.getWorldPortalShape();
Vec3 entityOffset = PortalGunTransformUtil.transformVector(this, destination, entity.position().subtract(crossing.probe())); PortalGunWorldPortalShape target = destination.getWorldPortalShape();
Vec3 requestedPosition = destinationPlaneCenter.add(transformedProbe).add(entityOffset); AABB exitBox = PortalGunTransitMath.exitBox(source, target, box);
Vec3 transformedMovement = PortalGunTransformUtil.transformVector(this, destination, movement); Vec3 worldVelocity = PortalGunTransitMath.entityVelocityToWorld(entity, velocity);
Vec3 exitPosition = putEntityWithinExitBounds(entity, requestedPosition, legacyExitBounds(destination, entity, destination.getNormalVec().normalize())); Vec3 worldExitVelocity = PortalGunTransitMath.exitVelocity(source, target, worldVelocity,
return new PortalTransitTransform(requestedPosition, exitPosition, transformedMovement); AntarchySettings.portalGunMinFloorExitSpeed(), AntarchySettings.portalGunMaxExitSpeed());
Vec3 exitVelocity = PortalGunTransitMath.entityVelocityFromWorld(entity, worldExitVelocity);
Vec3 worldLook = PortalGunTransitMath.entityVelocityToWorld(entity, look);
Vec3 worldExitLook = PortalGunTransitMath.transformVector(source, target, worldLook).normalize();
Vec3 exitLook = PortalGunTransitMath.entityVelocityFromWorld(entity, worldExitLook);
return new PortalTransit(exitBox, PortalGunTransitMath.entityPosition(exitBox, entity), exitVelocity, exitLook);
} }
public static void teleportEntityAfterMovement(Entity entity, AABB previousBox, Vec3 resolvedMovement) { public static void teleportEntityAfterMovement(Entity entity, AABB previousBox, Vec3 velocity) {
if (entity instanceof ServerPlayer) { if (entity instanceof Player) {
return; return;
} }
teleportAfterMovement(entity, previousBox, entity.getBoundingBox(), resolvedMovement); teleportAfterMovement(entity, previousBox, entity.getBoundingBox(), velocity);
} }
public static void teleportPlayerAfterMovementPacket(ServerPlayer player, AABB previousBox, Vec3 claimedMovement) { public static void teleportPlayerAfterMovementPacket(ServerPlayer player, AABB previousBox, Vec3 claimedMovement) {
teleportAfterMovement(player, previousBox, previousBox.move(claimedMovement), claimedMovement); Vec3 transitVelocity = PortalGunTransitMath.entityVelocityFromWorld(player, claimedMovement);
teleportAfterMovement(player, previousBox, previousBox.move(claimedMovement), transitVelocity, claimedMovement);
} }
private static void teleportAfterMovement(Entity entity, AABB previousBox, AABB currentBox, Vec3 resolvedMovement) { private static void teleportAfterMovement(Entity entity, AABB previousBox, AABB currentBox, Vec3 velocity) {
teleportAfterMovement(entity, previousBox, currentBox, velocity,
PortalGunTransitMath.entityVelocityToWorld(entity, velocity));
}
private static void teleportAfterMovement(Entity entity, AABB previousBox, AABB currentBox, Vec3 velocity, Vec3 attemptedMovement) {
if (entity.level().isClientSide || !entity.isAlive() || entity.isPassenger() || entity instanceof PortalGunPortalEntity) { if (entity.level().isClientSide || !entity.isAlive() || entity.isPassenger() || entity instanceof PortalGunPortalEntity) {
return; return;
} }
if (previousBox.getCenter().distanceToSqr(currentBox.getCenter()) <= 1.0E-10D) { if (velocity.lengthSqr() <= 1.0E-12D && previousBox.getCenter().distanceToSqr(currentBox.getCenter()) <= 1.0E-10D) {
return; return;
} }
AABB sweptBounds = previousBox.minmax(currentBox).inflate(1.0D); AABB sweptBounds = previousBox.minmax(currentBox).expandTowards(attemptedMovement).inflate(1.0D);
for (PortalGunPortalEntity portal : findPortalsNearBounds(entity.level(), sweptBounds)) { for (PortalGunPortalEntity portal : findPortalsNearBounds(entity.level(), sweptBounds)) {
if (!(portal.level() instanceof ServerLevel serverLevel) || !portal.canTeleportEntity(entity)) { if (!(portal.level() instanceof ServerLevel serverLevel) || !portal.canTeleportEntity(entity)) {
continue; continue;
@@ -428,28 +393,132 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity {
if (linked == null || !linked.isAlive()) { if (linked == null || !linked.isAlive()) {
continue; continue;
} }
PortalCrossing crossing = portal.resolveCrossing(entity, previousBox, currentBox); PortalCrossing crossing = portal.resolveCrossing(entity, previousBox, currentBox, attemptedMovement);
if (crossing != null) { if (crossing != null) {
if (entity instanceof Player && Boolean.getBoolean(DEBUG_TRAVERSAL_PROPERTY)) { if (entity instanceof Player && Boolean.getBoolean(DEBUG_TRAVERSAL_PROPERTY)) {
Antarchy.LOGGER.info("Portal gun traversal accepted path=movement portal={} player={} probe={} offset={} linked={}", Antarchy.LOGGER.info("Portal gun traversal accepted path=server portal={} player={} probe={} linked={}",
portal.getUUID(), entity.getUUID(), crossing.probe(), crossing.normalOffset(), linked.getUUID()); portal.getUUID(), entity.getUUID(), crossing.probe(), linked.getUUID());
} }
logCrossingPath(entity, "movement"); portal.teleportEntity(entity, linked, currentBox, velocity, false);
portal.teleportEntity(entity, linked, crossing.probe(), resolvedMovement, crossing.normalOffset());
return; return;
} }
} }
} }
public static boolean handleClientTransit(ServerPlayer player, int portalId, Vec3 start, Vec3 end, Vec3 velocity) {
if (!player.isAlive() || player.isPassenger() || player.isSpectator()) {
return false;
}
if (((ServerGamePacketListenerTeleportAccessor) player.connection).antarchy$getAwaitingPositionFromClient() != null) {
return true;
}
if (!isFinite(start) || !isFinite(end) || !isFinite(velocity)
|| start.distanceToSqr(player.position()) > MAX_CLIENT_TRANSIT_START_ERROR_SQR
|| end.distanceToSqr(start) > MAX_CLIENT_TRANSIT_MOVEMENT_SQR
|| velocity.lengthSqr() > MAX_CLIENT_TRANSIT_MOVEMENT_SQR) {
return false;
}
if (!(player.level() instanceof ServerLevel serverLevel)
|| !(serverLevel.getEntity(portalId) instanceof PortalGunPortalEntity portal)
|| !portal.isAlive()) {
return false;
}
PortalGunPortalEntity linked = portal.findLinkedPortal(serverLevel);
if (linked == null || !linked.isAlive()) {
return false;
}
AABB startBox = player.getBoundingBox().move(start.subtract(player.position()));
AABB endBox = startBox.move(end.subtract(start));
Vec3 crossingVelocity = PortalGunTransitMath.entityVelocityToWorld(player, velocity);
if (portal.resolveCrossing(player, startBox, endBox, crossingVelocity) == null) {
return false;
}
if (Boolean.getBoolean(DEBUG_TRAVERSAL_PROPERTY)) {
Antarchy.LOGGER.info("Portal gun traversal accepted path=client portal={} player={} end={} velocity={} linked={}",
portal.getUUID(), player.getUUID(), end, velocity, linked.getUUID());
}
portal.teleportEntity(player, linked, endBox, velocity, true);
return true;
}
public static boolean passProjectileThroughPortal(Projectile projectile, BlockHitResult hit) {
if (projectile instanceof PortalGunProjectileEntity || projectile.isRemoved() || projectile.isPassenger()) {
return false;
}
Vec3 location = hit.getLocation();
Vec3 velocity = projectile.getDeltaMovement();
for (PortalGunPortalEntity portal : findPortalsNearBounds(projectile.level(), new AABB(location, location).inflate(0.5D))) {
if (portal.getFacingDirection() != hit.getDirection() || portal.isTeleportCoolingDown(projectile)) {
continue;
}
PortalGunWorldPortalShape shape = portal.getWorldPortalShape();
PortalGunWorldPortalShape.PortalLocalCoords coords = shape.localCoords(location);
if (Math.abs(coords.depth()) > PROJECTILE_PLANE_TOLERANCE
|| Math.abs(coords.horizontal()) > shape.halfWidth()
|| Math.abs(coords.vertical()) > shape.halfHeight()
|| velocity.dot(shape.normal()) >= 0.0D) {
continue;
}
PortalGunPortalEntity linked = projectile.level() instanceof ServerLevel serverLevel ? portal.findLinkedPortal(serverLevel) : portal.getLinkedPortal();
if (linked == null || !linked.isAlive()) {
continue;
}
AABB crossingBox = projectile.getBoundingBox().move(location.subtract(projectile.position()));
if (projectile.level().isClientSide) {
PortalTransit transit = portal.computeTransit(projectile, linked, crossingBox, velocity, projectile.getLookAngle());
Vec3 exit = transit.position();
projectile.setPos(exit.x, exit.y, exit.z);
projectile.xo = exit.x;
projectile.yo = exit.y;
projectile.zo = exit.z;
projectile.setDeltaMovement(transit.velocity());
orientAlongVelocity(projectile, transit.velocity());
projectile.setPortalCooldown(TELEPORT_COOLDOWN_TICKS);
} else {
portal.teleportEntity(projectile, linked, crossingBox, velocity, false);
}
return true;
}
return false;
}
private static void orientAlongVelocity(Entity entity, Vec3 velocity) {
if (velocity.lengthSqr() < 1.0E-12D) {
return;
}
float yaw = (float) Math.toDegrees(Math.atan2(velocity.x, velocity.z));
float pitch = (float) Math.toDegrees(Math.atan2(velocity.y, velocity.horizontalDistance()));
entity.setYRot(yaw);
entity.setXRot(pitch);
entity.yRotO = yaw;
entity.xRotO = pitch;
}
private static boolean isFinite(Vec3 vector) {
return Double.isFinite(vector.x) && Double.isFinite(vector.y) && Double.isFinite(vector.z);
}
public static List<PortalGunPortalEntity> findPortalsNearBounds(Level level, AABB bounds) { public static List<PortalGunPortalEntity> findPortalsNearBounds(Level level, AABB bounds) {
List<PortalGunPortalEntity> portals = new java.util.ArrayList<>(); List<PortalGunPortalEntity> portals = null;
AABB searchBounds = bounds.inflate(10.0D); for (PortalGunPortalEntity portal : PortalGunPortalRegistry.all(level)) {
for (PortalGunPortalEntity portal : level.getEntitiesOfClass(PortalGunPortalEntity.class, searchBounds, PortalGunPortalEntity::isAlive)) { if (portal.isAlive() && portal.getWorldPortalShape().getBoundsForCulling().intersects(bounds)) {
if (portal.getWorldPortalShape().getBoundsForCulling().intersects(bounds)) { if (portals == null) {
portals = new java.util.ArrayList<>(2);
}
portals.add(portal); portals.add(portal);
} }
} }
return portals; return portals == null ? List.of() : portals;
}
@Override
public void setLevelCallback(net.minecraft.world.level.entity.EntityInLevelCallback callback) {
super.setLevelCallback(callback);
if (callback == net.minecraft.world.level.entity.EntityInLevelCallback.NULL) {
PortalGunPortalRegistry.untrack(this);
} else {
PortalGunPortalRegistry.track(this);
}
} }
public boolean containsPoint(Vec3 position) { public boolean containsPoint(Vec3 position) {
@@ -490,6 +559,9 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity {
@Override @Override
public void tick() { public void tick() {
super.tick(); super.tick();
if (!this.isRemoved()) {
PortalGunPortalRegistry.track(this);
}
int openingAnimationId = this.entityData.get(OPENING_ANIMATION_ID); int openingAnimationId = this.entityData.get(OPENING_ANIMATION_ID);
if (openingAnimationId != this.lastOpeningAnimationId) { if (openingAnimationId != this.lastOpeningAnimationId) {
this.lastOpeningAnimationId = openingAnimationId; this.lastOpeningAnimationId = openingAnimationId;
@@ -505,16 +577,8 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity {
if (!(this.level() instanceof ServerLevel serverLevel)) { if (!(this.level() instanceof ServerLevel serverLevel)) {
return; return;
} }
if (this.ownerId != null) {
PortalGunSavedData.getChannelNameForPortal(serverLevel.getServer(), this.ownerId, serverLevel.dimension().location(), this.getUUID())
.ifPresent(channelName -> this.adoptChannelIdentity(this.gunId, channelName));
if (this.gunId == null) {
PortalGunSavedData.getGunIdForPortal(serverLevel.getServer(), this.ownerId, serverLevel.dimension().location(), this.getUUID())
.ifPresent(gunId -> this.adoptChannelIdentity(gunId, this.channelName));
}
}
this.tickTeleportCooldowns(); this.tickTeleportCooldowns();
if (this.ownerId == null || !this.isStillRegistered(serverLevel)) { if (this.gunId == null || !this.isStillRegistered(serverLevel)) {
this.discard(); this.discard();
return; return;
} }
@@ -540,7 +604,7 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity {
} }
private boolean isStillRegistered(ServerLevel level) { private boolean isStillRegistered(ServerLevel level) {
return this.ownerId != null && PortalGunSavedData.isRegistered(level, this.ownerId, this.gunId, this.channelName, this.getPortalSide(), this.getUUID()); return PortalGunSavedData.isRegistered(level, this.gunId, this.getPortalSide(), this.getUUID());
} }
private boolean hasValidSurface() { private boolean hasValidSurface() {
@@ -576,36 +640,15 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity {
} }
private void tickTeleport(PortalGunPortalEntity linked) { private void tickTeleport(PortalGunPortalEntity linked) {
List<Entity> entities = this.level().getEntities(this, this.getScanRange(), this::canTeleportEntity); List<Entity> entities = this.level().getEntities(this, this.getScanRange(), entity -> !(entity instanceof Player) && this.canTeleportEntity(entity));
for (Entity entity : entities) { for (Entity entity : entities) {
AABB currentBox = entity.getBoundingBox(); AABB currentBox = entity.getBoundingBox();
AABB previousBox = currentBox.move(entity.xo - entity.getX(), entity.yo - entity.getY(), entity.zo - entity.getZ()); AABB previousBox = currentBox.move(entity.xo - entity.getX(), entity.yo - entity.getY(), entity.zo - entity.getZ());
PortalCrossing crossing = this.resolveCrossing(entity, previousBox, currentBox); PortalCrossing crossing = this.resolveCrossing(entity, previousBox, currentBox, entity.getDeltaMovement());
if (crossing == null) { if (crossing == null) {
if (entity instanceof Player && Boolean.getBoolean(DEBUG_TRAVERSAL_PROPERTY) && this.ageTicks % 20 == 0) {
Vec3 probe = this.teleportProbePosition(entity, currentBox);
double depth = this.getWorldPortalShape().localCoords(probe).depth();
if (depth < 1.5D) {
Antarchy.LOGGER.info("Portal gun traversal waiting portal={} player={} depth={} position={} linked={}",
this.getUUID(), entity.getUUID(), depth, entity.position(), linked.getUUID());
}
}
continue; continue;
} }
if (entity instanceof Player && Boolean.getBoolean(DEBUG_TRAVERSAL_PROPERTY)) { this.teleportEntity(entity, linked, currentBox, entity.getDeltaMovement(), false);
Antarchy.LOGGER.info("Portal gun traversal accepted portal={} player={} probe={} offset={} linked={}",
this.getUUID(), entity.getUUID(), crossing.probe(), crossing.normalOffset(), linked.getUUID());
}
logCrossingPath(entity, "portal_tick");
Vec3 resolvedMovement = entity.position().subtract(entity.xo, entity.yo, entity.zo);
this.teleportEntity(entity, linked, crossing.probe(), resolvedMovement, crossing.normalOffset());
}
}
private static void logCrossingPath(Entity entity, String path) {
String entityType = BuiltInRegistries.ENTITY_TYPE.getKey(entity.getType()).toString();
if (LOGGED_CROSSING_PATHS.add(entityType + ":" + path)) {
Antarchy.LOGGER.info("Portal gun entity crossing path entityType={} path={}", entityType, path);
} }
} }
@@ -620,56 +663,81 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity {
return this.teleportCooldowns.containsKey(entity.getUUID()) || entity.getPortalCooldown() > 0; return this.teleportCooldowns.containsKey(entity.getUUID()) || entity.getPortalCooldown() > 0;
} }
private void teleportEntity(Entity entity, PortalGunPortalEntity destination, Vec3 currentProbe, Vec3 entryMotion, double normalOffset) { private void teleportEntity(Entity entity, PortalGunPortalEntity destination, AABB crossingBox, Vec3 velocity, boolean clientAuthoritative) {
List<EntityTeleportState> group = new java.util.ArrayList<>(); List<EntityTeleportState> group = new java.util.ArrayList<>();
collectTeleportGroup(entity, null, group); collectTeleportGroup(entity, null, group);
Vec3 rootOriginalPosition = entity.position(); Vec3 rootOriginalPosition = entity.position();
PortalTransitTransform transitTransform = this.previewTransit(entity, destination, new PortalCrossing(currentProbe, normalOffset), entryMotion); PortalTransit transit = this.computeTransit(entity, destination, crossingBox, velocity, entity.getLookAngle());
Vec3 exitNormal = destination.getNormalVec().normalize(); Vec3 exitPos = transit.position();
Vec3 requestedExitPos = transitTransform.requestedPosition();
Vec3 exitPos = transitTransform.exitPosition();
Vec3 transformedMotion = transitTransform.movement();
this.level().playSound(null, this.getX(), this.getY(), this.getZ(), sound("portal_enter"), SoundSource.PLAYERS, 0.7F, 0.96F + this.random.nextFloat() * 0.08F); this.level().playSound(null, this.getX(), this.getY(), this.getZ(), sound("portal_enter"), SoundSource.PLAYERS, 0.7F, 0.96F + this.random.nextFloat() * 0.08F);
for (EntityTeleportState state : group) { for (EntityTeleportState state : group) {
Entity member = state.entity(); state.entity().stopRiding();
member.stopRiding();
} }
for (EntityTeleportState state : group) { for (EntityTeleportState state : group) {
Entity member = state.entity(); Entity member = state.entity();
Vec3 memberOffset = PortalGunTransformUtil.transformVector(this, destination, state.position().subtract(rootOriginalPosition)); boolean root = member == entity;
Vec3 memberExitPos = putEntityWithinExitBounds(member, requestedExitPos.add(memberOffset), legacyExitBounds(destination, member, exitNormal)); Vec3 memberExitPos = root
Vec3 memberLook = PortalGunTransformUtil.transformVector(this, destination, state.look()).normalize(); ? exitPos
Vec3 memberMotion = member == entity : exitPos.add(PortalGunTransformUtil.transformVector(this, destination, state.position().subtract(rootOriginalPosition)));
? transformedMotion Vec3 memberLook;
: PortalGunTransformUtil.transformVector(this, destination, state.motion()); if (root) {
memberLook = transit.look();
} else if (AntarchyGravityApi.isGravityInverted(member)) {
Vec3 worldLook = PortalGunTransitMath.entityVelocityToWorld(member, state.look());
memberLook = PortalGunTransitMath.entityVelocityFromWorld(member,
PortalGunTransformUtil.transformVector(this, destination, worldLook).normalize());
} else {
memberLook = PortalGunTransformUtil.transformVector(this, destination, state.look()).normalize();
}
Vec3 memberMotion;
if (root) {
memberMotion = transit.velocity();
} else if (AntarchyGravityApi.isGravityInverted(member)) {
Vec3 worldMotion = PortalGunTransitMath.entityVelocityToWorld(member, state.motion());
memberMotion = PortalGunTransitMath.entityVelocityFromWorld(member,
PortalGunTransformUtil.transformVector(this, destination, worldMotion));
} else {
memberMotion = PortalGunTransformUtil.transformVector(this, destination, state.motion());
}
float yaw = PortalGunTransformUtil.yawFromLook(memberLook); float yaw = PortalGunTransformUtil.yawFromLook(memberLook);
float pitch = PortalGunTransformUtil.pitchFromLook(memberLook); float pitch = PortalGunTransformUtil.pitchFromLook(memberLook);
if (member instanceof ServerPlayer player && destination.level() instanceof ServerLevel destinationLevel) { float fallDistance = PortalGunTransitMath.exitFallDistance(member.fallDistance,
AntarchyGravityApi.isGravityInverted(member) ? PortalGunTransitMath.entityVelocityToWorld(member, memberMotion) : memberMotion,
member);
if (member instanceof ServerPlayer player && root && clientAuthoritative) {
player.moveTo(memberExitPos.x, memberExitPos.y, memberExitPos.z, yaw, pitch);
player.setYHeadRot(yaw);
player.setDeltaMovement(memberMotion);
player.connection.resetPosition();
player.serverLevel().getChunkSource().move(player);
} else if (member instanceof ServerPlayer player && destination.level() instanceof ServerLevel destinationLevel) {
player.teleportTo(destinationLevel, memberExitPos.x, memberExitPos.y, memberExitPos.z, yaw, pitch); player.teleportTo(destinationLevel, memberExitPos.x, memberExitPos.y, memberExitPos.z, yaw, pitch);
player.setDeltaMovement(memberMotion);
player.hasImpulse = true;
player.connection.send(new ClientboundSetEntityMotionPacket(player));
} else { } else {
member.teleportTo(memberExitPos.x, memberExitPos.y, memberExitPos.z); member.teleportTo(memberExitPos.x, memberExitPos.y, memberExitPos.z);
member.setYRot(yaw); member.setYRot(yaw);
member.setXRot(pitch); member.setXRot(pitch);
member.setYHeadRot(yaw); member.setYHeadRot(yaw);
member.setYBodyRot(yaw); member.setYBodyRot(yaw);
} member.setDeltaMovement(memberMotion);
member.setDeltaMovement(memberMotion); member.hasImpulse = true;
member.hasImpulse = true; member.hurtMarked = true;
member.hurtMarked = true;
if (member instanceof ServerPlayer player) {
player.connection.send(new ClientboundSetEntityMotionPacket(player));
} }
member.xo = member.getX(); member.xo = member.getX();
member.yo = member.getY(); member.yo = member.getY();
member.zo = member.getZ(); member.zo = member.getZ();
member.yRotO = yaw; member.yRotO = yaw;
member.xRotO = pitch; member.xRotO = pitch;
if (member instanceof Projectile) {
orientAlongVelocity(member, memberMotion);
}
member.setOnGround(false); member.setOnGround(false);
member.horizontalCollision = false; member.horizontalCollision = false;
member.verticalCollision = false; member.verticalCollision = false;
member.verticalCollisionBelow = false; member.verticalCollisionBelow = false;
float verticalMotion = (float) memberMotion.y; member.fallDistance = fallDistance;
member.fallDistance = 0.1F * (verticalMotion / -0.1F * verticalMotion / -0.1F);
this.applyTeleportCooldown(member, destination); this.applyTeleportCooldown(member, destination);
this.handleSpecialEntityPostTeleport(member); this.handleSpecialEntityPostTeleport(member);
} }
@@ -684,12 +752,22 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity {
if (!(member instanceof ServerPlayer)) { if (!(member instanceof ServerPlayer)) {
destinationLevel.getChunkSource().broadcastAndSend(member, new ClientboundTeleportEntityPacket(member)); destinationLevel.getChunkSource().broadcastAndSend(member, new ClientboundTeleportEntityPacket(member));
destinationLevel.getChunkSource().broadcastAndSend(member, new ClientboundSetEntityMotionPacket(member)); destinationLevel.getChunkSource().broadcastAndSend(member, new ClientboundSetEntityMotionPacket(member));
syncTrackedPosition(destinationLevel, member);
} }
} }
} }
destination.level().playSound(null, exitPos.x, exitPos.y, exitPos.z, sound("portal_exit"), SoundSource.PLAYERS, 0.7F, 0.96F + this.random.nextFloat() * 0.08F); destination.level().playSound(null, exitPos.x, exitPos.y, exitPos.z, sound("portal_exit"), SoundSource.PLAYERS, 0.7F, 0.96F + this.random.nextFloat() * 0.08F);
} }
private static void syncTrackedPosition(ServerLevel level, Entity entity) {
Object tracked = ((ChunkMapAccessor) level.getChunkSource().chunkMap).antarchy$getEntityMap().get(entity.getId());
if (tracked instanceof TrackedEntityAccessor trackedEntity) {
ServerEntityAccessor serverEntity = (ServerEntityAccessor) trackedEntity.antarchy$getServerEntity();
serverEntity.antarchy$getPositionCodec().setBase(entity.trackingPosition());
serverEntity.antarchy$setLastSentMovement(entity.getDeltaMovement());
}
}
private void collectTeleportGroup(Entity entity, Entity vehicle, List<EntityTeleportState> group) { private void collectTeleportGroup(Entity entity, Entity vehicle, List<EntityTeleportState> group) {
group.add(new EntityTeleportState(entity, vehicle, entity.position(), entity.getLookAngle(), entity.getDeltaMovement())); group.add(new EntityTeleportState(entity, vehicle, entity.position(), entity.getLookAngle(), entity.getDeltaMovement()));
for (Entity passenger : entity.getPassengers()) { for (Entity passenger : entity.getPassengers()) {
@@ -697,37 +775,6 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity {
} }
} }
private static Vec3 putEntityWithinExitBounds(Entity entity, Vec3 position, AABB exitBounds) {
AABB entityBounds = entity.getBoundingBox().move(position.x - entity.getX(), position.y - entity.getY(), position.z - entity.getZ());
double x = position.x;
double y = position.y;
double z = position.z;
if (entityBounds.maxX > exitBounds.maxX) {
x += exitBounds.maxX - entityBounds.maxX;
}
if (entityBounds.minX < exitBounds.minX) {
x += exitBounds.minX - entityBounds.minX;
}
if (y + entity.getEyeHeight() > exitBounds.maxY) {
y += exitBounds.maxY - y - entity.getEyeHeight();
}
if (y < exitBounds.minY) {
y += exitBounds.minY - y + 0.001D;
}
if (entityBounds.maxZ > exitBounds.maxZ) {
z += exitBounds.maxZ - entityBounds.maxZ;
}
if (entityBounds.minZ < exitBounds.minZ) {
z += exitBounds.minZ - entityBounds.minZ;
}
return new Vec3(x, y, z);
}
private static AABB legacyExitBounds(PortalGunPortalEntity destination, Entity entity, Vec3 exitNormal) {
double maxDimension = Math.max(entity.getBbWidth(), entity.getBbHeight());
return destination.getScanRange().expandTowards(exitNormal.scale(-maxDimension));
}
private void tickTeleportCooldowns() { private void tickTeleportCooldowns() {
Iterator<Map.Entry<UUID, Integer>> iterator = this.teleportCooldowns.entrySet().iterator(); Iterator<Map.Entry<UUID, Integer>> iterator = this.teleportCooldowns.entrySet().iterator();
while (iterator.hasNext()) { while (iterator.hasNext()) {
@@ -783,15 +830,9 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity {
if (this.ownerId != null) { if (this.ownerId != null) {
tag.putUUID("OwnerId", this.ownerId); tag.putUUID("OwnerId", this.ownerId);
} }
if (this.ownerIdentity != null) {
tag.putString("OwnerIdentity", this.ownerIdentity);
}
if (this.gunId != null) { if (this.gunId != null) {
tag.putUUID("GunId", this.gunId); tag.putUUID("GunId", this.gunId);
} }
if (this.channelName != null) {
tag.putString("ChannelName", this.channelName);
}
if (this.linkedPortalId != null) { if (this.linkedPortalId != null) {
tag.putUUID("LinkedPortalId", this.linkedPortalId); tag.putUUID("LinkedPortalId", this.linkedPortalId);
} }
@@ -800,9 +841,7 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity {
tag.putInt("Side", this.getPortalSide().ordinal()); tag.putInt("Side", this.getPortalSide().ordinal());
tag.putInt("Facing", this.getFacingDirection().get3DDataValue()); tag.putInt("Facing", this.getFacingDirection().get3DDataValue());
tag.putInt("UpAxis", this.getUpAxis().get3DDataValue()); tag.putInt("UpAxis", this.getUpAxis().get3DDataValue());
tag.putInt("ChannelRed", this.getChannelRed()); tag.putInt("Variant", this.getVariant().id());
tag.putInt("ChannelGreen", this.getChannelGreen());
tag.putInt("ChannelBlue", this.getChannelBlue());
tag.putInt("PortalWidth", this.getPortalWidth()); tag.putInt("PortalWidth", this.getPortalWidth());
tag.putInt("PortalHeight", this.getPortalHeight()); tag.putInt("PortalHeight", this.getPortalHeight());
tag.putInt("SupportX", this.supportOrigin.getX()); tag.putInt("SupportX", this.supportOrigin.getX());
@@ -839,12 +878,9 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity {
if (tag.hasUUID("OwnerId")) { if (tag.hasUUID("OwnerId")) {
this.ownerId = tag.getUUID("OwnerId"); this.ownerId = tag.getUUID("OwnerId");
} }
this.ownerIdentity = tag.contains("OwnerIdentity") ? tag.getString("OwnerIdentity") : String.valueOf(this.ownerId);
this.gunId = tag.hasUUID("GunId") ? tag.getUUID("GunId") : null; this.gunId = tag.hasUUID("GunId") ? tag.getUUID("GunId") : null;
this.channelName = tag.contains("ChannelName") ? tag.getString("ChannelName") : this.gunId == null ? "" : "Random Channel #" + this.gunId.hashCode();
this.entityData.set(OWNER_ID, Optional.ofNullable(this.ownerId)); this.entityData.set(OWNER_ID, Optional.ofNullable(this.ownerId));
this.entityData.set(GUN_ID, Optional.ofNullable(this.gunId)); this.entityData.set(GUN_ID, Optional.ofNullable(this.gunId));
this.entityData.set(CHANNEL_NAME, this.channelName);
if (tag.hasUUID("LinkedPortalId")) { if (tag.hasUUID("LinkedPortalId")) {
this.linkedPortalId = tag.getUUID("LinkedPortalId"); this.linkedPortalId = tag.getUUID("LinkedPortalId");
this.entityData.set(LINKED_PORTAL, Optional.of(this.linkedPortalId)); this.entityData.set(LINKED_PORTAL, Optional.of(this.linkedPortalId));
@@ -854,16 +890,7 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity {
this.entityData.set(SIDE, tag.getInt("Side")); this.entityData.set(SIDE, tag.getInt("Side"));
this.entityData.set(FACING, tag.getInt("Facing")); this.entityData.set(FACING, tag.getInt("Facing"));
this.entityData.set(UP_AXIS, tag.getInt("UpAxis")); this.entityData.set(UP_AXIS, tag.getInt("UpAxis"));
if (this.ownerId != null && ("Global".equals(this.ownerIdentity) || !tag.contains("ChannelRed"))) { this.entityData.set(VARIANT, PortalGunVariant.byId(tag.getInt("Variant")).id());
int[] channelColor = generateChannelColor(this.ownerIdentity, this.channelName, this.getPortalSide());
this.entityData.set(CHANNEL_RED, channelColor[0]);
this.entityData.set(CHANNEL_GREEN, channelColor[1]);
this.entityData.set(CHANNEL_BLUE, channelColor[2]);
} else if (tag.contains("ChannelRed")) {
this.entityData.set(CHANNEL_RED, tag.getInt("ChannelRed"));
this.entityData.set(CHANNEL_GREEN, tag.getInt("ChannelGreen"));
this.entityData.set(CHANNEL_BLUE, tag.getInt("ChannelBlue"));
}
this.supportOrigin = new BlockPos(tag.getInt("SupportX"), tag.getInt("SupportY"), tag.getInt("SupportZ")); this.supportOrigin = new BlockPos(tag.getInt("SupportX"), tag.getInt("SupportY"), tag.getInt("SupportZ"));
this.masterPos = new BlockPos(tag.getInt("MasterX"), tag.getInt("MasterY"), tag.getInt("MasterZ")); this.masterPos = new BlockPos(tag.getInt("MasterX"), tag.getInt("MasterY"), tag.getInt("MasterZ"));
this.basePos = new BlockPos(tag.getInt("BaseX"), tag.getInt("BaseY"), tag.getInt("BaseZ")); this.basePos = new BlockPos(tag.getInt("BaseX"), tag.getInt("BaseY"), tag.getInt("BaseZ"));
@@ -892,7 +919,7 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity {
UUID ownerId = this.ownerId; UUID ownerId = this.ownerId;
boolean destroyPortal = reason.shouldDestroy(); boolean destroyPortal = reason.shouldDestroy();
if (destroyPortal && !level.isClientSide && ownerId != null && level instanceof ServerLevel serverLevel) { if (destroyPortal && !level.isClientSide && ownerId != null && level instanceof ServerLevel serverLevel) {
PortalGunSavedData.clearPortal(serverLevel.getServer(), ownerId, this.gunId, this.channelName, this.getPortalSide(), this.getUUID(), serverLevel.dimension().location()); PortalGunSavedData.clearPortal(serverLevel.getServer(), this.gunId, this.getPortalSide(), this.getUUID(), serverLevel.dimension().location());
} }
super.remove(reason); super.remove(reason);
if (destroyPortal && !level.isClientSide && ownerId != null && level instanceof ServerLevel serverLevel) { if (destroyPortal && !level.isClientSide && ownerId != null && level instanceof ServerLevel serverLevel) {
@@ -971,40 +998,10 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity {
.orElseThrow(() -> new IllegalStateException("Missing portal gun sound event: " + soundId)); .orElseThrow(() -> new IllegalStateException("Missing portal gun sound event: " + soundId));
} }
private static int[] generateChannelColor(String ownerIdentity, String channelName, PortalSide side) { public record PortalCrossing(Vec3 probe) {
if ("Global".equals(ownerIdentity)) {
int[] globalColors = switch (channelName) {
case "Chell" -> new int[] {361215, 16756742};
case "Atlas" -> new int[] {5482192, 4064209};
case "P-body" -> new int[] {16373344, 8394260};
default -> null;
};
if (globalColors != null) {
int color = globalColors[side == PortalSide.BLUE ? 0 : 1];
return new int[] {color >> 16 & 0xFF, color >> 8 & 0xFF, color & 0xFF};
}
}
String owner = String.valueOf(ownerIdentity);
String identity = owner + "_" + String.valueOf(channelName);
Random random = new Random();
random.setSeed((long) Math.abs(identity.hashCode() * owner.hashCode()));
int firstColor = Math.round(1.6777215E7F * random.nextFloat());
float[] hsb = Color.RGBtoHSB(firstColor >> 16 & 0xFF, firstColor >> 8 & 0xFF, firstColor & 0xFF, null);
hsb[2] = 0.65F + 0.25F * hsb[2];
int portalAColor = Color.HSBtoRGB(hsb[0], hsb[1], hsb[2]);
float oppositeHue = hsb[0] + 0.5F;
if (oppositeHue > 1.0F) {
oppositeHue -= 1.0F;
}
int portalBColor = Color.HSBtoRGB(oppositeHue, hsb[1], hsb[2]);
int color = side == PortalSide.BLUE ? portalAColor : portalBColor;
return new int[] {color >> 16 & 0xFF, color >> 8 & 0xFF, color & 0xFF};
} }
public record PortalCrossing(Vec3 probe, double normalOffset) { public record PortalTransit(AABB exitBox, Vec3 position, Vec3 velocity, Vec3 look) {
}
public record PortalTransitTransform(Vec3 requestedPosition, Vec3 exitPosition, Vec3 movement) {
} }
private record EntityTeleportState(Entity entity, Entity vehicle, Vec3 position, Vec3 look, Vec3 motion) { private record EntityTeleportState(Entity entity, Entity vehicle, Vec3 position, Vec3 look, Vec3 motion) {
@@ -0,0 +1,81 @@
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 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 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<>());
}
portals.put(portal.getUUID(), portal);
}
static void untrack(PortalGunPortalEntity portal) {
Map<UUID, PortalGunPortalEntity> portals = portalsIn(portal.level());
if (portals != null) {
portals.remove(portal.getUUID(), portal);
}
}
public static Collection<PortalGunPortalEntity> all(Level level) {
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) {
Map<UUID, PortalGunPortalEntity> portals = portalsIn(level);
if (portals == null || portals.isEmpty()) {
return List.of();
}
List<PortalGunPortalEntity> nearby = null;
for (PortalGunPortalEntity portal : portals.values()) {
if (portal.isAlive() && portal.getPortalInsides().intersects(bounds)) {
if (nearby == null) {
nearby = new ArrayList<>(2);
}
nearby.add(portal);
}
}
return nearby == null ? List.of() : nearby;
}
private static Map<UUID, PortalGunPortalEntity> portalsIn(Level level) {
if (level == null) {
return null;
}
synchronized (LEVELS) {
return LEVELS.get(level);
}
}
}
@@ -1,6 +1,9 @@
package com.craisinlord.antarchy.content.portalgun; package com.craisinlord.antarchy.content.portalgun;
import com.craisinlord.antarchy.content.item.PortalGunItem; import com.craisinlord.antarchy.content.item.PortalGunItem;
import com.craisinlord.antarchy.content.gravity.AntarchyGravityApi;
import com.craisinlord.antarchy.content.gravity.AntarchyGravityDirection;
import com.craisinlord.antarchy.content.gravity.AntarchyGravityTransition;
import com.craisinlord.antarchy.config.AntarchySettings; import com.craisinlord.antarchy.config.AntarchySettings;
import java.util.Comparator; import java.util.Comparator;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
@@ -61,6 +64,10 @@ public class PortalGunProjectileEntity extends ThrowableItemProjectile {
private static final EntityDataAccessor<Float> PROJECTILE_SPEED = SynchedEntityData.defineId(PortalGunProjectileEntity.class, EntityDataSerializers.FLOAT); private static final EntityDataAccessor<Float> PROJECTILE_SPEED = SynchedEntityData.defineId(PortalGunProjectileEntity.class, EntityDataSerializers.FLOAT);
private static final EntityDataAccessor<Boolean> PASS_THROUGH_GLASS = SynchedEntityData.defineId(PortalGunProjectileEntity.class, EntityDataSerializers.BOOLEAN); private static final EntityDataAccessor<Boolean> PASS_THROUGH_GLASS = SynchedEntityData.defineId(PortalGunProjectileEntity.class, EntityDataSerializers.BOOLEAN);
private static final EntityDataAccessor<Boolean> PASS_THROUGH_LIQUID = SynchedEntityData.defineId(PortalGunProjectileEntity.class, EntityDataSerializers.BOOLEAN); private static final EntityDataAccessor<Boolean> PASS_THROUGH_LIQUID = SynchedEntityData.defineId(PortalGunProjectileEntity.class, EntityDataSerializers.BOOLEAN);
private static final int TRAIL_PARTICLES_PER_TICK = 4;
private static final float TRAIL_DUST_SCALE = 0.45F;
private static final float IMPACT_DUST_SCALE = 0.8F;
private static final int IMPACT_PARTICLES = 14;
public static final double MIN_LAUNCH_SPEED = 4.98D; public static final double MIN_LAUNCH_SPEED = 4.98D;
public static final double LAUNCH_SPEED_VARIANCE = 0.02D; public static final double LAUNCH_SPEED_VARIANCE = 0.02D;
public static final double MAX_TRAVEL_DISTANCE = 10000.0D; public static final double MAX_TRAVEL_DISTANCE = 10000.0D;
@@ -109,6 +116,7 @@ public class PortalGunProjectileEntity extends ThrowableItemProjectile {
} }
public void configure(PortalGunPortalEntity.PortalSide side, UUID gunId, ItemStack gunStack) { public void configure(PortalGunPortalEntity.PortalSide side, UUID gunId, ItemStack gunStack) {
AntarchyGravityApi.setAirborneGravityDirection(this, AntarchyGravityDirection.DOWN, false, AntarchyGravityTransition.INSTANT);
this.entityData.set(SIDE, side.ordinal()); this.entityData.set(SIDE, side.ordinal());
this.gunId = gunId; this.gunId = gunId;
this.setItem(gunStack.copyWithCount(1)); this.setItem(gunStack.copyWithCount(1));
@@ -134,6 +142,10 @@ public class PortalGunProjectileEntity extends ThrowableItemProjectile {
return this.entityData.get(PROJECTILE_SPEED); return this.entityData.get(PROJECTILE_SPEED);
} }
public PortalGunVariant getVariant() {
return this.getItem().getItem() instanceof PortalGunItem portalGun ? portalGun.getVariant() : PortalGunVariant.DEFAULT;
}
public PortalGunPortalEntity.PortalSide getPortalSide() { public PortalGunPortalEntity.PortalSide getPortalSide() {
return this.entityData.get(SIDE) == PortalGunPortalEntity.PortalSide.ORANGE.ordinal() ? PortalGunPortalEntity.PortalSide.ORANGE : PortalGunPortalEntity.PortalSide.BLUE; return this.entityData.get(SIDE) == PortalGunPortalEntity.PortalSide.ORANGE.ordinal() ? PortalGunPortalEntity.PortalSide.ORANGE : PortalGunPortalEntity.PortalSide.BLUE;
} }
@@ -265,14 +277,12 @@ public class PortalGunProjectileEntity extends ThrowableItemProjectile {
); );
} }
} }
if (this.level().isClientSide) { if (this.level().isClientSide && this.tickCount > 1) {
net.minecraft.core.particles.DustParticleOptions dust = this.trailDust(TRAIL_DUST_SCALE);
Vec3 point = this.position(); Vec3 point = this.position();
if (this.getPortalSide() == PortalGunPortalEntity.PortalSide.BLUE) { for (int i = 0; i < TRAIL_PARTICLES_PER_TICK; i++) {
this.level().addParticle(net.minecraft.core.particles.ParticleTypes.SOUL_FIRE_FLAME, point.x, point.y, point.z, 0.0D, 0.0D, 0.0D); Vec3 trailPoint = point.subtract(motion.scale((double) i / TRAIL_PARTICLES_PER_TICK));
this.level().addParticle(net.minecraft.core.particles.ParticleTypes.END_ROD, point.x, point.y, point.z, 0.0D, 0.0D, 0.0D); this.level().addParticle(dust, trailPoint.x, trailPoint.y, trailPoint.z, 0.0D, 0.0D, 0.0D);
} else {
this.level().addParticle(net.minecraft.core.particles.ParticleTypes.FLAME, point.x, point.y, point.z, 0.0D, 0.0D, 0.0D);
this.level().addParticle(net.minecraft.core.particles.ParticleTypes.CRIT, point.x, point.y, point.z, 0.0D, 0.0D, 0.0D);
} }
} }
} }
@@ -368,13 +378,13 @@ public class PortalGunProjectileEntity extends ThrowableItemProjectile {
} }
private void spawnImpactParticles(ServerLevel level, Vec3 impactPos) { private void spawnImpactParticles(ServerLevel level, Vec3 impactPos) {
if (this.getPortalSide() == PortalGunPortalEntity.PortalSide.BLUE) { level.sendParticles(this.trailDust(IMPACT_DUST_SCALE), impactPos.x, impactPos.y, impactPos.z, IMPACT_PARTICLES, 0.12D, 0.12D, 0.12D, 0.02D);
level.sendParticles(net.minecraft.core.particles.ParticleTypes.SOUL_FIRE_FLAME, impactPos.x, impactPos.y, impactPos.z, 10, 0.08D, 0.08D, 0.08D, 0.01D); }
level.sendParticles(net.minecraft.core.particles.ParticleTypes.END_ROD, impactPos.x, impactPos.y, impactPos.z, 6, 0.04D, 0.04D, 0.04D, 0.01D);
return; private net.minecraft.core.particles.DustParticleOptions trailDust(float scale) {
} int color = this.getVariant().color(this.getPortalSide());
level.sendParticles(net.minecraft.core.particles.ParticleTypes.FLAME, impactPos.x, impactPos.y, impactPos.z, 10, 0.08D, 0.08D, 0.08D, 0.01D); return new net.minecraft.core.particles.DustParticleOptions(
level.sendParticles(net.minecraft.core.particles.ParticleTypes.CRIT, impactPos.x, impactPos.y, impactPos.z, 6, 0.04D, 0.04D, 0.04D, 0.01D); new org.joml.Vector3f((color >> 16 & 0xFF) / 255.0F, (color >> 8 & 0xFF) / 255.0F, (color & 0xFF) / 255.0F), scale);
} }
private void syncSpawnPosition(Vec3 pos) { private void syncSpawnPosition(Vec3 pos) {
@@ -2,9 +2,7 @@ package com.craisinlord.antarchy.content.portalgun;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.nio.charset.StandardCharsets;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.UUID; import java.util.UUID;
@@ -18,17 +16,13 @@ import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.saveddata.SavedData; import net.minecraft.world.level.saveddata.SavedData;
public final class PortalGunSavedData extends SavedData { public final class PortalGunSavedData extends SavedData {
private static final String ID = "antarchy_portal_gun_pairs"; private static final String ID = "antarchy_portal_gun_pairs_v2";
private static final String ENTRIES_KEY = "Entries"; private static final String ENTRIES_KEY = "Entries";
private static final String OWNER_KEY = "Owner";
private static final String DIMENSION_KEY = "Dimension"; private static final String DIMENSION_KEY = "Dimension";
private static final String GUN_KEY = "Gun"; private static final String GUN_KEY = "Gun";
private static final String BLUE_KEY = "Blue"; private static final String BLUE_KEY = "Blue";
private static final String ORANGE_KEY = "Orange"; private static final String ORANGE_KEY = "Orange";
private static final String CHANNELS_KEY = "Channels";
private final Map<PairKey, PairRecord> pairs = new HashMap<>(); private final Map<PairKey, PairRecord> pairs = new HashMap<>();
private final Map<UUID, PairRecord> legacyPairs = new HashMap<>();
private final Map<OwnerChannelKey, UUID> channelGunIds = new HashMap<>();
public static PortalGunSavedData create() { public static PortalGunSavedData create() {
return new PortalGunSavedData(); return new PortalGunSavedData();
@@ -38,23 +32,13 @@ public final class PortalGunSavedData extends SavedData {
PortalGunSavedData data = new PortalGunSavedData(); PortalGunSavedData data = new PortalGunSavedData();
for (net.minecraft.nbt.Tag rawEntry : tag.getList(ENTRIES_KEY, net.minecraft.nbt.Tag.TAG_COMPOUND)) { for (net.minecraft.nbt.Tag rawEntry : tag.getList(ENTRIES_KEY, net.minecraft.nbt.Tag.TAG_COMPOUND)) {
CompoundTag entryTag = (CompoundTag) rawEntry; CompoundTag entryTag = (CompoundTag) rawEntry;
UUID owner = entryTag.getUUID(OWNER_KEY); ResourceLocation dimension = ResourceLocation.tryParse(entryTag.getString(DIMENSION_KEY));
if (dimension == null || !entryTag.hasUUID(GUN_KEY)) {
continue;
}
UUID blue = entryTag.hasUUID(BLUE_KEY) ? entryTag.getUUID(BLUE_KEY) : null; UUID blue = entryTag.hasUUID(BLUE_KEY) ? entryTag.getUUID(BLUE_KEY) : null;
UUID orange = entryTag.hasUUID(ORANGE_KEY) ? entryTag.getUUID(ORANGE_KEY) : null; UUID orange = entryTag.hasUUID(ORANGE_KEY) ? entryTag.getUUID(ORANGE_KEY) : null;
PairRecord record = new PairRecord(blue, orange); data.pairs.put(new PairKey(dimension, entryTag.getUUID(GUN_KEY)), new PairRecord(blue, orange));
ResourceLocation dimension = entryTag.contains(DIMENSION_KEY) ? ResourceLocation.tryParse(entryTag.getString(DIMENSION_KEY)) : null;
if (dimension == null) {
data.legacyPairs.put(owner, record);
} else {
UUID gunId = entryTag.hasUUID(GUN_KEY) ? entryTag.getUUID(GUN_KEY) : null;
data.pairs.put(new PairKey(owner, dimension, gunId), record);
}
}
for (net.minecraft.nbt.Tag rawChannel : tag.getList(CHANNELS_KEY, net.minecraft.nbt.Tag.TAG_COMPOUND)) {
CompoundTag channelTag = (CompoundTag) rawChannel;
if (channelTag.hasUUID(OWNER_KEY) && channelTag.hasUUID(GUN_KEY) && channelTag.contains("Channel")) {
data.channelGunIds.put(new OwnerChannelKey(channelTag.getUUID(OWNER_KEY), channelTag.getString("Channel")), channelTag.getUUID(GUN_KEY));
}
} }
return data; return data;
} }
@@ -64,42 +48,20 @@ public final class PortalGunSavedData extends SavedData {
ListTag entries = new ListTag(); ListTag entries = new ListTag();
for (Map.Entry<PairKey, PairRecord> entry : this.pairs.entrySet()) { for (Map.Entry<PairKey, PairRecord> entry : this.pairs.entrySet()) {
CompoundTag entryTag = new CompoundTag(); CompoundTag entryTag = new CompoundTag();
entryTag.putUUID(OWNER_KEY, entry.getKey().owner());
entryTag.putString(DIMENSION_KEY, entry.getKey().dimension().toString()); entryTag.putString(DIMENSION_KEY, entry.getKey().dimension().toString());
if (entry.getKey().gunId() != null) { entryTag.putUUID(GUN_KEY, entry.getKey().gunId());
entryTag.putUUID(GUN_KEY, entry.getKey().gunId()); if (entry.getValue().bluePortalId() != null) {
entryTag.putUUID(BLUE_KEY, entry.getValue().bluePortalId());
}
if (entry.getValue().orangePortalId() != null) {
entryTag.putUUID(ORANGE_KEY, entry.getValue().orangePortalId());
} }
writePair(entryTag, entry.getValue());
entries.add(entryTag); entries.add(entryTag);
} }
for (Map.Entry<UUID, PairRecord> entry : this.legacyPairs.entrySet()) {
CompoundTag entryTag = new CompoundTag();
entryTag.putUUID(OWNER_KEY, entry.getKey());
writePair(entryTag, entry.getValue());
entries.add(entryTag);
}
ListTag channels = new ListTag();
for (Map.Entry<OwnerChannelKey, UUID> entry : this.channelGunIds.entrySet()) {
CompoundTag channelTag = new CompoundTag();
channelTag.putUUID(OWNER_KEY, entry.getKey().owner());
channelTag.putString("Channel", entry.getKey().channelName());
channelTag.putUUID(GUN_KEY, entry.getValue());
channels.add(channelTag);
}
tag.put(ENTRIES_KEY, entries); tag.put(ENTRIES_KEY, entries);
tag.put(CHANNELS_KEY, channels);
return tag; return tag;
} }
private static void writePair(CompoundTag tag, PairRecord record) {
if (record.bluePortalId() != null) {
tag.putUUID(BLUE_KEY, record.bluePortalId());
}
if (record.orangePortalId() != null) {
tag.putUUID(ORANGE_KEY, record.orangePortalId());
}
}
private static PortalGunSavedData get(MinecraftServer server) { private static PortalGunSavedData get(MinecraftServer server) {
return server.overworld().getDataStorage().computeIfAbsent( return server.overworld().getDataStorage().computeIfAbsent(
new SavedData.Factory<>(PortalGunSavedData::create, PortalGunSavedData::load, null), new SavedData.Factory<>(PortalGunSavedData::create, PortalGunSavedData::load, null),
@@ -107,132 +69,57 @@ public final class PortalGunSavedData extends SavedData {
); );
} }
public static Optional<UUID> getPortalId(MinecraftServer server, UUID owner, UUID gunId, String channelName, PortalGunPortalEntity.PortalSide side, ResourceLocation dimension) { public static Optional<UUID> getPortalId(MinecraftServer server, UUID gunId, PortalGunPortalEntity.PortalSide side, ResourceLocation dimension) {
PortalGunSavedData data = get(server); if (gunId == null) {
UUID resolvedGunId = data.resolveChannelGunId(owner, channelName, gunId); return Optional.empty();
data.migrateLegacyPair(server, owner); }
data.adoptLegacyPairs(server, owner, resolvedGunId, channelName); PairRecord record = get(server).pairs.get(new PairKey(dimension, gunId));
PairRecord record = data.pairs.get(new PairKey(owner, dimension, resolvedGunId));
if (record == null) { if (record == null) {
return Optional.empty(); return Optional.empty();
} }
data.synchronizeLoadedPortal(server, owner, dimension, record.bluePortalId(), resolvedGunId, channelName);
data.synchronizeLoadedPortal(server, owner, dimension, record.orangePortalId(), resolvedGunId, channelName);
return Optional.ofNullable(side == PortalGunPortalEntity.PortalSide.BLUE ? record.bluePortalId() : record.orangePortalId()); return Optional.ofNullable(side == PortalGunPortalEntity.PortalSide.BLUE ? record.bluePortalId() : record.orangePortalId());
} }
public static Optional<UUID> getPortalId(MinecraftServer server, UUID owner, UUID gunId, PortalGunPortalEntity.PortalSide side, ResourceLocation dimension) { public static boolean isRegistered(ServerLevel level, UUID gunId, PortalGunPortalEntity.PortalSide side, UUID portalId) {
return getPortalId(server, owner, gunId, String.valueOf(gunId), side, dimension); return portalId != null && getPortalId(level.getServer(), gunId, side, level.dimension().location()).map(portalId::equals).orElse(false);
} }
public static Optional<UUID> getGunIdForPortal(MinecraftServer server, UUID owner, ResourceLocation dimension, UUID portalId) { public static void setPortal(ServerLevel level, UUID gunId, PortalGunPortalEntity.PortalSide side, UUID portalId) {
PortalGunSavedData data = get(server); if (gunId == null) {
data.migrateLegacyPair(server, owner); return;
for (Map.Entry<PairKey, PairRecord> entry : data.pairs.entrySet()) {
PairKey key = entry.getKey();
if (key.owner().equals(owner) && key.dimension().equals(dimension) && key.gunId() != null
&& (portalId.equals(entry.getValue().bluePortalId()) || portalId.equals(entry.getValue().orangePortalId()))) {
return Optional.of(key.gunId());
}
} }
return Optional.empty();
}
public static Optional<String> getChannelNameForPortal(MinecraftServer server, UUID owner, ResourceLocation dimension, UUID portalId) {
PortalGunSavedData data = get(server);
for (Map.Entry<PairKey, PairRecord> entry : data.pairs.entrySet()) {
PairKey key = entry.getKey();
if (!key.owner().equals(owner) || !key.dimension().equals(dimension)
|| (!portalId.equals(entry.getValue().bluePortalId()) && !portalId.equals(entry.getValue().orangePortalId()))) {
continue;
}
return data.channelGunIds.entrySet().stream()
.filter(channel -> channel.getKey().owner().equals(owner) && channel.getValue().equals(key.gunId()))
.map(channel -> channel.getKey().channelName())
.findFirst();
}
return Optional.empty();
}
public static boolean isRegistered(ServerLevel level, UUID owner, UUID gunId, PortalGunPortalEntity.PortalSide side, UUID portalId) {
return isRegistered(level, owner, gunId, String.valueOf(gunId), side, portalId);
}
public static boolean isRegistered(ServerLevel level, UUID owner, UUID gunId, String channelName, PortalGunPortalEntity.PortalSide side, UUID portalId) {
return getPortalId(level.getServer(), owner, gunId, channelName, side, level.dimension().location()).map(portalId::equals).orElse(false);
}
public static void setPortal(ServerLevel level, UUID owner, UUID gunId, PortalGunPortalEntity.PortalSide side, UUID portalId) {
setPortal(level, owner, gunId, String.valueOf(gunId), side, portalId);
}
public static void setPortal(ServerLevel level, UUID owner, UUID gunId, String channelName, PortalGunPortalEntity.PortalSide side, UUID portalId) {
PortalGunSavedData data = get(level.getServer()); PortalGunSavedData data = get(level.getServer());
ResourceLocation dimension = level.dimension().location(); PairKey key = new PairKey(level.dimension().location(), gunId);
UUID resolvedGunId = data.resolveChannelGunId(owner, channelName, gunId);
data.migrateLegacyPair(level.getServer(), owner);
data.adoptLegacyPairs(level.getServer(), owner, resolvedGunId, channelName);
PairKey key = new PairKey(owner, dimension, resolvedGunId);
PairRecord current = data.pairs.getOrDefault(key, new PairRecord(null, null)); PairRecord current = data.pairs.getOrDefault(key, new PairRecord(null, null));
PairRecord updated = side == PortalGunPortalEntity.PortalSide.BLUE data.put(key, side == PortalGunPortalEntity.PortalSide.BLUE
? new PairRecord(portalId, current.orangePortalId()) ? new PairRecord(portalId, current.orangePortalId())
: new PairRecord(current.bluePortalId(), portalId); : new PairRecord(current.bluePortalId(), portalId));
if (updated.bluePortalId() == null && updated.orangePortalId() == null) { }
data.pairs.remove(key);
} else { public static void clearPortal(MinecraftServer server, UUID gunId, PortalGunPortalEntity.PortalSide side, UUID portalId, ResourceLocation dimension) {
data.pairs.put(key, updated); if (gunId == null) {
return;
} }
data.setDirty();
}
public static void clearPortal(MinecraftServer server, UUID owner, UUID gunId, PortalGunPortalEntity.PortalSide side, UUID portalId, ResourceLocation dimension) {
clearPortal(server, owner, gunId, String.valueOf(gunId), side, portalId, dimension);
}
public static void clearPortal(MinecraftServer server, UUID owner, UUID gunId, String channelName, PortalGunPortalEntity.PortalSide side, UUID portalId, ResourceLocation dimension) {
PortalGunSavedData data = get(server); PortalGunSavedData data = get(server);
UUID resolvedGunId = data.resolveChannelGunId(owner, channelName, gunId); PairKey key = new PairKey(dimension, gunId);
data.migrateLegacyPair(server, owner);
data.adoptLegacyPairs(server, owner, resolvedGunId, channelName);
PairKey key = new PairKey(owner, dimension, resolvedGunId);
PairRecord current = data.pairs.get(key); PairRecord current = data.pairs.get(key);
if (current == null) { if (current == null) {
return; return;
} }
UUID blue = current.bluePortalId(); UUID blue = side == PortalGunPortalEntity.PortalSide.BLUE && portalId.equals(current.bluePortalId()) ? null : current.bluePortalId();
UUID orange = current.orangePortalId(); UUID orange = side == PortalGunPortalEntity.PortalSide.ORANGE && portalId.equals(current.orangePortalId()) ? null : current.orangePortalId();
if (side == PortalGunPortalEntity.PortalSide.BLUE && portalId.equals(blue)) { data.put(key, new PairRecord(blue, orange));
blue = null;
}
if (side == PortalGunPortalEntity.PortalSide.ORANGE && portalId.equals(orange)) {
orange = null;
}
if (blue == null && orange == null) {
data.pairs.remove(key);
} else {
data.pairs.put(key, new PairRecord(blue, orange));
}
data.setDirty();
} }
public static void clearAllPortals(ServerLevel level, UUID owner, UUID gunId) { public static void clearAllPortals(ServerLevel level, UUID gunId) {
clearAllPortals(level, owner, gunId, String.valueOf(gunId)); if (gunId == null) {
} return;
}
public static void clearAllPortals(ServerLevel level, UUID owner, UUID gunId, String channelName) {
MinecraftServer server = level.getServer(); MinecraftServer server = level.getServer();
PortalGunSavedData data = get(server); PortalGunSavedData data = get(server);
UUID resolvedGunId = data.resolveChannelGunId(owner, channelName, gunId);
data.migrateLegacyPair(server, owner);
data.adoptLegacyPairs(server, owner, resolvedGunId, channelName);
Set<UUID> portalIds = new HashSet<>(); Set<UUID> portalIds = new HashSet<>();
PairRecord legacy = gunId == null ? data.legacyPairs.remove(owner) : null;
if (legacy != null) {
addPortalIds(portalIds, legacy);
}
data.pairs.entrySet().removeIf(entry -> { data.pairs.entrySet().removeIf(entry -> {
if (!entry.getKey().owner().equals(owner) || !entry.getKey().dimension().equals(level.dimension().location()) if (!entry.getKey().gunId().equals(gunId)) {
|| !Objects.equals(entry.getKey().gunId(), resolvedGunId)) {
return false; return false;
} }
addPortalIds(portalIds, entry.getValue()); addPortalIds(portalIds, entry.getValue());
@@ -244,20 +131,15 @@ public final class PortalGunSavedData extends SavedData {
} }
} }
public static void clearPortalSide(ServerLevel level, UUID owner, UUID gunId, PortalGunPortalEntity.PortalSide side) { public static void clearPortalSide(ServerLevel level, UUID gunId, PortalGunPortalEntity.PortalSide side) {
clearPortalSide(level, owner, gunId, String.valueOf(gunId), side); if (gunId == null) {
} return;
}
public static void clearPortalSide(ServerLevel level, UUID owner, UUID gunId, String channelName, PortalGunPortalEntity.PortalSide side) {
MinecraftServer server = level.getServer(); MinecraftServer server = level.getServer();
PortalGunSavedData data = get(server); PortalGunSavedData data = get(server);
UUID resolvedGunId = data.resolveChannelGunId(owner, channelName, gunId);
data.migrateLegacyPair(server, owner);
data.adoptLegacyPairs(server, owner, resolvedGunId, channelName);
Set<UUID> portalIds = new HashSet<>(); Set<UUID> portalIds = new HashSet<>();
for (Map.Entry<PairKey, PairRecord> entry : new HashMap<>(data.pairs).entrySet()) { for (Map.Entry<PairKey, PairRecord> entry : new HashMap<>(data.pairs).entrySet()) {
PairKey key = entry.getKey(); if (!entry.getKey().gunId().equals(gunId)) {
if (!key.owner().equals(owner) || !key.dimension().equals(level.dimension().location()) || !Objects.equals(key.gunId(), resolvedGunId)) {
continue; continue;
} }
PairRecord pair = entry.getValue(); PairRecord pair = entry.getValue();
@@ -266,23 +148,17 @@ public final class PortalGunSavedData extends SavedData {
continue; continue;
} }
portalIds.add(removed); portalIds.add(removed);
PairRecord remaining = side == PortalGunPortalEntity.PortalSide.BLUE data.put(entry.getKey(), side == PortalGunPortalEntity.PortalSide.BLUE
? new PairRecord(null, pair.orangePortalId()) ? new PairRecord(null, pair.orangePortalId())
: new PairRecord(pair.bluePortalId(), null); : new PairRecord(pair.bluePortalId(), null));
if (remaining.bluePortalId() == null && remaining.orangePortalId() == null) {
data.pairs.remove(key);
} else {
data.pairs.put(key, remaining);
}
} }
data.setDirty();
for (UUID portalId : portalIds) { for (UUID portalId : portalIds) {
discardIfLoaded(server, portalId); discardIfLoaded(server, portalId);
} }
} }
public static PortalGunPortalEntity findLoadedPortal(ServerLevel level, UUID owner, UUID gunId, PortalGunPortalEntity.PortalSide side) { public static PortalGunPortalEntity findLoadedPortal(ServerLevel level, UUID gunId, PortalGunPortalEntity.PortalSide side) {
Optional<UUID> portalId = getPortalId(level.getServer(), owner, gunId, side, level.dimension().location()); Optional<UUID> portalId = getPortalId(level.getServer(), gunId, side, level.dimension().location());
if (portalId.isEmpty()) { if (portalId.isEmpty()) {
return null; return null;
} }
@@ -290,143 +166,13 @@ public final class PortalGunSavedData extends SavedData {
return entity instanceof PortalGunPortalEntity portal && !portal.isRemoved() ? portal : null; return entity instanceof PortalGunPortalEntity portal && !portal.isRemoved() ? portal : null;
} }
public static PortalGunPortalEntity findLoadedPortal(ServerLevel level, UUID owner, UUID gunId, String channelName, PortalGunPortalEntity.PortalSide side) { private void put(PairKey key, PairRecord record) {
Optional<UUID> portalId = getPortalId(level.getServer(), owner, gunId, channelName, side, level.dimension().location()); if (record.bluePortalId() == null && record.orangePortalId() == null) {
if (portalId.isEmpty()) { this.pairs.remove(key);
return null; } else {
} this.pairs.put(key, record);
Entity entity = level.getEntity(portalId.get());
return entity instanceof PortalGunPortalEntity portal && !portal.isRemoved() ? portal : null;
}
private UUID resolveChannelGunId(UUID owner, String channelName, UUID gunId) {
if (channelName == null || channelName.isEmpty()) {
return gunId;
}
OwnerChannelKey key = new OwnerChannelKey(owner, channelName);
UUID existing = this.channelGunIds.get(key);
if (existing != null) {
return existing;
}
UUID channelGunId = UUID.nameUUIDFromBytes((owner + "\u0000" + channelName).getBytes(StandardCharsets.UTF_8));
this.channelGunIds.put(key, channelGunId);
if (gunId != null && !gunId.equals(channelGunId)) {
this.migrateGunPairs(owner, gunId, channelGunId);
} }
this.setDirty(); this.setDirty();
return channelGunId;
}
private void migrateGunPairs(UUID owner, UUID previousGunId, UUID channelGunId) {
for (Map.Entry<PairKey, PairRecord> entry : new HashMap<>(this.pairs).entrySet()) {
PairKey previousKey = entry.getKey();
if (!previousKey.owner().equals(owner) || !previousGunId.equals(previousKey.gunId())) {
continue;
}
PairKey channelKey = new PairKey(owner, previousKey.dimension(), channelGunId);
PairRecord existing = this.pairs.get(channelKey);
PairRecord migrated = mergePairs(existing, entry.getValue());
this.pairs.remove(previousKey);
this.pairs.put(channelKey, migrated);
}
}
private static PairRecord mergePairs(PairRecord preferred, PairRecord fallback) {
if (preferred == null) {
return fallback;
}
return new PairRecord(
preferred.bluePortalId() == null ? fallback.bluePortalId() : preferred.bluePortalId(),
preferred.orangePortalId() == null ? fallback.orangePortalId() : preferred.orangePortalId()
);
}
private void migrateLegacyPair(MinecraftServer server, UUID owner) {
PairRecord legacy = this.legacyPairs.get(owner);
if (legacy == null) {
return;
}
ServerLevel blueLevel = findPortalLevel(server, legacy.bluePortalId());
ServerLevel orangeLevel = findPortalLevel(server, legacy.orangePortalId());
UUID legacyBlue = legacy.bluePortalId();
UUID legacyOrange = legacy.orangePortalId();
if (blueLevel != null) {
Entity blueEntity = blueLevel.getEntity(legacyBlue);
UUID blueGunId = blueEntity instanceof PortalGunPortalEntity portal ? portal.getGunId() : null;
PairKey key = new PairKey(owner, blueLevel.dimension().location(), blueGunId);
this.pairs.put(key, withPortal(this.pairs.get(key), PortalGunPortalEntity.PortalSide.BLUE, legacyBlue));
legacyBlue = null;
}
if (orangeLevel != null) {
Entity orangeEntity = orangeLevel.getEntity(legacyOrange);
UUID orangeGunId = orangeEntity instanceof PortalGunPortalEntity portal ? portal.getGunId() : null;
PairKey key = new PairKey(owner, orangeLevel.dimension().location(), orangeGunId);
this.pairs.put(key, withPortal(this.pairs.get(key), PortalGunPortalEntity.PortalSide.ORANGE, legacyOrange));
legacyOrange = null;
}
if (!Objects.equals(legacy.bluePortalId(), legacyBlue) || !Objects.equals(legacy.orangePortalId(), legacyOrange)) {
if (legacyBlue == null && legacyOrange == null) {
this.legacyPairs.remove(owner);
} else {
this.legacyPairs.put(owner, new PairRecord(legacyBlue, legacyOrange));
}
this.setDirty();
}
}
private void adoptLegacyPairs(MinecraftServer server, UUID owner, UUID gunId, String channelName) {
if (gunId == null) {
return;
}
boolean changed = false;
for (Map.Entry<PairKey, PairRecord> entry : new HashMap<>(this.pairs).entrySet()) {
PairKey legacyKey = entry.getKey();
if (!legacyKey.owner().equals(owner) || legacyKey.gunId() != null) {
continue;
}
PairKey ownedKey = new PairKey(owner, legacyKey.dimension(), gunId);
if (this.pairs.containsKey(ownedKey)) {
continue;
}
this.pairs.remove(legacyKey);
this.pairs.put(ownedKey, entry.getValue());
synchronizeLoadedPortal(server, owner, legacyKey.dimension(), entry.getValue().bluePortalId(), gunId, channelName);
synchronizeLoadedPortal(server, owner, legacyKey.dimension(), entry.getValue().orangePortalId(), gunId, channelName);
changed = true;
}
if (changed) {
this.setDirty();
}
}
private static void synchronizeLoadedPortal(MinecraftServer server, UUID owner, ResourceLocation dimension, UUID portalId, UUID gunId, String channelName) {
if (portalId == null) {
return;
}
ServerLevel level = server.getLevel(net.minecraft.resources.ResourceKey.create(net.minecraft.core.registries.Registries.DIMENSION, dimension));
if (level == null || !(level.getEntity(portalId) instanceof PortalGunPortalEntity portal) || !owner.equals(portal.getOwnerId())) {
return;
}
portal.adoptChannelIdentity(gunId, channelName);
}
private static PairRecord withPortal(PairRecord current, PortalGunPortalEntity.PortalSide side, UUID portalId) {
PairRecord pair = current == null ? new PairRecord(null, null) : current;
return side == PortalGunPortalEntity.PortalSide.BLUE
? new PairRecord(pair.bluePortalId() == null ? portalId : pair.bluePortalId(), pair.orangePortalId())
: new PairRecord(pair.bluePortalId(), pair.orangePortalId() == null ? portalId : pair.orangePortalId());
}
private static ServerLevel findPortalLevel(MinecraftServer server, UUID portalId) {
if (portalId == null) {
return null;
}
for (ServerLevel level : server.getAllLevels()) {
if (level.getEntity(portalId) instanceof PortalGunPortalEntity portal && !portal.isRemoved()) {
return level;
}
}
return null;
} }
private static void addPortalIds(Set<UUID> portalIds, PairRecord pair) { private static void addPortalIds(Set<UUID> portalIds, PairRecord pair) {
@@ -439,16 +185,15 @@ public final class PortalGunSavedData extends SavedData {
} }
private static void discardIfLoaded(MinecraftServer server, UUID portalId) { private static void discardIfLoaded(MinecraftServer server, UUID portalId) {
ServerLevel level = findPortalLevel(server, portalId); for (ServerLevel level : server.getAllLevels()) {
if (level != null && level.getEntity(portalId) instanceof PortalGunPortalEntity portal) { if (level.getEntity(portalId) instanceof PortalGunPortalEntity portal && !portal.isRemoved()) {
portal.discard(); portal.discard();
return;
}
} }
} }
private record PairKey(UUID owner, ResourceLocation dimension, UUID gunId) { private record PairKey(ResourceLocation dimension, UUID gunId) {
}
private record OwnerChannelKey(UUID owner, String channelName) {
} }
private record PairRecord(UUID bluePortalId, UUID orangePortalId) { private record PairRecord(UUID bluePortalId, UUID orangePortalId) {
@@ -0,0 +1,18 @@
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());
}
}
}
@@ -0,0 +1,138 @@
package com.craisinlord.antarchy.content.portalgun;
import com.craisinlord.antarchy.content.gravity.AntarchyGravityApi;
import com.craisinlord.antarchy.content.gravity.AntarchyGravityDirection;
import com.craisinlord.antarchy.content.gravity.AntarchyGravityRotationUtil;
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, AntarchyGravityApi.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, AntarchyGravityApi.isGravityInverted(entity));
}
public static Vec3 entityVelocityToWorld(Vec3 velocity, boolean inverted) {
if (!inverted) {
return velocity;
}
return AntarchyGravityRotationUtil.vecPlayerToWorld(velocity, AntarchyGravityDirection.UP);
}
public static Vec3 entityVelocityFromWorld(Entity entity, Vec3 velocity) {
return entityVelocityFromWorld(velocity, AntarchyGravityApi.isGravityInverted(entity));
}
public static Vec3 entityVelocityFromWorld(Vec3 velocity, boolean inverted) {
if (!inverted) {
return velocity;
}
return AntarchyGravityRotationUtil.vecWorldToPlayer(velocity, AntarchyGravityDirection.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 (!AntarchyGravityApi.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));
}
}
@@ -0,0 +1,66 @@
package com.craisinlord.antarchy.content.portalgun;
import com.craisinlord.antarchy.Antarchy;
import net.minecraft.resources.ResourceLocation;
public enum PortalGunVariant {
DEFAULT("", 0x40A0FF, 0xFF961E, "blue", "orange"),
ATLAS("atlas", 0x53A6D0, 0x6A2BE0, "blue", "purple"),
P_BODY("p_body", 0xF9D660, 0xC0282A, "yellow", "red");
private final String key;
private final int blueColor;
private final int orangeColor;
private final String blueColorName;
private final String orangeColorName;
private final ResourceLocation crosshairTexture;
private final ResourceLocation modelTexture;
private final ResourceLocation blueVortexTexture;
private final ResourceLocation orangeVortexTexture;
PortalGunVariant(String key, int blueColor, int orangeColor, String blueColorName, String orangeColorName) {
this.key = key;
this.blueColor = blueColor;
this.orangeColor = orangeColor;
this.blueColorName = blueColorName;
this.orangeColorName = orangeColorName;
String prefix = key.isEmpty() ? "" : key + "_";
this.crosshairTexture = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "textures/gui/portal_gun_" + prefix + "crosshair.png");
this.modelTexture = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "textures/models/item/portal_gun" + (key.isEmpty() ? "" : "_" + key) + ".png");
this.blueVortexTexture = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "textures/entity/portal_gun/portal_" + prefix + "blue_vortex.png");
this.orangeVortexTexture = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "textures/entity/portal_gun/portal_" + prefix + "orange_vortex.png");
}
public String key() {
return this.key;
}
public int color(PortalGunPortalEntity.PortalSide side) {
return side == PortalGunPortalEntity.PortalSide.BLUE ? this.blueColor : this.orangeColor;
}
public String colorTranslationKey(PortalGunPortalEntity.PortalSide side) {
return "tooltip.antarchy.portal_gun.color." + (side == PortalGunPortalEntity.PortalSide.BLUE ? this.blueColorName : this.orangeColorName);
}
public ResourceLocation crosshairTexture() {
return this.crosshairTexture;
}
public ResourceLocation modelTexture() {
return this.modelTexture;
}
public ResourceLocation vortexTexture(PortalGunPortalEntity.PortalSide side) {
return side == PortalGunPortalEntity.PortalSide.BLUE ? this.blueVortexTexture : this.orangeVortexTexture;
}
public int id() {
return this.ordinal();
}
public static PortalGunVariant byId(int id) {
PortalGunVariant[] values = values();
return id >= 0 && id < values.length ? values[id] : DEFAULT;
}
}
@@ -52,6 +52,19 @@ public record PortalGunWorldPortalShape(
}; };
} }
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) { public PortalLocalCoords localCoords(Vec3 position) {
Vec3 relative = position.subtract(this.center); Vec3 relative = position.subtract(this.center);
return new PortalLocalCoords(relative.dot(this.right), relative.dot(this.up), relative.dot(this.normal)); return new PortalLocalCoords(relative.dot(this.right), relative.dot(this.up), relative.dot(this.normal));
@@ -28,10 +28,12 @@ import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.WeakHashMap; import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
/** Places the authored King's Tree as independently generated chunk slices on a fixed world grid. */ /** Places the authored King's Tree as independently generated chunk slices on a fixed world grid. */
public final class KingsTreeGridFeature extends Feature<NoneFeatureConfiguration> { public final class KingsTreeGridFeature extends Feature<NoneFeatureConfiguration> {
private static final Map<ServerLevel, Set<Long>> CLAIMED_TREE_KINGS = new WeakHashMap<>(); private static final Map<ServerLevel, Set<Long>> CLAIMED_TREE_KINGS = new WeakHashMap<>();
private static final ConcurrentLinkedQueue<PendingKingSpawn> PENDING_KING_SPAWNS = new ConcurrentLinkedQueue<>();
private static final int TREE_MIN_X = -191; private static final int TREE_MIN_X = -191;
private static final int TREE_MAX_X = 192; private static final int TREE_MAX_X = 192;
private static final int TREE_MIN_Z = -227; private static final int TREE_MIN_Z = -227;
@@ -108,53 +110,70 @@ public final class KingsTreeGridFeature extends Feature<NoneFeatureConfiguration
} }
} }
if (centralTilePlaced) { if (centralTilePlaced) {
spawnTreeKing(level, centerX, centerZ, baseY); queueTreeKing(level.getLevel(), centerX, centerZ, baseY);
} }
return placed; return placed;
} }
private static void spawnTreeKing(WorldGenLevel level, int centerX, int centerZ, int baseY) { private static void queueTreeKing(ServerLevel serverLevel, int centerX, int centerZ, int baseY) {
var serverLevel = level.getLevel();
long treeKey = ((long) centerX << 32) ^ (centerZ & 0xffffffffL); long treeKey = ((long) centerX << 32) ^ (centerZ & 0xffffffffL);
synchronized (serverLevel) { synchronized (CLAIMED_TREE_KINGS) {
synchronized (CLAIMED_TREE_KINGS) { if (!CLAIMED_TREE_KINGS.computeIfAbsent(serverLevel, ignored -> new java.util.HashSet<>()).add(treeKey)) {
if (!CLAIMED_TREE_KINGS.computeIfAbsent(serverLevel, ignored -> new java.util.HashSet<>()).add(treeKey)) {
return;
}
}
Vec3 treeCenter = new Vec3(centerX + 0.5D, 0.0D, centerZ + 0.5D);
AABB searchBox = new AABB(
centerX - 512.0D, level.getMinBuildHeight(), centerZ - 512.0D,
centerX + 512.0D, level.getMaxBuildHeight(), centerZ + 512.0D);
List<KingEntity> treeKings = serverLevel.getEntitiesOfClass(KingEntity.class, searchBox,
king -> king.isAlive() && king.isTreePatrolFor(treeCenter));
if (!treeKings.isEmpty()) {
// Repair older worlds that already contain duplicate tree-bound Kings.
for (int index = 1; index < treeKings.size(); index++) {
treeKings.get(index).discard();
}
return; return;
} }
KingEntity king = AntarchyObjects.KING.get().create(serverLevel);
if (king == null) {
synchronized (CLAIMED_TREE_KINGS) {
CLAIMED_TREE_KINGS.getOrDefault(serverLevel, Set.of()).remove(treeKey);
}
return;
}
double minimumY = baseY + 24.0D;
double maximumY = baseY + 304.0D;
// Introduce the King high in the canopy, where his tree patrol is most visible.
double spawnY = minimumY + (maximumY - minimumY) * 0.65D;
Vec3 spawn = findSafeKingSpawn(serverLevel, king, centerX, centerZ, spawnY);
king.moveTo(spawn.x, spawn.y, spawn.z,
(float) (Math.atan2(centerZ + 0.5D - spawn.z, centerX + 0.5D - spawn.x)
* 180.0D / Math.PI) - 90.0F,
0.0F);
king.setTreePatrolHome(treeCenter, minimumY, maximumY, 0.0D);
king.setPersistenceRequired();
serverLevel.addFreshEntity(king);
} }
PENDING_KING_SPAWNS.add(new PendingKingSpawn(serverLevel, centerX, centerZ, baseY));
}
public static void tickPendingSpawns(ServerLevel level) {
int pendingCount = PENDING_KING_SPAWNS.size();
for (int index = 0; index < pendingCount; index++) {
PendingKingSpawn pending = PENDING_KING_SPAWNS.poll();
if (pending == null) {
return;
}
if (pending.level() != level || !spawnTreeKing(pending)) {
PENDING_KING_SPAWNS.add(pending);
}
}
}
private static boolean spawnTreeKing(PendingKingSpawn pending) {
ServerLevel serverLevel = pending.level();
int centerX = pending.centerX();
int centerZ = pending.centerZ();
int baseY = pending.baseY();
Vec3 treeCenter = new Vec3(centerX + 0.5D, 0.0D, centerZ + 0.5D);
AABB searchBox = new AABB(
centerX - 512.0D, serverLevel.getMinBuildHeight(), centerZ - 512.0D,
centerX + 512.0D, serverLevel.getMaxBuildHeight(), centerZ + 512.0D);
List<KingEntity> treeKings = serverLevel.getEntitiesOfClass(KingEntity.class, searchBox,
king -> king.isAlive() && king.isTreePatrolFor(treeCenter));
if (!treeKings.isEmpty()) {
for (int index = 1; index < treeKings.size(); index++) {
treeKings.get(index).discard();
}
return true;
}
KingEntity king = AntarchyObjects.KING.get().create(serverLevel);
if (king == null) {
return true;
}
double minimumY = baseY + 24.0D;
double maximumY = baseY + 304.0D;
double spawnY = minimumY + (maximumY - minimumY) * 0.65D;
Vec3 spawn = findSafeKingSpawn(serverLevel, king, centerX, centerZ, spawnY);
if (spawn == null) {
return false;
}
king.moveTo(spawn.x, spawn.y, spawn.z,
(float) (Math.atan2(centerZ + 0.5D - spawn.z, centerX + 0.5D - spawn.x)
* 180.0D / Math.PI) - 90.0F,
0.0F);
king.setTreePatrolHome(treeCenter, minimumY, maximumY, 0.0D);
king.setPersistenceRequired();
serverLevel.addFreshEntity(king);
return true;
} }
private static Vec3 findSafeKingSpawn(ServerLevel level, KingEntity king, private static Vec3 findSafeKingSpawn(ServerLevel level, KingEntity king,
@@ -169,7 +188,7 @@ public final class KingsTreeGridFeature extends Feature<NoneFeatureConfiguration
for (double heightOffset : heightOffsets) { for (double heightOffset : heightOffsets) {
Vec3 candidate = new Vec3(x, spawnY + heightOffset, z); Vec3 candidate = new Vec3(x, spawnY + heightOffset, z);
BlockPos blockPos = BlockPos.containing(candidate); BlockPos blockPos = BlockPos.containing(candidate);
if (!level.hasChunksAt(blockPos.offset(-8, -8, -8), blockPos.offset(8, 16, 8))) { if (!areCollisionChunksLoaded(level, blockPos.offset(-8, -8, -8), blockPos.offset(8, 16, 8))) {
continue; continue;
} }
king.moveTo(candidate.x, candidate.y, candidate.z, 0.0F, 0.0F); king.moveTo(candidate.x, candidate.y, candidate.z, 0.0F, 0.0F);
@@ -179,7 +198,22 @@ public final class KingsTreeGridFeature extends Feature<NoneFeatureConfiguration
} }
} }
} }
return new Vec3(centerX + 64.5D, spawnY + 32.0D, centerZ + 0.5D); return null;
}
private static boolean areCollisionChunksLoaded(ServerLevel level, BlockPos min, BlockPos max) {
var chunkSource = level.getChunkSource();
for (int chunkX = min.getX() >> 4; chunkX <= max.getX() >> 4; chunkX++) {
for (int chunkZ = min.getZ() >> 4; chunkZ <= max.getZ() >> 4; chunkZ++) {
if (chunkSource.getChunkNow(chunkX, chunkZ) == null) {
return false;
}
}
}
return true;
}
private record PendingKingSpawn(ServerLevel level, int centerX, int centerZ, int baseY) {
} }
private static ResourceLocation tileLocation(int tileX, int tileZ) { private static ResourceLocation tileLocation(int tileX, int tileZ) {
@@ -3,6 +3,7 @@ package com.craisinlord.antarchy.mixins;
import com.craisinlord.antarchy.content.portalgun.PortalGunCollisionHelper; import com.craisinlord.antarchy.content.portalgun.PortalGunCollisionHelper;
import com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity; import com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity;
import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.MoverType;
import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.Vec3;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
@@ -12,34 +13,30 @@ import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(Entity.class) @Mixin(value = Entity.class, priority = 1100)
public abstract class PortalGunEntityCollisionMixin { public abstract class PortalGunEntityCollisionMixin {
@Unique @Unique
private AABB antarchy$portalGunStartBox; private AABB antarchy$portalGunStartBox;
@Unique @Unique
private AABB antarchy$portalGunMoveStartBox; private AABB antarchy$portalGunMoveStartBox;
@Unique @Unique
private Vec3 antarchy$portalGunMoveStartPosition; private Vec3 antarchy$portalGunMoveStartVelocity;
@Inject(method = "move", at = @At("HEAD")) @Inject(method = "move", at = @At("HEAD"))
private void antarchy$capturePortalGunMovementStart(net.minecraft.world.entity.MoverType type, Vec3 movement, CallbackInfo ci) { private void antarchy$capturePortalGunMovementStart(MoverType type, Vec3 movement, CallbackInfo ci) {
Entity entity = (Entity) (Object) this; this.antarchy$portalGunMoveStartBox = ((Entity) (Object) this).getBoundingBox();
this.antarchy$portalGunMoveStartBox = entity.getBoundingBox(); this.antarchy$portalGunMoveStartVelocity = ((Entity) (Object) this).getDeltaMovement();
this.antarchy$portalGunMoveStartPosition = entity.position();
} }
@Inject(method = "move", at = @At("RETURN")) @Inject(method = "move", at = @At("RETURN"))
private void antarchy$teleportMobOnPortalCrossing(net.minecraft.world.entity.MoverType type, Vec3 movement, CallbackInfo ci) { private void antarchy$teleportMobOnPortalCrossing(MoverType type, Vec3 movement, CallbackInfo ci) {
Entity entity = (Entity) (Object) this; AABB startBox = this.antarchy$portalGunMoveStartBox;
if (this.antarchy$portalGunMoveStartBox != null && this.antarchy$portalGunMoveStartPosition != null) { Vec3 startVelocity = this.antarchy$portalGunMoveStartVelocity;
PortalGunPortalEntity.teleportEntityAfterMovement(
entity,
this.antarchy$portalGunMoveStartBox,
entity.position().subtract(this.antarchy$portalGunMoveStartPosition)
);
}
this.antarchy$portalGunMoveStartBox = null; this.antarchy$portalGunMoveStartBox = null;
this.antarchy$portalGunMoveStartPosition = null; this.antarchy$portalGunMoveStartVelocity = null;
if (startBox != null && startVelocity != null) {
PortalGunPortalEntity.teleportEntityAfterMovement((Entity) (Object) this, startBox, startVelocity);
}
} }
@Inject(method = "collide", at = @At("HEAD")) @Inject(method = "collide", at = @At("HEAD"))
@@ -53,8 +50,11 @@ public abstract class PortalGunEntityCollisionMixin {
if (this.antarchy$portalGunStartBox == null) { if (this.antarchy$portalGunStartBox == null) {
return; return;
} }
Vec3 adjusted = PortalGunCollisionHelper.resolveCollision(entity, this.antarchy$portalGunStartBox, movement, cir.getReturnValue()); Vec3 collided = cir.getReturnValue();
cir.setReturnValue(adjusted); Vec3 adjusted = PortalGunCollisionHelper.resolveCollision(entity, this.antarchy$portalGunStartBox, movement, collided);
if (adjusted != collided) {
cir.setReturnValue(adjusted);
}
} }
} }
@@ -15,7 +15,7 @@ public abstract class PortalGunEntityInWallMixin {
if (entity.level() == null) { if (entity.level() == null) {
return; return;
} }
for (PortalGunPortalEntity portal : entity.level().getEntitiesOfClass(PortalGunPortalEntity.class, entity.getBoundingBox().inflate(2.0D), PortalGunPortalEntity::isAlive)) { for (PortalGunPortalEntity portal : com.craisinlord.antarchy.content.portalgun.PortalGunPortalRegistry.near(entity.level(), entity.getBoundingBox().inflate(2.0D))) {
if (portal.intersectsEntityBounds(entity) || portal.getCollisionRemovalAabbForEntity(entity).intersects(entity.getBoundingBox())) { if (portal.intersectsEntityBounds(entity) || portal.getCollisionRemovalAabbForEntity(entity).intersects(entity.getBoundingBox())) {
cir.setReturnValue(false); cir.setReturnValue(false);
return; return;
@@ -0,0 +1,22 @@
package com.craisinlord.antarchy.mixins;
import com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity;
import net.minecraft.world.entity.projectile.Projectile;
import net.minecraft.world.entity.projectile.ProjectileDeflection;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.HitResult;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(Projectile.class)
public abstract class PortalGunProjectileHitMixin {
@Inject(method = "hitTargetOrDeflectSelf", at = @At("HEAD"), cancellable = true)
private void antarchy$passThroughPortal(HitResult hit, CallbackInfoReturnable<ProjectileDeflection> cir) {
if (hit.getType() == HitResult.Type.BLOCK && hit instanceof BlockHitResult blockHit
&& PortalGunPortalEntity.passProjectileThroughPortal((Projectile) (Object) this, blockHit)) {
cir.setReturnValue(ProjectileDeflection.NONE);
}
}
}
@@ -0,0 +1,37 @@
package com.craisinlord.antarchy.mixins.client;
import com.craisinlord.antarchy.content.client.PortalGunClientTransit;
import net.minecraft.client.player.LocalPlayer;
import net.minecraft.world.entity.MoverType;
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(LocalPlayer.class)
public abstract class LocalPlayerPortalTransitMixin {
@Unique
private AABB antarchy$portalTransitStartBox;
@Unique
private Vec3 antarchy$portalTransitStartVelocity;
@Inject(method = "move", at = @At("HEAD"))
private void antarchy$capturePortalTransitStart(MoverType type, Vec3 movement, CallbackInfo ci) {
this.antarchy$portalTransitStartBox = ((LocalPlayer) (Object) this).getBoundingBox();
this.antarchy$portalTransitStartVelocity = ((LocalPlayer) (Object) this).getDeltaMovement();
}
@Inject(method = "move", at = @At("RETURN"))
private void antarchy$applyPortalTransit(MoverType type, Vec3 movement, CallbackInfo ci) {
AABB startBox = this.antarchy$portalTransitStartBox;
Vec3 startVelocity = this.antarchy$portalTransitStartVelocity;
this.antarchy$portalTransitStartBox = null;
this.antarchy$portalTransitStartVelocity = null;
if (startBox != null && startVelocity != null && type == MoverType.SELF) {
PortalGunClientTransit.afterLocalMove((LocalPlayer) (Object) this, startBox, startVelocity);
}
}
}
@@ -1,6 +1,5 @@
package com.craisinlord.antarchy.mixins.client; package com.craisinlord.antarchy.mixins.client;
import com.craisinlord.antarchy.content.entity.DorrieEntity;
import com.craisinlord.antarchy.content.entity.HerculesBeetleEntity; import com.craisinlord.antarchy.content.entity.HerculesBeetleEntity;
import net.minecraft.client.Camera; import net.minecraft.client.Camera;
import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.Entity;
@@ -40,10 +39,5 @@ public abstract class MountedVehicleCameraMixin {
return; return;
} }
// if (vehicle instanceof DorrieEntity) {
// args.set(0, (Float) args.get(0) * 0.25F);
// args.set(1, (Float) args.get(1) + 0.1F);
// args.set(2, (Float) args.get(2) + 0.55F);
// }
} }
} }
@@ -54,7 +54,7 @@ public abstract class PortalGunEntityTeleportPacketMixin {
double edgeAllowance = entityExtent * 0.5D; double edgeAllowance = entityExtent * 0.5D;
AABB search = entity.getBoundingBox().move(offset).inflate(entityExtent + PortalGunWorldPortalShape.DEFAULT_SCAN_DISTANCE); AABB search = entity.getBoundingBox().move(offset).inflate(entityExtent + PortalGunWorldPortalShape.DEFAULT_SCAN_DISTANCE);
Vec3 probe = destination.add(0.0D, entity.getEyeHeight(), 0.0D); Vec3 probe = destination.add(0.0D, entity.getEyeHeight(), 0.0D);
for (PortalGunPortalEntity portal : entity.level().getEntitiesOfClass(PortalGunPortalEntity.class, search, Entity::isAlive)) { for (PortalGunPortalEntity portal : com.craisinlord.antarchy.content.portalgun.PortalGunPortalRegistry.near(entity.level(), search)) {
PortalGunWorldPortalShape.PortalLocalCoords coords = portal.getWorldPortalShape().localCoords(probe); PortalGunWorldPortalShape.PortalLocalCoords coords = portal.getWorldPortalShape().localCoords(probe);
if (coords.depth() >= -entityExtent - 0.5D if (coords.depth() >= -entityExtent - 0.5D
&& coords.depth() <= PortalGunWorldPortalShape.DEFAULT_SCAN_DISTANCE + 0.5D && coords.depth() <= PortalGunWorldPortalShape.DEFAULT_SCAN_DISTANCE + 0.5D
@@ -4,7 +4,6 @@ import com.craisinlord.antarchy.content.client.PortalGunRollClientState;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.ScreenEffectRenderer; import net.minecraft.client.renderer.ScreenEffectRenderer;
import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.block.Blocks;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
@@ -16,7 +15,7 @@ public abstract class ScreenEffectRendererPortalMixin {
private static void antarchy$ignorePortalViewBlock(Player player, CallbackInfoReturnable<net.minecraft.world.level.block.state.BlockState> cir) { private static void antarchy$ignorePortalViewBlock(Player player, CallbackInfoReturnable<net.minecraft.world.level.block.state.BlockState> cir) {
Minecraft minecraft = Minecraft.getInstance(); Minecraft minecraft = Minecraft.getInstance();
if (player != null && PortalGunRollClientState.isEntityInsidePortal(minecraft, player)) { if (player != null && PortalGunRollClientState.isEntityInsidePortal(minecraft, player)) {
cir.setReturnValue(Blocks.AIR.defaultBlockState()); cir.setReturnValue(null);
} }
} }
} }
@@ -0,0 +1,33 @@
package com.craisinlord.antarchy.mixins.gravity;
import com.craisinlord.antarchy.content.gravity.AntarchyGravityApi;
import net.minecraft.core.BlockPos;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.BubbleColumnBlock;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.Vec3;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(BubbleColumnBlock.class)
public abstract class BubbleColumnGravityMixin {
@Inject(method = "entityInside", at = @At("HEAD"), cancellable = true)
private void antarchy$applyBubbleForces(BlockState state, Level level, BlockPos pos, Entity entity, CallbackInfo ci) {
if (!AntarchyGravityApi.isGravityInverted(entity)) {
return;
}
boolean drag = state.getValue(BubbleColumnBlock.DRAG_DOWN);
Vec3 velocity = AntarchyGravityApi.getWorldVelocity(entity);
double vertical = drag ? Math.max(-0.9D, velocity.y - 0.03D) : Math.min(1.8D, velocity.y + 0.1D);
AntarchyGravityApi.setWorldVelocity(entity, new Vec3(velocity.x, vertical, velocity.z));
entity.onAboveBubbleCol(!drag);
if (drag) {
entity.resetFallDistance();
}
ci.cancel();
}
}
@@ -4,7 +4,6 @@ import com.craisinlord.antarchy.content.fluid.AntarchyFluidChecks;
import com.craisinlord.antarchy.content.gravity.AntarchyGravityApi; import com.craisinlord.antarchy.content.gravity.AntarchyGravityApi;
import com.craisinlord.antarchy.content.gravity.AntarchyGravityDirection; import com.craisinlord.antarchy.content.gravity.AntarchyGravityDirection;
import com.craisinlord.antarchy.content.gravity.AntarchyGravityRotationUtil; import com.craisinlord.antarchy.content.gravity.AntarchyGravityRotationUtil;
// import com.craisinlord.antarchy.content.portalgun.PortalGunCollisionHelper;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation; import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import java.util.List; import java.util.List;
@@ -1,6 +1,8 @@
package com.craisinlord.antarchy.mixins.gravity; package com.craisinlord.antarchy.mixins.gravity;
import net.minecraft.network.protocol.game.VecDeltaCodec;
import net.minecraft.server.level.ServerEntity; import net.minecraft.server.level.ServerEntity;
import net.minecraft.world.phys.Vec3;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor; import org.spongepowered.asm.mixin.gen.Accessor;
@@ -9,6 +11,12 @@ import org.spongepowered.asm.mixin.gen.Accessor;
* Accessor for server entity tracking internals. * Accessor for server entity tracking internals.
*/ */
public interface ServerEntityAccessor { public interface ServerEntityAccessor {
@Accessor("positionCodec")
VecDeltaCodec antarchy$getPositionCodec();
@Accessor("lastSentMovement")
void antarchy$setLastSentMovement(Vec3 lastSentMovement);
@Accessor("updateInterval") @Accessor("updateInterval")
void antarchy$setUpdateInterval(int updateInterval); void antarchy$setUpdateInterval(int updateInterval);
} }
@@ -7,7 +7,9 @@
"AbstractMinecartMixin", "AbstractMinecartMixin",
"AbstractArrowAccessor", "AbstractArrowAccessor",
"PortalGunEntityCollisionMixin", "PortalGunEntityCollisionMixin",
"PortalGunEntityInWallMixin",
"PortalGunPlayerMovePacketMixin", "PortalGunPlayerMovePacketMixin",
"PortalGunProjectileHitMixin",
"ServerGamePacketListenerTeleportAccessor", "ServerGamePacketListenerTeleportAccessor",
"PortalGunZombieMixin", "PortalGunZombieMixin",
"FallingBlockEntityAccessor", "FallingBlockEntityAccessor",
@@ -71,6 +73,7 @@
"gravity.AbstractArrowGravityMixin", "gravity.AbstractArrowGravityMixin",
"gravity.BehaviorUtilsGravityMixin", "gravity.BehaviorUtilsGravityMixin",
"gravity.BucketItemGravityMixin", "gravity.BucketItemGravityMixin",
"gravity.BubbleColumnGravityMixin",
"gravity.ChunkMapAccessor", "gravity.ChunkMapAccessor",
"gravity.CrossbowItemGravityMixin", "gravity.CrossbowItemGravityMixin",
"gravity.DirectionFacingMixin", "gravity.DirectionFacingMixin",
@@ -171,6 +174,7 @@
"client.MountedVehicleCameraMixin", "client.MountedVehicleCameraMixin",
"client.ParalyzedMouseTurnMixin", "client.ParalyzedMouseTurnMixin",
"client.LocalPlayerPortalMixin", "client.LocalPlayerPortalMixin",
"client.LocalPlayerPortalTransitMixin",
"client.PortalGunEntityTeleportPacketMixin", "client.PortalGunEntityTeleportPacketMixin",
"client.PortalGunZoomFovMixin", "client.PortalGunZoomFovMixin",
"client.LivingEntityPortalLerpAccessor", "client.LivingEntityPortalLerpAccessor",
@@ -671,6 +671,8 @@
"tooltip.antarchy.reverie_bottle.release": "Right click to release the Reverie.", "tooltip.antarchy.reverie_bottle.release": "Right click to release the Reverie.",
"item.antarchy.gravity_gun": "Gravity Gun", "item.antarchy.gravity_gun": "Gravity Gun",
"item.antarchy.portal_gun": "Portal Gun", "item.antarchy.portal_gun": "Portal Gun",
"item.antarchy.portal_gun_atlas": "Atlas Portal Gun",
"item.antarchy.portal_gun_p_body": "P-body Portal Gun",
"item.antarchy.diamond_minecart": "The Diamond Minecart", "item.antarchy.diamond_minecart": "The Diamond Minecart",
"item.antarchy.cogwheel_pet": "Cogwheel Pet", "item.antarchy.cogwheel_pet": "Cogwheel Pet",
"item.antarchy.fire_dragon_pet": "Fire Dragon Pet", "item.antarchy.fire_dragon_pet": "Fire Dragon Pet",
@@ -837,13 +839,14 @@
"tooltip.antarchy.gravity_gun.secondary": "Usage: pull in an entity or a valid block.", "tooltip.antarchy.gravity_gun.secondary": "Usage: pull in an entity or a valid block.",
"tooltip.antarchy.gravity_gun.scroll": "Hold Ctrl and scroll to move the held target closer or farther away.", "tooltip.antarchy.gravity_gun.scroll": "Hold Ctrl and scroll to move the held target closer or farther away.",
"tooltip.antarchy.gravity_gun.blacklist": "Does not affect blacklisted blocks or entities.", "tooltip.antarchy.gravity_gun.blacklist": "Does not affect blacklisted blocks or entities.",
"tooltip.antarchy.portal_gun.primary": "Attack: fire a blue portal.", "tooltip.antarchy.portal_gun.primary": "Attack: fire a %s portal.",
"tooltip.antarchy.portal_gun.secondary": "Use: fire an orange portal.", "tooltip.antarchy.portal_gun.secondary": "Use: fire a %s portal.",
"tooltip.antarchy.portal_gun.channel": "Channel: %s", "tooltip.antarchy.portal_gun.color.blue": "blue",
"tooltip.antarchy.portal_gun.owner": "Owner: %s", "tooltip.antarchy.portal_gun.color.orange": "orange",
"tooltip.antarchy.portal_gun.color.purple": "purple",
"tooltip.antarchy.portal_gun.color.yellow": "yellow",
"tooltip.antarchy.portal_gun.color.red": "red",
"tooltip.antarchy.portal_gun.reset": "Sneak + use: fizzle your active portals.", "tooltip.antarchy.portal_gun.reset": "Sneak + use: fizzle your active portals.",
"tooltip.antarchy.portal_gun.size": "Portal size: %1$s x %2$s",
"tooltip.antarchy.portal_gun.grab_strength": "Grab strength: %s/4",
"tooltip.antarchy.antimetal_inversion": "Inverts entites directly below it", "tooltip.antarchy.antimetal_inversion": "Inverts entites directly below it",
"tooltip.antarchy.diamond_minecart.forward": "Will be implemented later.", "tooltip.antarchy.diamond_minecart.forward": "Will be implemented later.",
"tooltip.antarchy.diamond_minecart.rails": " ", "tooltip.antarchy.diamond_minecart.rails": " ",
@@ -1250,7 +1253,6 @@
"advancements.antarchy.duct_tape.description": "and fixed it with the power of duct tape!", "advancements.antarchy.duct_tape.description": "and fixed it with the power of duct tape!",
"key.categories.antarchy": "Antarchy", "key.categories.antarchy": "Antarchy",
"key.antarchy.portal_gun_zoom": "Portal Gun Zoom", "key.antarchy.portal_gun_zoom": "Portal Gun Zoom",
"key.antarchy.portal_gun_grab": "Portal Gun Grab",
"key.antarchy.portal_gun_reset": "Portal Gun Reset", "key.antarchy.portal_gun_reset": "Portal Gun Reset",
"key.antarchy.brutalfly_flap": "Brutalfly Elytra: Flap", "key.antarchy.brutalfly_flap": "Brutalfly Elytra: Flap",
"advancements.antarchy.shrink_ray_craft.title": "Honey, I Shrunk the Mobs", "advancements.antarchy.shrink_ray_craft.title": "Honey, I Shrunk the Mobs",
@@ -0,0 +1,7 @@
{
"parent": "minecraft:item/generated",
"gui_light": "front",
"textures": {
"layer0": "antarchy:item/portal_gun/portal_gun_atlas_item"
}
}
@@ -0,0 +1,7 @@
{
"parent": "minecraft:item/generated",
"gui_light": "front",
"textures": {
"layer0": "antarchy:item/portal_gun/portal_gun_p_body_item"
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 704 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 706 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 705 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 701 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 286 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 341 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 534 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 415 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 416 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 648 B

@@ -0,0 +1,27 @@
{
"type": "minecraft:crafting_shaped",
"category": "equipment",
"pattern": [
"T N",
"TSN",
"TBT"
],
"key": {
"T": {
"item": "antarchy:titanium_ingot"
},
"N": {
"item": "antarchy:nightmare_scale"
},
"S": {
"item": "minecraft:nether_star"
},
"B": {
"item": "antarchy:bluestone_comparator"
}
},
"result": {
"id": "antarchy:portal_gun",
"count": 1
}
}
@@ -0,0 +1,19 @@
{
"type": "minecraft:crafting_shapeless",
"category": "equipment",
"ingredients": [
{
"item": "antarchy:portal_gun"
},
{
"item": "minecraft:light_blue_dye"
},
{
"item": "minecraft:purple_dye"
}
],
"result": {
"id": "antarchy:portal_gun_atlas",
"count": 1
}
}
@@ -0,0 +1,19 @@
{
"type": "minecraft:crafting_shapeless",
"category": "equipment",
"ingredients": [
{
"item": "antarchy:portal_gun"
},
{
"item": "minecraft:yellow_dye"
},
{
"item": "minecraft:red_dye"
}
],
"result": {
"id": "antarchy:portal_gun_p_body",
"count": 1
}
}
@@ -94,13 +94,13 @@
}, },
{ {
"type": "antarchy:apple_cow", "type": "antarchy:apple_cow",
"weight": 24, "weight": 20,
"minCount": 1, "minCount": 1,
"maxCount": 3 "maxCount": 3
}, },
{ {
"type": "antarchy:golden_apple_cow", "type": "antarchy:golden_apple_cow",
"weight": 20, "weight": 16,
"minCount": 1, "minCount": 1,
"maxCount": 3 "maxCount": 3
}, },
@@ -34,6 +34,10 @@
} }
}, },
"spawn_costs": { "spawn_costs": {
"antarchy:basilisk": {
"energy_budget": 0.1,
"charge": 14.0
},
"antarchy:glimmer": { "antarchy:glimmer": {
"energy_budget": 0.1, "energy_budget": 0.1,
"charge": 9 "charge": 9
@@ -86,7 +90,14 @@
], ],
"creature_spawn_probability": 0.07, "creature_spawn_probability": 0.07,
"spawners": { "spawners": {
"monster": [], "monster": [
{
"type": "antarchy:basilisk",
"weight": 10,
"minCount": 1,
"maxCount": 1
}
],
"creature": [ "creature": [
{ {
"type": "minecraft:frog", "type": "minecraft:frog",
@@ -93,7 +93,7 @@
"monster": [ "monster": [
{ {
"type": "antarchy:basilisk", "type": "antarchy:basilisk",
"weight": 20, "weight": 24,
"minCount": 1, "minCount": 1,
"maxCount": 1 "maxCount": 1
} }
@@ -96,13 +96,13 @@
}, },
{ {
"type": "antarchy:apple_cow", "type": "antarchy:apple_cow",
"weight": 24, "weight": 20,
"minCount": 1, "minCount": 1,
"maxCount": 3 "maxCount": 3
}, },
{ {
"type": "antarchy:golden_apple_cow", "type": "antarchy:golden_apple_cow",
"weight": 20, "weight": 16,
"minCount": 1, "minCount": 1,
"maxCount": 3 "maxCount": 3
}, },
@@ -39,8 +39,8 @@
], ],
"placement": { "placement": {
"salt": 659843583, "salt": 659843583,
"spacing": 42, "spacing": 37,
"separation": 35, "separation": 30,
"type": "integrated_api:advanced_random_spread" "type": "integrated_api:advanced_random_spread"
} }
} }
@@ -49,7 +49,7 @@
"base": 13, "base": 13,
"per_level_above_first": 8 "per_level_above_first": 8
}, },
"max_level": 10, "max_level": 4,
"min_cost": { "min_cost": {
"base": 5, "base": 5,
"per_level_above_first": 8 "per_level_above_first": 8
@@ -54,7 +54,7 @@
"base": 18, "base": 18,
"per_level_above_first": 8 "per_level_above_first": 8
}, },
"max_level": 10, "max_level": 4,
"min_cost": { "min_cost": {
"base": 10, "base": 10,
"per_level_above_first": 8 "per_level_above_first": 8
@@ -37,7 +37,7 @@
"base": 9, "base": 9,
"per_level_above_first": 6 "per_level_above_first": 6
}, },
"max_level": 10, "max_level": 4,
"min_cost": { "min_cost": {
"base": 3, "base": 3,
"per_level_above_first": 6 "per_level_above_first": 6
@@ -33,7 +33,7 @@
"base": 12, "base": 12,
"per_level_above_first": 11 "per_level_above_first": 11
}, },
"max_level": 10, "max_level": 4,
"min_cost": { "min_cost": {
"base": 1, "base": 1,
"per_level_above_first": 11 "per_level_above_first": 11
@@ -7,7 +7,6 @@ import com.craisinlord.antarchy.content.entity.ReverieEntity;
import com.craisinlord.antarchy.content.entity.ant.RainbowAntEntity; import com.craisinlord.antarchy.content.entity.ant.RainbowAntEntity;
import com.craisinlord.antarchy.config.AntarchySettings; import com.craisinlord.antarchy.config.AntarchySettings;
import com.craisinlord.antarchy.content.AntarchyObjects; import com.craisinlord.antarchy.content.AntarchyObjects;
import com.craisinlord.antarchy.content.block.DuctTapeBlock;
import com.craisinlord.antarchy.content.item.AttitudeAdjusterItem; import com.craisinlord.antarchy.content.item.AttitudeAdjusterItem;
import com.craisinlord.antarchy.content.item.AttitudeAdjusterSlamManager; import com.craisinlord.antarchy.content.item.AttitudeAdjusterSlamManager;
import com.craisinlord.antarchy.content.fluid.AntarchyFluidChecks; import com.craisinlord.antarchy.content.fluid.AntarchyFluidChecks;
@@ -19,7 +18,6 @@ import com.craisinlord.antarchy.content.gravity.AntarchyGravityTransition;
import com.craisinlord.antarchy.content.horde.CavarynHordeManager; import com.craisinlord.antarchy.content.horde.CavarynHordeManager;
import com.craisinlord.antarchy.content.portal.PermanentPortalManager; import com.craisinlord.antarchy.content.portal.PermanentPortalManager;
import com.craisinlord.antarchy.content.portalgun.PortalGunProjectileEntity; import com.craisinlord.antarchy.content.portalgun.PortalGunProjectileEntity;
import com.craisinlord.antarchy.content.portalgun.PortalGunGrabManager;
import com.craisinlord.antarchy.content.command.CavarynCommand; import com.craisinlord.antarchy.content.command.CavarynCommand;
import com.craisinlord.antarchy.content.command.CaterpillarCommand; import com.craisinlord.antarchy.content.command.CaterpillarCommand;
import com.craisinlord.antarchy.content.command.DimensionalTearCommand; import com.craisinlord.antarchy.content.command.DimensionalTearCommand;
@@ -78,7 +76,6 @@ public final class AntarchyFabricEvents {
public static void register() { public static void register() {
ServerLifecycleEvents.SERVER_STOPPING.register(server -> PortalGunProjectileEntity.releaseAllProjectileTickets()); ServerLifecycleEvents.SERVER_STOPPING.register(server -> PortalGunProjectileEntity.releaseAllProjectileTickets());
ServerLifecycleEvents.SERVER_STOPPING.register(PortalGunGrabManager::clear);
ServerLifecycleEvents.SERVER_STOPPING.register(com.craisinlord.antarchy.content.portalgun.PortalGunResetManager::clear); ServerLifecycleEvents.SERVER_STOPPING.register(com.craisinlord.antarchy.content.portalgun.PortalGunResetManager::clear);
ServerPlayConnectionEvents.JOIN.register((handler, sender, server) -> { ServerPlayConnectionEvents.JOIN.register((handler, sender, server) -> {
ChronosphereManager.refresh(handler.player); ChronosphereManager.refresh(handler.player);
@@ -209,14 +206,13 @@ public final class AntarchyFabricEvents {
}); });
ServerTickEvents.END_SERVER_TICK.register(server -> { ServerTickEvents.END_SERVER_TICK.register(server -> {
PortalGunGrabManager.tick(server);
Set<UUID> activeThisTick = new HashSet<>(); Set<UUID> activeThisTick = new HashSet<>();
for (ServerLevel level : server.getAllLevels()) { for (ServerLevel level : server.getAllLevels()) {
CavarynHordeManager.tick(level); CavarynHordeManager.tick(level);
com.craisinlord.antarchy.content.horde.CavarynCreatureSpawner.tick(level); com.craisinlord.antarchy.content.horde.CavarynCreatureSpawner.tick(level);
ThoraxisUndersideManager.tick(level); ThoraxisUndersideManager.tick(level);
com.craisinlord.antarchy.content.worldgen.elythia.KingsTreeGridFeature.tickPendingSpawns(level);
tickInvertedPlayers(level, activeThisTick); tickInvertedPlayers(level, activeThisTick);
tickDuctTapePlayers(level);
tickIchorPlayers(level); tickIchorPlayers(level);
tickBloodglassRecharge(level); tickBloodglassRecharge(level);
AttitudeAdjusterSlamManager.tick(level); AttitudeAdjusterSlamManager.tick(level);
@@ -338,14 +334,6 @@ public final class AntarchyFabricEvents {
} }
} }
private static void tickDuctTapePlayers(ServerLevel level) {
for (Player player : level.players()) {
if (DuctTapeBlock.shouldTickStuckEntity(player)) {
DuctTapeBlock.tickStuckEntity(player);
}
}
}
private static void tickIchorPlayers(ServerLevel level) { private static void tickIchorPlayers(ServerLevel level) {
if (!AntarchySettings.ichorWitherEnabled()) { if (!AntarchySettings.ichorWitherEnabled()) {
return; return;
@@ -625,15 +625,14 @@ public final class AntarchyFabricClientBootstrap {
DorrieJumpHudRenderer.render(guiGraphics); DorrieJumpHudRenderer.render(guiGraphics);
HerculesBeetleChargeHudRenderer.render(guiGraphics); HerculesBeetleChargeHudRenderer.render(guiGraphics);
com.craisinlord.antarchy.content.client.KingJudgmentFlashHudRenderer.render(guiGraphics); com.craisinlord.antarchy.content.client.KingJudgmentFlashHudRenderer.render(guiGraphics);
com.craisinlord.antarchy.content.client.PortalGunCrosshairRenderer.render(guiGraphics);
}); });
WorldRenderEvents.LAST.register(context -> { WorldRenderEvents.LAST.register(context -> {
BloodCrystalKatanaTrailHandler.render(context); BloodCrystalKatanaTrailHandler.render(context);
ScorpionWhipTetherRenderHandler.render(context); ScorpionWhipTetherRenderHandler.render(context);
WormHookTetherRenderHandler.render(context); WormHookTetherRenderHandler.render(context);
if (com.craisinlord.antarchy.content.client.renderer.PortalGunPortalViewRenderer.isEnabled()) { com.craisinlord.antarchy.content.client.renderer.PortalGunPortalViewRenderer.render(context.camera(), context.positionMatrix(), context.projectionMatrix(), context.tickCounter());
com.craisinlord.antarchy.content.client.renderer.PortalGunPortalViewRenderer.render(context.camera(), context.positionMatrix(), context.projectionMatrix(), context.tickCounter());
}
}); });
} }
@@ -51,14 +51,8 @@ public final class AntarchyKeyBindings {
); );
public static final KeyMapping PORTAL_GUN_ZOOM = new KeyMapping( public static final KeyMapping PORTAL_GUN_ZOOM = new KeyMapping(
"key.antarchy.portal_gun_zoom", "key.antarchy.portal_gun_zoom",
InputConstants.Type.MOUSE,
GLFW.GLFW_MOUSE_BUTTON_MIDDLE,
CATEGORY
);
public static final KeyMapping PORTAL_GUN_GRAB = new KeyMapping(
"key.antarchy.portal_gun_grab",
InputConstants.Type.KEYSYM, InputConstants.Type.KEYSYM,
GLFW.GLFW_KEY_G, GLFW.GLFW_KEY_Z,
CATEGORY CATEGORY
); );
public static final KeyMapping PORTAL_GUN_RESET = new KeyMapping( public static final KeyMapping PORTAL_GUN_RESET = new KeyMapping(
@@ -78,7 +72,6 @@ public final class AntarchyKeyBindings {
KeyBindingHelper.registerKeyBinding(ROYAL_INVERSION_TOGGLE); KeyBindingHelper.registerKeyBinding(ROYAL_INVERSION_TOGGLE);
KeyBindingHelper.registerKeyBinding(ROYAL_BOUNDARY); KeyBindingHelper.registerKeyBinding(ROYAL_BOUNDARY);
KeyBindingHelper.registerKeyBinding(PORTAL_GUN_ZOOM); KeyBindingHelper.registerKeyBinding(PORTAL_GUN_ZOOM);
KeyBindingHelper.registerKeyBinding(PORTAL_GUN_GRAB);
KeyBindingHelper.registerKeyBinding(PORTAL_GUN_RESET); KeyBindingHelper.registerKeyBinding(PORTAL_GUN_RESET);
} }
@@ -110,10 +103,6 @@ public final class AntarchyKeyBindings {
return Minecraft.getInstance().screen == null && PORTAL_GUN_ZOOM.consumeClick(); return Minecraft.getInstance().screen == null && PORTAL_GUN_ZOOM.consumeClick();
} }
public static boolean consumePortalGunGrabPressed() {
return Minecraft.getInstance().screen == null && PORTAL_GUN_GRAB.consumeClick();
}
public static boolean consumePortalGunResetPressed() { public static boolean consumePortalGunResetPressed() {
return Minecraft.getInstance().screen == null && PORTAL_GUN_RESET.consumeClick(); return Minecraft.getInstance().screen == null && PORTAL_GUN_RESET.consumeClick();
} }
@@ -27,14 +27,12 @@ public final class DorrieJumpClientHandler {
return; return;
} }
// Space bar → upward movement.
boolean pressingJump = mc.options.keyJump.isDown(); boolean pressingJump = mc.options.keyJump.isDown();
if (pressingJump != wasPressingJump) { if (pressingJump != wasPressingJump) {
ClientPlayNetworking.send(new DorrieJumpInputPayload(pressingJump)); ClientPlayNetworking.send(new DorrieJumpInputPayload(pressingJump));
wasPressingJump = pressingJump; wasPressingJump = pressingJump;
} }
// Left ctrl → charge jump.
boolean pressingCharge = AntarchyKeyBindings.isMountSpecialPressed(); boolean pressingCharge = AntarchyKeyBindings.isMountSpecialPressed();
if (pressingCharge != wasPressingCharge) { if (pressingCharge != wasPressingCharge) {
ClientPlayNetworking.send(new DorrieChargeJumpPayload(pressingCharge)); ClientPlayNetworking.send(new DorrieChargeJumpPayload(pressingCharge));
@@ -3,7 +3,6 @@ package com.craisinlord.antarchy.fabric.client;
import com.craisinlord.antarchy.content.item.PortalGunItem; import com.craisinlord.antarchy.content.item.PortalGunItem;
import com.craisinlord.antarchy.content.client.PortalGunZoomClientState; import com.craisinlord.antarchy.content.client.PortalGunZoomClientState;
import com.craisinlord.antarchy.content.network.PortalGunPrimaryPayload; import com.craisinlord.antarchy.content.network.PortalGunPrimaryPayload;
import com.craisinlord.antarchy.content.network.PortalGunGrabPayload;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking; import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
@@ -20,23 +19,19 @@ public final class PortalGunClientHandler {
private static void tickMouse(Minecraft mc) { private static void tickMouse(Minecraft mc) {
if (mc.player == null) { if (mc.player == null) {
com.craisinlord.antarchy.content.client.PortalGunGrabClientState.clear();
com.craisinlord.antarchy.content.client.PortalGunResetClientState.clear(); com.craisinlord.antarchy.content.client.PortalGunResetClientState.clear();
} }
boolean hasPortalGun = mc.player != null && (mc.player.getMainHandItem().getItem() instanceof PortalGunItem || mc.player.getOffhandItem().getItem() instanceof PortalGunItem); boolean hasPortalGun = mc.player != null && (mc.player.getMainHandItem().getItem() instanceof PortalGunItem || mc.player.getOffhandItem().getItem() instanceof PortalGunItem);
boolean gameplayInput = mc.screen == null; boolean gameplayInput = mc.screen == null;
boolean held = hasPortalGun && gameplayInput; boolean held = hasPortalGun && gameplayInput;
boolean zoomPressed = AntarchyKeyBindings.consumePortalGunZoomPressed(); boolean zoomPressed = AntarchyKeyBindings.consumePortalGunZoomPressed();
PortalGunZoomClientState.tick(mc, hasPortalGun, zoomPressed && gameplayInput); PortalGunZoomClientState.tick(mc, held, zoomPressed && gameplayInput);
boolean resetActive = com.craisinlord.antarchy.content.client.PortalGunResetClientState.tick( boolean resetActive = com.craisinlord.antarchy.content.client.PortalGunResetClientState.tick(
mc, mc,
held, held,
AntarchyKeyBindings.isPortalGunResetDown(), AntarchyKeyBindings.isPortalGunResetDown(),
ClientPlayNetworking::send ClientPlayNetworking::send
); );
if (held && AntarchyKeyBindings.consumePortalGunGrabPressed()) {
ClientPlayNetworking.send(new PortalGunGrabPayload());
}
if (mc.player == null || mc.level == null || mc.screen != null) { if (mc.player == null || mc.level == null || mc.screen != null) {
lastAttackDown = false; lastAttackDown = false;
return; return;
@@ -20,6 +20,8 @@ public final class SeparateLargeItemModels {
entry(AntarchyFabricItems.GROWTH_RAY.get(), "grow_ray_gui"), entry(AntarchyFabricItems.GROWTH_RAY.get(), "grow_ray_gui"),
entry(AntarchyFabricItems.KRAKENS_GRASP.get(), "krakens_grasp_gui"), entry(AntarchyFabricItems.KRAKENS_GRASP.get(), "krakens_grasp_gui"),
entry(AntarchyFabricItems.PORTAL_GUN.get(), "portal_gun_gui"), entry(AntarchyFabricItems.PORTAL_GUN.get(), "portal_gun_gui"),
entry(AntarchyFabricItems.PORTAL_GUN_ATLAS.get(), "portal_gun_atlas_gui"),
entry(AntarchyFabricItems.PORTAL_GUN_P_BODY.get(), "portal_gun_p_body_gui"),
entry(AntarchyFabricItems.ROYAL_ASSAILANT_BATTLEAXE.get(), "royal_assailant_battleaxe_gui"), entry(AntarchyFabricItems.ROYAL_ASSAILANT_BATTLEAXE.get(), "royal_assailant_battleaxe_gui"),
entry(AntarchyFabricItems.ROYAL_ASSAILANT_STAFF.get(), "royal_assailant_staff_gui"), entry(AntarchyFabricItems.ROYAL_ASSAILANT_STAFF.get(), "royal_assailant_staff_gui"),
entry(AntarchyFabricItems.ROYAL_GUARDIAN_SWORD.get(), "royal_guardian_sword_gui"), entry(AntarchyFabricItems.ROYAL_GUARDIAN_SWORD.get(), "royal_guardian_sword_gui"),
@@ -1,30 +0,0 @@
package com.craisinlord.antarchy.fabric.mixin.client;
import com.craisinlord.antarchy.content.block.DuctTapeBlock;
import net.minecraft.client.Minecraft;
import net.minecraft.client.player.Input;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(Input.class)
public abstract class DuctTapeInputMixin {
@Inject(method = "tick", at = @At("RETURN"))
private void blockInputInDuctTape(boolean isSneaking, float slowFactor, CallbackInfo ci) {
Minecraft mc = Minecraft.getInstance();
if (mc.player == null || mc.level == null) return;
if (!mc.player.isNoGravity() && !DuctTapeBlock.isTouchingTape(mc.level, mc.player)) return;
Input self = (Input) (Object) this;
self.left = false;
self.right = false;
self.up = false;
self.down = false;
self.leftImpulse = 0.0F;
self.forwardImpulse = 0.0F;
self.jumping = false;
self.shiftKeyDown = false;
}
}
@@ -0,0 +1,28 @@
package com.craisinlord.antarchy.fabric.mixin.client;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import com.mojang.blaze3d.pipeline.RenderTarget;
import java.nio.IntBuffer;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL30;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
@Mixin(RenderTarget.class)
public abstract class RenderTargetDepthStencilMixin {
@WrapOperation(method = "createBuffers", at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/platform/GlStateManager;_texImage2D(IIIIIIIILjava/nio/IntBuffer;)V"))
private void antarchy$allocateDepthStencil(int target, int level, int internalFormat, int width, int height, int border, int format, int type,
IntBuffer pixels, Operation<Void> original) {
if (internalFormat == GL11.GL_DEPTH_COMPONENT) {
original.call(target, level, GL30.GL_DEPTH24_STENCIL8, width, height, border, GL30.GL_DEPTH_STENCIL, GL30.GL_UNSIGNED_INT_24_8, pixels);
return;
}
original.call(target, level, internalFormat, width, height, border, format, type, pixels);
}
@WrapOperation(method = "createBuffers", at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/platform/GlStateManager;_glFramebufferTexture2D(IIIII)V"))
private void antarchy$attachDepthStencil(int target, int attachment, int textureTarget, int texture, int level, Operation<Void> original) {
original.call(target, attachment == GL30.GL_DEPTH_ATTACHMENT ? GL30.GL_DEPTH_STENCIL_ATTACHMENT : attachment, textureTarget, texture, level);
}
}
@@ -45,8 +45,6 @@ public final class AntarchyFabricClientNetworking {
com.craisinlord.antarchy.content.client.PortalGunIndicatorClientState.setRequestSender(ClientPlayNetworking::send); com.craisinlord.antarchy.content.client.PortalGunIndicatorClientState.setRequestSender(ClientPlayNetworking::send);
ClientPlayNetworking.registerGlobalReceiver(PortalGunIndicatorPayload.TYPE, (payload, context) -> ClientPlayNetworking.registerGlobalReceiver(PortalGunIndicatorPayload.TYPE, (payload, context) ->
context.client().execute(() -> com.craisinlord.antarchy.content.client.PortalGunIndicatorClientState.update(payload))); context.client().execute(() -> com.craisinlord.antarchy.content.client.PortalGunIndicatorClientState.update(payload)));
ClientPlayNetworking.registerGlobalReceiver(PortalGunGrabStatePayload.TYPE, (payload, context) ->
context.client().execute(() -> com.craisinlord.antarchy.content.client.PortalGunGrabClientState.update(payload.active())));
ClientPlayNetworking.registerGlobalReceiver(GravityStatePayload.TYPE, (payload, context) -> ClientPlayNetworking.registerGlobalReceiver(GravityStatePayload.TYPE, (payload, context) ->
context.client().execute(() -> AntarchyFabricNetworking.handleGravityState(context.player(), payload))); context.client().execute(() -> AntarchyFabricNetworking.handleGravityState(context.player(), payload)));
ClientPlayNetworking.registerGlobalReceiver(BloodglassStatePayload.TYPE, (payload, context) -> ClientPlayNetworking.registerGlobalReceiver(BloodglassStatePayload.TYPE, (payload, context) ->
@@ -50,7 +50,6 @@ public final class AntarchyFabricNetworking {
registerServerReceivers(); registerServerReceivers();
com.craisinlord.antarchy.content.network.AntarchyGameNetworkHandler.setResultSender(ServerPlayNetworking::send); com.craisinlord.antarchy.content.network.AntarchyGameNetworkHandler.setResultSender(ServerPlayNetworking::send);
com.craisinlord.antarchy.content.portalgun.PortalGunIndicatorSync.setSender(ServerPlayNetworking::send); com.craisinlord.antarchy.content.portalgun.PortalGunIndicatorSync.setSender(ServerPlayNetworking::send);
com.craisinlord.antarchy.content.portalgun.PortalGunGrabManager.setStateSender(ServerPlayNetworking::send);
} }
public static void bootstrapMultipartCommon() { public static void bootstrapMultipartCommon() {
@@ -76,7 +75,6 @@ public final class AntarchyFabricNetworking {
private static void registerPayloadTypes() { private static void registerPayloadTypes() {
PayloadTypeRegistry.playS2C().register(AntarchyGameResultPayload.TYPE, AntarchyGameResultPayload.STREAM_CODEC); PayloadTypeRegistry.playS2C().register(AntarchyGameResultPayload.TYPE, AntarchyGameResultPayload.STREAM_CODEC);
PayloadTypeRegistry.playS2C().register(PortalGunIndicatorPayload.TYPE, PortalGunIndicatorPayload.STREAM_CODEC); PayloadTypeRegistry.playS2C().register(PortalGunIndicatorPayload.TYPE, PortalGunIndicatorPayload.STREAM_CODEC);
PayloadTypeRegistry.playS2C().register(PortalGunGrabStatePayload.TYPE, PortalGunGrabStatePayload.STREAM_CODEC);
PayloadTypeRegistry.playS2C().register(GravityStatePayload.TYPE, GravityStatePayload.STREAM_CODEC); PayloadTypeRegistry.playS2C().register(GravityStatePayload.TYPE, GravityStatePayload.STREAM_CODEC);
PayloadTypeRegistry.playS2C().register(BloodglassStatePayload.TYPE, BloodglassStatePayload.STREAM_CODEC); PayloadTypeRegistry.playS2C().register(BloodglassStatePayload.TYPE, BloodglassStatePayload.STREAM_CODEC);
PayloadTypeRegistry.playS2C().register(TigerEyeCamouflageStatePayload.TYPE, TigerEyeCamouflageStatePayload.STREAM_CODEC); PayloadTypeRegistry.playS2C().register(TigerEyeCamouflageStatePayload.TYPE, TigerEyeCamouflageStatePayload.STREAM_CODEC);
@@ -92,8 +90,8 @@ public final class AntarchyFabricNetworking {
PayloadTypeRegistry.playC2S().register(GravityGunPrimaryPayload.TYPE, GravityGunPrimaryPayload.STREAM_CODEC); PayloadTypeRegistry.playC2S().register(GravityGunPrimaryPayload.TYPE, GravityGunPrimaryPayload.STREAM_CODEC);
PayloadTypeRegistry.playC2S().register(PortalGunPrimaryPayload.TYPE, PortalGunPrimaryPayload.STREAM_CODEC); PayloadTypeRegistry.playC2S().register(PortalGunPrimaryPayload.TYPE, PortalGunPrimaryPayload.STREAM_CODEC);
PayloadTypeRegistry.playC2S().register(com.craisinlord.antarchy.content.network.PortalGunTransitPayload.TYPE, com.craisinlord.antarchy.content.network.PortalGunTransitPayload.STREAM_CODEC);
PayloadTypeRegistry.playC2S().register(PortalGunIndicatorRequestPayload.TYPE, PortalGunIndicatorRequestPayload.STREAM_CODEC); PayloadTypeRegistry.playC2S().register(PortalGunIndicatorRequestPayload.TYPE, PortalGunIndicatorRequestPayload.STREAM_CODEC);
PayloadTypeRegistry.playC2S().register(PortalGunGrabPayload.TYPE, PortalGunGrabPayload.STREAM_CODEC);
PayloadTypeRegistry.playC2S().register(PortalGunResetPayload.TYPE, PortalGunResetPayload.STREAM_CODEC); PayloadTypeRegistry.playC2S().register(PortalGunResetPayload.TYPE, PortalGunResetPayload.STREAM_CODEC);
PayloadTypeRegistry.playC2S().register(EyeOfStormPrimaryPayload.TYPE, EyeOfStormPrimaryPayload.STREAM_CODEC); PayloadTypeRegistry.playC2S().register(EyeOfStormPrimaryPayload.TYPE, EyeOfStormPrimaryPayload.STREAM_CODEC);
PayloadTypeRegistry.playC2S().register(OpenGiantFryingPanPayload.TYPE, OpenGiantFryingPanPayload.STREAM_CODEC); PayloadTypeRegistry.playC2S().register(OpenGiantFryingPanPayload.TYPE, OpenGiantFryingPanPayload.STREAM_CODEC);
@@ -128,10 +126,15 @@ public final class AntarchyFabricNetworking {
context.server().execute(() -> handleGravityGunPrimary(context.player(), payload))); context.server().execute(() -> handleGravityGunPrimary(context.player(), payload)));
ServerPlayNetworking.registerGlobalReceiver(PortalGunPrimaryPayload.TYPE, (payload, context) -> ServerPlayNetworking.registerGlobalReceiver(PortalGunPrimaryPayload.TYPE, (payload, context) ->
context.server().execute(() -> handlePortalGunPrimary(context.player(), payload))); context.server().execute(() -> handlePortalGunPrimary(context.player(), payload)));
ServerPlayNetworking.registerGlobalReceiver(com.craisinlord.antarchy.content.network.PortalGunTransitPayload.TYPE, (payload, context) -> {
if (context.server().isSameThread()) {
com.craisinlord.antarchy.content.portalgun.PortalGunTransitHandler.handle(context.player(), payload);
} else {
context.server().execute(() -> com.craisinlord.antarchy.content.portalgun.PortalGunTransitHandler.handle(context.player(), payload));
}
});
ServerPlayNetworking.registerGlobalReceiver(PortalGunIndicatorRequestPayload.TYPE, (payload, context) -> ServerPlayNetworking.registerGlobalReceiver(PortalGunIndicatorRequestPayload.TYPE, (payload, context) ->
context.server().execute(() -> com.craisinlord.antarchy.content.portalgun.PortalGunIndicatorSync.handleRequest(context.player(), payload))); context.server().execute(() -> com.craisinlord.antarchy.content.portalgun.PortalGunIndicatorSync.handleRequest(context.player(), payload)));
ServerPlayNetworking.registerGlobalReceiver(PortalGunGrabPayload.TYPE, (payload, context) ->
context.server().execute(() -> com.craisinlord.antarchy.content.portalgun.PortalGunGrabManager.toggle(context.player())));
ServerPlayNetworking.registerGlobalReceiver(PortalGunResetPayload.TYPE, (payload, context) -> ServerPlayNetworking.registerGlobalReceiver(PortalGunResetPayload.TYPE, (payload, context) ->
context.server().execute(() -> com.craisinlord.antarchy.content.portalgun.PortalGunResetManager.handleInput(context.player(), payload.action()))); context.server().execute(() -> com.craisinlord.antarchy.content.portalgun.PortalGunResetManager.handleInput(context.player(), payload.action())));
ServerPlayNetworking.registerGlobalReceiver(EyeOfStormPrimaryPayload.TYPE, (payload, context) -> ServerPlayNetworking.registerGlobalReceiver(EyeOfStormPrimaryPayload.TYPE, (payload, context) ->
@@ -1561,7 +1561,28 @@ public final class AntarchyFabricItems {
AntarchyFabricEntities.PORTAL_GUN_BLACK_HOLE, AntarchyFabricEntities.PORTAL_GUN_BLACK_HOLE,
AntarchyFabricEntities.PORTAL_GUN_PROJECTILE, AntarchyFabricEntities.PORTAL_GUN_PROJECTILE,
AntarchyFabricBlocks.PORTAL_GUN_PORTAL_MASTER, AntarchyFabricBlocks.PORTAL_GUN_PORTAL_MASTER,
AntarchyFabricBlocks.PORTAL_GUN_PORTAL_BASE AntarchyFabricBlocks.PORTAL_GUN_PORTAL_BASE,
com.craisinlord.antarchy.content.portalgun.PortalGunVariant.DEFAULT
));
public static final DeferredItem<PortalGunItem> PORTAL_GUN_ATLAS = ITEMS.register("portal_gun_atlas",
() -> new PortalGunItem(
new Item.Properties().stacksTo(1).durability(256).rarity(Rarity.RARE),
AntarchyFabricEntities.PORTAL_GUN_PORTAL,
AntarchyFabricEntities.PORTAL_GUN_BLACK_HOLE,
AntarchyFabricEntities.PORTAL_GUN_PROJECTILE,
AntarchyFabricBlocks.PORTAL_GUN_PORTAL_MASTER,
AntarchyFabricBlocks.PORTAL_GUN_PORTAL_BASE,
com.craisinlord.antarchy.content.portalgun.PortalGunVariant.ATLAS
));
public static final DeferredItem<PortalGunItem> PORTAL_GUN_P_BODY = ITEMS.register("portal_gun_p_body",
() -> new PortalGunItem(
new Item.Properties().stacksTo(1).durability(256).rarity(Rarity.RARE),
AntarchyFabricEntities.PORTAL_GUN_PORTAL,
AntarchyFabricEntities.PORTAL_GUN_BLACK_HOLE,
AntarchyFabricEntities.PORTAL_GUN_PROJECTILE,
AntarchyFabricBlocks.PORTAL_GUN_PORTAL_MASTER,
AntarchyFabricBlocks.PORTAL_GUN_PORTAL_BASE,
com.craisinlord.antarchy.content.portalgun.PortalGunVariant.P_BODY
)); ));
public static final DeferredItem<EyeOfTheStormItem> EYE_OF_THE_STORM = ITEMS.register("eye_of_the_storm", public static final DeferredItem<EyeOfTheStormItem> EYE_OF_THE_STORM = ITEMS.register("eye_of_the_storm",
@@ -27,12 +27,12 @@
"infinity.AmendmentRareOresMixin" "infinity.AmendmentRareOresMixin"
], ],
"client": [ "client": [
"client.RenderTargetDepthStencilMixin",
"client.SeparateLargeItemModelsMixin", "client.SeparateLargeItemModelsMixin",
"client.SeparateLargeItemGuiLightingMixin", "client.SeparateLargeItemGuiLightingMixin",
"client.CameraShakeMixin", "client.CameraShakeMixin",
"client.BrutalflyElytraExperienceBarMixin", "client.BrutalflyElytraExperienceBarMixin",
"client.BloodglassChatOrderMixin", "client.BloodglassChatOrderMixin",
"client.DuctTapeInputMixin",
"client.LiquidFogMixin", "client.LiquidFogMixin",
"client.GravityGunMouseScrollMixin", "client.GravityGunMouseScrollMixin",
"client.KeybindingConflictFixMixin", "client.KeybindingConflictFixMixin",
@@ -0,0 +1,3 @@
{
"parent": "antarchy:item/portal_gun_held_geo"
}
@@ -0,0 +1,3 @@
{
"parent": "antarchy:item/portal_gun_held_geo"
}
View File
Whitespace-only changes.
@@ -671,8 +671,9 @@ public final class AntarchyConfigModuleNeoforge {
AntarchySettings.setPortalGunSeeThroughPortals(AntarchyMiscConfig.portalGunSeeThroughPortals()); AntarchySettings.setPortalGunSeeThroughPortals(AntarchyMiscConfig.portalGunSeeThroughPortals());
AntarchySettings.setPortalGunFancyPortals(AntarchyMiscConfig.portalGunFancyPortals()); AntarchySettings.setPortalGunFancyPortals(AntarchyMiscConfig.portalGunFancyPortals());
AntarchySettings.setPortalGunMaxShootDistance(AntarchyMiscConfig.portalGunMaxShootDistance()); AntarchySettings.setPortalGunMaxShootDistance(AntarchyMiscConfig.portalGunMaxShootDistance());
AntarchySettings.setPortalGunEntityGrabWeightBase(AntarchyMiscConfig.portalGunEntityGrabWeightBase()); AntarchySettings.setPortalGunMinFloorExitSpeed(AntarchyMiscConfig.portalGunMinFloorExitSpeed());
AntarchySettings.setPortalGunIndicatorSize(AntarchyMiscConfig.portalGunIndicatorSize()); AntarchySettings.setPortalGunMaxLiveViews(AntarchyMiscConfig.portalGunMaxLiveViews());
AntarchySettings.setPortalGunMaxExitSpeed(AntarchyMiscConfig.portalGunMaxExitSpeed());
// Misc // Misc
@@ -39,8 +39,9 @@ public final class AntarchyMiscConfig {
private static final ModConfigSpec.BooleanValue PORTAL_GUN_SEE_THROUGH_PORTALS; private static final ModConfigSpec.BooleanValue PORTAL_GUN_SEE_THROUGH_PORTALS;
private static final ModConfigSpec.BooleanValue PORTAL_GUN_FANCY_PORTALS; private static final ModConfigSpec.BooleanValue PORTAL_GUN_FANCY_PORTALS;
private static final ModConfigSpec.IntValue PORTAL_GUN_MAX_SHOOT_DISTANCE; private static final ModConfigSpec.IntValue PORTAL_GUN_MAX_SHOOT_DISTANCE;
private static final ModConfigSpec.IntValue PORTAL_GUN_ENTITY_GRAB_WEIGHT_BASE; private static final ModConfigSpec.DoubleValue PORTAL_GUN_MIN_FLOOR_EXIT_SPEED;
private static final ModConfigSpec.IntValue PORTAL_GUN_INDICATOR_SIZE; private static final ModConfigSpec.IntValue PORTAL_GUN_MAX_LIVE_VIEWS;
private static final ModConfigSpec.DoubleValue PORTAL_GUN_MAX_EXIT_SPEED;
// Diamond Minecart // Diamond Minecart
private static final ModConfigSpec.BooleanValue DIAMOND_MINECART_ENABLED; private static final ModConfigSpec.BooleanValue DIAMOND_MINECART_ENABLED;
@@ -146,8 +147,9 @@ public final class AntarchyMiscConfig {
PORTAL_GUN_SEE_THROUGH_PORTALS = b.define("seeThroughPortals", true); PORTAL_GUN_SEE_THROUGH_PORTALS = b.define("seeThroughPortals", true);
PORTAL_GUN_FANCY_PORTALS = b.define("fancyPortals", true); PORTAL_GUN_FANCY_PORTALS = b.define("fancyPortals", true);
PORTAL_GUN_MAX_SHOOT_DISTANCE = b.defineInRange("maxShootDistance", 10000, 1, Integer.MAX_VALUE); PORTAL_GUN_MAX_SHOOT_DISTANCE = b.defineInRange("maxShootDistance", 10000, 1, Integer.MAX_VALUE);
PORTAL_GUN_ENTITY_GRAB_WEIGHT_BASE = b.defineInRange("entityGrabWeightBase", 100, 0, 1000); PORTAL_GUN_MAX_LIVE_VIEWS = b.comment("How many portals can show a live see-through view at once. Each one re-renders the world, so higher values cost frame rate. 0 shows only the animated surface.").defineInRange("maxLiveViews", 8, 0, 32);
PORTAL_GUN_INDICATOR_SIZE = b.defineInRange("indicatorSize", 30, 0, 100); PORTAL_GUN_MIN_FLOOR_EXIT_SPEED = b.comment("Minimum speed in blocks/tick when leaving an upward-facing portal, so the traveller clears it. 0 disables.").defineInRange("minFloorExitSpeed", 0.6D, 0.0D, 4.0D);
PORTAL_GUN_MAX_EXIT_SPEED = b.comment("Maximum speed in blocks/tick when leaving a portal. 0 keeps momentum uncapped.").defineInRange("maxExitSpeed", 0.0D, 0.0D, 100.0D);
b.pop(); b.pop();
@@ -224,8 +226,9 @@ public final class AntarchyMiscConfig {
public static boolean portalGunSeeThroughPortals() { return PORTAL_GUN_SEE_THROUGH_PORTALS.get(); } public static boolean portalGunSeeThroughPortals() { return PORTAL_GUN_SEE_THROUGH_PORTALS.get(); }
public static boolean portalGunFancyPortals() { return PORTAL_GUN_FANCY_PORTALS.get(); } public static boolean portalGunFancyPortals() { return PORTAL_GUN_FANCY_PORTALS.get(); }
public static int portalGunMaxShootDistance() { return PORTAL_GUN_MAX_SHOOT_DISTANCE.get(); } public static int portalGunMaxShootDistance() { return PORTAL_GUN_MAX_SHOOT_DISTANCE.get(); }
public static int portalGunEntityGrabWeightBase() { return PORTAL_GUN_ENTITY_GRAB_WEIGHT_BASE.get(); } public static double portalGunMinFloorExitSpeed() { return PORTAL_GUN_MIN_FLOOR_EXIT_SPEED.get(); }
public static int portalGunIndicatorSize() { return PORTAL_GUN_INDICATOR_SIZE.get(); } public static int portalGunMaxLiveViews() { return PORTAL_GUN_MAX_LIVE_VIEWS.get(); }
public static double portalGunMaxExitSpeed() { return PORTAL_GUN_MAX_EXIT_SPEED.get(); }
static boolean diamondMinecartEnabled() { return DIAMOND_MINECART_ENABLED.get(); } static boolean diamondMinecartEnabled() { return DIAMOND_MINECART_ENABLED.get(); }
static boolean diamondMinecartPlacesRails() { return DIAMOND_MINECART_PLACES_RAILS.get(); } static boolean diamondMinecartPlacesRails() { return DIAMOND_MINECART_PLACES_RAILS.get(); }
@@ -65,13 +65,6 @@ public final class AntarchyKeyBindings {
InputConstants.MOUSE_BUTTON_MIDDLE, InputConstants.MOUSE_BUTTON_MIDDLE,
CATEGORY CATEGORY
); );
public static final KeyMapping PORTAL_GUN_GRAB = new KeyMapping(
"key.antarchy.portal_gun_grab",
KeyConflictContext.IN_GAME,
InputConstants.Type.KEYSYM,
InputConstants.KEY_G,
CATEGORY
);
public static final KeyMapping PORTAL_GUN_RESET = new KeyMapping( public static final KeyMapping PORTAL_GUN_RESET = new KeyMapping(
"key.antarchy.portal_gun_reset", "key.antarchy.portal_gun_reset",
KeyConflictContext.IN_GAME, KeyConflictContext.IN_GAME,
@@ -91,7 +84,6 @@ public final class AntarchyKeyBindings {
event.register(ROYAL_INVERSION_TOGGLE); event.register(ROYAL_INVERSION_TOGGLE);
event.register(ROYAL_BOUNDARY); event.register(ROYAL_BOUNDARY);
event.register(PORTAL_GUN_ZOOM); event.register(PORTAL_GUN_ZOOM);
event.register(PORTAL_GUN_GRAB);
event.register(PORTAL_GUN_RESET); event.register(PORTAL_GUN_RESET);
} }
@@ -119,10 +111,6 @@ public final class AntarchyKeyBindings {
return PORTAL_GUN_ZOOM.consumeClick(); return PORTAL_GUN_ZOOM.consumeClick();
} }
public static boolean consumePortalGunGrabPressed() {
return PORTAL_GUN_GRAB.consumeClick();
}
public static boolean consumePortalGunResetPressed() { public static boolean consumePortalGunResetPressed() {
return PORTAL_GUN_RESET.consumeClick(); return PORTAL_GUN_RESET.consumeClick();
} }
Loaded 100 of 111 files, more files were not shown because too many files have changed in this diff. Show more