portal gun fixes and variants
No files matched your search
@@ -143,8 +143,9 @@ public final class AntarchyConfigCatalog {
|
||||
|| name.equals("portalGunCanPortalsResizeWhenCreated")
|
||||
|| name.equals("portalGunCanPortalProjectilesChunkload")
|
||||
|| name.equals("portalGunMaxShootDistance")
|
||||
|| name.equals("portalGunEntityGrabWeightBase")
|
||||
|| name.equals("portalGunIndicatorSize")
|
||||
|| name.equals("portalGunMinFloorExitSpeed")
|
||||
|| name.equals("portalGunMaxLiveViews")
|
||||
|| name.equals("portalGunMaxExitSpeed")
|
||||
|| name.equals("hushweedSporeLifetimeSeconds")
|
||||
|| name.equals("elythiaFireflyParticlesEnabled")
|
||||
|| name.equals("duplicatorTreeEnabled")
|
||||
|
||||
@@ -555,9 +555,10 @@ public final class AntarchySettings {
|
||||
private static boolean portalGunCanPortalProjectilesChunkload = true;
|
||||
private static boolean portalGunSeeThroughPortals = true;
|
||||
private static boolean portalGunFancyPortals = true;
|
||||
private static int portalGunIndicatorSize = 30;
|
||||
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 int bloodCrystalArmorShieldRechargeTicks = 600;
|
||||
private static int bloodCrystalAppleShieldCount = 2;
|
||||
@@ -1335,16 +1336,20 @@ public final class AntarchySettings {
|
||||
portalGunFancyPortals = value;
|
||||
}
|
||||
|
||||
public static void setPortalGunIndicatorSize(int value) {
|
||||
portalGunIndicatorSize = Math.max(0, Math.min(100, value));
|
||||
}
|
||||
|
||||
public static void setPortalGunMaxShootDistance(int value) {
|
||||
portalGunMaxShootDistance = Math.max(1, value);
|
||||
}
|
||||
|
||||
public static void setPortalGunEntityGrabWeightBase(int value) {
|
||||
portalGunEntityGrabWeightBase = Math.max(0, value);
|
||||
public static void setPortalGunMaxLiveViews(int value) {
|
||||
portalGunMaxLiveViews = Math.max(0, Math.min(32, value));
|
||||
}
|
||||
|
||||
public static void setPortalGunMinFloorExitSpeed(double value) {
|
||||
portalGunMinFloorExitSpeed = Math.max(0.0D, Math.min(4.0D, value));
|
||||
}
|
||||
|
||||
public static void setPortalGunMaxExitSpeed(double value) {
|
||||
portalGunMaxExitSpeed = Math.max(0.0D, Math.min(100.0D, value));
|
||||
}
|
||||
|
||||
public static boolean permanentPortalsEnabled() { return permanentPortalsEnabled; }
|
||||
@@ -1358,9 +1363,10 @@ public final class AntarchySettings {
|
||||
public static boolean portalGunCanPortalProjectilesChunkload() { return portalGunCanPortalProjectilesChunkload; }
|
||||
public static boolean portalGunSeeThroughPortals() { return portalGunSeeThroughPortals; }
|
||||
public static boolean portalGunFancyPortals() { return portalGunFancyPortals; }
|
||||
public static int portalGunIndicatorSize() { return portalGunIndicatorSize; }
|
||||
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) {
|
||||
brownAntRequiresReagent = value;
|
||||
|
||||
@@ -6,23 +6,14 @@ import com.craisinlord.antarchy.content.AntarchyObjects;
|
||||
import com.craisinlord.antarchy.content.AntarchySoundEvents;
|
||||
import com.craisinlord.antarchy.content.AntarchyTags;
|
||||
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.Direction;
|
||||
import net.minecraft.sounds.SoundEvents;
|
||||
import net.minecraft.sounds.SoundSource;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.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.item.DiggerItem;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.ShearsItem;
|
||||
import net.minecraft.world.item.SwordItem;
|
||||
import net.minecraft.world.item.context.BlockPlaceContext;
|
||||
import net.minecraft.world.level.BlockGetter;
|
||||
import net.minecraft.world.level.Level;
|
||||
@@ -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.EnumProperty;
|
||||
import net.minecraft.world.level.block.state.properties.IntegerProperty;
|
||||
import net.minecraft.world.phys.AABB;
|
||||
import net.minecraft.world.phys.BlockHitResult;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import net.minecraft.world.phys.shapes.CollisionContext;
|
||||
import net.minecraft.world.phys.shapes.Shapes;
|
||||
import net.minecraft.world.phys.shapes.VoxelShape;
|
||||
@@ -54,24 +43,6 @@ public class DuctTapeBlock extends Block {
|
||||
public static final DirectionProperty FACING = BlockStateProperties.HORIZONTAL_FACING;
|
||||
|
||||
private static final int TAPE_HEIGHT = 2;
|
||||
private static final long STICK_SOUND_COOLDOWN_TICKS = 10L;
|
||||
private static final long TAPE_DECAY_INTERVAL_TICKS = 30L;
|
||||
private static final double FLOOR_TRIGGER_HEIGHT = 0.30D;
|
||||
private static final double WALL_TRIGGER_DEPTH = 0.25D;
|
||||
private static final double TRIGGER_PAD = 0.12D;
|
||||
private static final double WALL_Y_PAD = 0.12D;
|
||||
private static final double WALL_SIDE_PAD = 0.08D;
|
||||
private static final double WALL_INNER_INSET = 0.03D;
|
||||
private static final double TRIGGER_INFLATE_DEFAULT = 0.15D;
|
||||
private static final int SEARCH_INTERVAL_TICKS = 4;
|
||||
private static final double SEARCH_MOTION_THRESHOLD_SQR = 1.0E-8D;
|
||||
private static final double SEARCH_PAD_XZ = Math.max(WALL_TRIGGER_DEPTH, TRIGGER_PAD);
|
||||
private static final double SEARCH_PAD_Y = FLOOR_TRIGGER_HEIGHT;
|
||||
|
||||
private static final Map<UUID, Long> STICK_SOUND_TIMES = new HashMap<>();
|
||||
private static final Map<UUID, String> STICK_CONTACT_KEYS = new HashMap<>();
|
||||
private static final Map<String, Long> TAPE_DECAY_TIMES = new HashMap<>();
|
||||
private static final Map<UUID, StuckContact> STUCK_ENTITIES = new HashMap<>();
|
||||
|
||||
public DuctTapeBlock(BlockBehaviour.Properties properties) {
|
||||
super(properties);
|
||||
@@ -138,14 +109,6 @@ public class DuctTapeBlock extends Block {
|
||||
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
|
||||
protected ItemInteractionResult useItemOn(
|
||||
ItemStack stack,
|
||||
@@ -160,14 +123,6 @@ public class DuctTapeBlock extends Block {
|
||||
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)) {
|
||||
return ItemInteractionResult.PASS_TO_DEFAULT_BLOCK_INTERACTION;
|
||||
}
|
||||
@@ -194,24 +149,6 @@ public class DuctTapeBlock extends Block {
|
||||
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
|
||||
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
|
||||
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) {
|
||||
int remainingUses = state.getValue(USES) - 1;
|
||||
if (remainingUses > 0) {
|
||||
@@ -257,322 +188,6 @@ public class DuctTapeBlock extends Block {
|
||||
return;
|
||||
}
|
||||
|
||||
releaseEntitiesForTape(level, pos);
|
||||
TAPE_DECAY_TIMES.remove(tapeDecayKey(level, pos));
|
||||
level.removeBlock(pos, false);
|
||||
}
|
||||
|
||||
private static void removeTape(Level level, BlockPos pos, BlockState state) {
|
||||
releaseEntitiesForTape(level, pos);
|
||||
TAPE_DECAY_TIMES.remove(tapeDecayKey(level, pos));
|
||||
level.removeBlock(pos, false);
|
||||
}
|
||||
|
||||
private static void maybeConsumeTapeDecay(Level level, BlockPos pos, BlockState state) {
|
||||
long gameTime = level.getGameTime();
|
||||
String key = tapeDecayKey(level, pos);
|
||||
Long lastConsumed = TAPE_DECAY_TIMES.get(key);
|
||||
if (lastConsumed == null) {
|
||||
TAPE_DECAY_TIMES.put(key, gameTime);
|
||||
return;
|
||||
}
|
||||
|
||||
if (gameTime - lastConsumed < TAPE_DECAY_INTERVAL_TICKS) {
|
||||
return;
|
||||
}
|
||||
|
||||
TAPE_DECAY_TIMES.put(key, gameTime);
|
||||
consumeTapeUse(level, pos, state);
|
||||
}
|
||||
|
||||
public static void tickStuckEntity(Entity entity) {
|
||||
if (entity == null || entity.level().isClientSide() || isStickyBlacklist(entity)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!entity.isAlive() || entity.isRemoved()) {
|
||||
releaseEntity(entity);
|
||||
return;
|
||||
}
|
||||
|
||||
StuckContact contact = STUCK_ENTITIES.get(entity.getUUID());
|
||||
if (contact != null && isContactValid(entity.level(), contact)) {
|
||||
BlockPos pos = BlockPos.of(contact.blockPos());
|
||||
BlockState state = entity.level().getBlockState(pos);
|
||||
applyStick(state, pos, entity);
|
||||
if (!(entity instanceof ItemEntity)) {
|
||||
maybeConsumeTapeDecay(entity.level(), pos, state);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (contact != null) {
|
||||
releaseEntity(entity);
|
||||
}
|
||||
|
||||
if (!shouldSearchForTape(entity)) {
|
||||
return;
|
||||
}
|
||||
|
||||
TapeHit hit = findTouchedTape(entity.level(), entity);
|
||||
if (hit == null) {
|
||||
if (entity.isNoGravity()) {
|
||||
entity.setNoGravity(false);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
rememberStuckContact(entity.level(), hit.pos(), entity);
|
||||
applyStick(hit.state(), hit.pos(), entity);
|
||||
if (!(entity instanceof ItemEntity)) {
|
||||
maybeConsumeTapeDecay(entity.level(), hit.pos(), hit.state());
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isStuckToTape(Entity entity) {
|
||||
if (entity == null || entity.level().isClientSide() || isStickyBlacklist(entity)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
StuckContact contact = STUCK_ENTITIES.get(entity.getUUID());
|
||||
return contact != null && isContactValid(entity.level(), contact);
|
||||
}
|
||||
|
||||
public static boolean isTouchingTape(Entity entity) {
|
||||
return entity != null && isTouchingTape(entity.level(), entity);
|
||||
}
|
||||
|
||||
public static boolean isTouchingTape(Level level, Entity entity) {
|
||||
if (isStickyBlacklist(entity)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return findTouchedTape(level, entity) != null;
|
||||
}
|
||||
|
||||
public static boolean shouldTickStuckEntity(Entity entity) {
|
||||
if (entity == null || entity.level().isClientSide() || isStickyBlacklist(entity)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!entity.isAlive() || entity.isRemoved()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
StuckContact contact = STUCK_ENTITIES.get(entity.getUUID());
|
||||
if (contact != null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return shouldSearchForTape(entity);
|
||||
}
|
||||
|
||||
private static boolean isStickyBlacklist(Entity entity) {
|
||||
return entity != null && entity.getType().is(AntarchyTags.Entities.DUCT_TAPE_STICK_BLACKLIST);
|
||||
}
|
||||
|
||||
private static void applyStick(BlockState state, BlockPos pos, Entity entity) {
|
||||
entity.setDeltaMovement(Vec3.ZERO);
|
||||
entity.resetFallDistance();
|
||||
entity.hasImpulse = true;
|
||||
entity.setNoGravity(true);
|
||||
|
||||
Vec3 anchor = getTapeAnchor(state, pos, entity);
|
||||
entity.setPos(anchor.x, anchor.y, anchor.z);
|
||||
|
||||
if (state.getValue(FACE) == AttachFace.FLOOR) {
|
||||
entity.setOnGround(true);
|
||||
}
|
||||
}
|
||||
|
||||
private static void rememberStuckContact(Level level, BlockPos pos, Entity entity) {
|
||||
UUID entityId = entity.getUUID();
|
||||
String key = tapeKey(level, pos);
|
||||
StuckContact existing = STUCK_ENTITIES.get(entityId);
|
||||
if (existing != null && existing.tapeKey().equals(key)) {
|
||||
return;
|
||||
}
|
||||
|
||||
double offsetX = entity.getX() - pos.getX();
|
||||
double offsetY = entity.getY() - pos.getY();
|
||||
double offsetZ = entity.getZ() - pos.getZ();
|
||||
STUCK_ENTITIES.put(entityId, new StuckContact(
|
||||
key,
|
||||
level.dimension().location().toString(),
|
||||
pos.asLong(),
|
||||
clamp(offsetX, 0.05D, 0.95D),
|
||||
clamp(offsetY, 0.0D, 0.98D),
|
||||
clamp(offsetZ, 0.05D, 0.95D)
|
||||
));
|
||||
}
|
||||
|
||||
private static Vec3 getTapeAnchor(BlockState state, BlockPos pos, Entity entity) {
|
||||
StuckContact contact = STUCK_ENTITIES.get(entity.getUUID());
|
||||
double offsetX = contact != null ? contact.offsetX() : 0.5D;
|
||||
double offsetY = contact != null ? contact.offsetY() : 0.0D;
|
||||
double offsetZ = contact != null ? contact.offsetZ() : 0.5D;
|
||||
double wallOffset = entity instanceof Player ? 0.06D : -0.28D;
|
||||
double centerX = pos.getX() + 0.5D;
|
||||
double centerZ = pos.getZ() + 0.5D;
|
||||
|
||||
return switch (state.getValue(FACE)) {
|
||||
case FLOOR -> new Vec3(
|
||||
pos.getX() + offsetX,
|
||||
pos.getY() + 0.01D,
|
||||
pos.getZ() + offsetZ
|
||||
);
|
||||
case WALL -> switch (state.getValue(FACING)) {
|
||||
case NORTH -> new Vec3(centerX, pos.getY() + offsetY, pos.getZ() - wallOffset);
|
||||
case SOUTH -> new Vec3(centerX, pos.getY() + offsetY, pos.getZ() + 1.0D + wallOffset);
|
||||
case EAST -> new Vec3(pos.getX() + 1.0D + wallOffset, pos.getY() + offsetY, centerZ);
|
||||
case WEST -> new Vec3(pos.getX() - wallOffset, pos.getY() + offsetY, centerZ);
|
||||
default -> new Vec3(centerX, pos.getY() + offsetY, centerZ);
|
||||
};
|
||||
default -> new Vec3(pos.getX() + offsetX, pos.getY() + 0.01D, pos.getZ() + offsetZ);
|
||||
};
|
||||
}
|
||||
|
||||
private static boolean isContactValid(Level level, StuckContact contact) {
|
||||
if (!level.dimension().location().toString().equals(contact.dimensionId())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
BlockPos pos = BlockPos.of(contact.blockPos());
|
||||
BlockState state = level.getBlockState(pos);
|
||||
return state.getBlock() instanceof DuctTapeBlock
|
||||
&& tapeKey(level, pos).equals(contact.tapeKey());
|
||||
}
|
||||
|
||||
private static boolean shouldSearchForTape(Entity entity) {
|
||||
Vec3 velocity = entity.getDeltaMovement();
|
||||
if (velocity.lengthSqr() < SEARCH_MOTION_THRESHOLD_SQR) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (entity.tickCount + entity.getId()) % SEARCH_INTERVAL_TICKS == 0;
|
||||
}
|
||||
|
||||
private static TapeHit findTouchedTape(Level level, Entity entity) {
|
||||
AABB box = entity.getBoundingBox().inflate(SEARCH_PAD_XZ, SEARCH_PAD_Y, SEARCH_PAD_XZ);
|
||||
BlockPos min = BlockPos.containing(box.minX, box.minY, box.minZ);
|
||||
BlockPos max = BlockPos.containing(box.maxX, box.maxY, box.maxZ);
|
||||
if (!level.hasChunksAt(min, max)) {
|
||||
return null;
|
||||
}
|
||||
BlockPos.MutableBlockPos cursor = new BlockPos.MutableBlockPos();
|
||||
for (int x = min.getX(); x <= max.getX(); x++) {
|
||||
for (int y = min.getY(); y <= max.getY(); y++) {
|
||||
for (int z = min.getZ(); z <= max.getZ(); z++) {
|
||||
cursor.set(x, y, z);
|
||||
BlockState state = level.getBlockState(cursor);
|
||||
if (state.isAir()) {
|
||||
continue;
|
||||
}
|
||||
if (state.getBlock() instanceof DuctTapeBlock && touchesTape(level, entity, cursor, state)) {
|
||||
return new TapeHit(cursor.immutable(), state);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static boolean touchesTape(Level level, Entity entity, BlockPos pos, BlockState state) {
|
||||
return entity.getBoundingBox().intersects(getStickyTriggerBounds(state, pos));
|
||||
}
|
||||
|
||||
private static AABB getStickyTriggerBounds(BlockState state, BlockPos pos) {
|
||||
AABB bounds = getShapeFor(state).bounds().move(pos);
|
||||
return switch (state.getValue(FACE)) {
|
||||
case FLOOR -> new AABB(
|
||||
bounds.minX - TRIGGER_PAD,
|
||||
pos.getY() - 0.02D,
|
||||
bounds.minZ - TRIGGER_PAD,
|
||||
bounds.maxX + TRIGGER_PAD,
|
||||
pos.getY() + FLOOR_TRIGGER_HEIGHT,
|
||||
bounds.maxZ + TRIGGER_PAD
|
||||
);
|
||||
case WALL -> {
|
||||
yield switch (state.getValue(FACING)) {
|
||||
case NORTH -> new AABB(bounds.minX - WALL_SIDE_PAD, bounds.minY - WALL_Y_PAD, bounds.minZ - WALL_TRIGGER_DEPTH, bounds.maxX + WALL_SIDE_PAD, bounds.maxY + WALL_Y_PAD, bounds.maxZ + WALL_INNER_INSET);
|
||||
case SOUTH -> new AABB(bounds.minX - WALL_SIDE_PAD, bounds.minY - WALL_Y_PAD, bounds.minZ - WALL_INNER_INSET, bounds.maxX + WALL_SIDE_PAD, bounds.maxY + WALL_Y_PAD, bounds.maxZ + WALL_TRIGGER_DEPTH);
|
||||
case EAST -> new AABB(bounds.minX - WALL_TRIGGER_DEPTH, bounds.minY - WALL_Y_PAD, bounds.minZ - WALL_SIDE_PAD, bounds.maxX + WALL_INNER_INSET, bounds.maxY + WALL_Y_PAD, bounds.maxZ + WALL_SIDE_PAD);
|
||||
case WEST -> new AABB(bounds.minX - WALL_INNER_INSET, bounds.minY - WALL_Y_PAD, bounds.minZ - WALL_SIDE_PAD, bounds.maxX + WALL_TRIGGER_DEPTH, bounds.maxY + WALL_Y_PAD, bounds.maxZ + WALL_SIDE_PAD);
|
||||
default -> bounds.inflate(TRIGGER_INFLATE_DEFAULT);
|
||||
};
|
||||
}
|
||||
default -> bounds.inflate(TRIGGER_INFLATE_DEFAULT);
|
||||
};
|
||||
}
|
||||
|
||||
private static void releaseEntitiesForTape(Level level, BlockPos pos) {
|
||||
String key = tapeKey(level, pos);
|
||||
AABB searchBox = AABB.unitCubeFromLowerCorner(Vec3.atLowerCornerOf(pos)).inflate(2.0D);
|
||||
STUCK_ENTITIES.entrySet().removeIf(entry -> {
|
||||
StuckContact contact = entry.getValue();
|
||||
if (!key.equals(contact.tapeKey())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
level.getEntitiesOfClass(Entity.class, searchBox, candidate -> candidate.getUUID().equals(entry.getKey()))
|
||||
.forEach(candidate -> candidate.setNoGravity(false));
|
||||
|
||||
STICK_SOUND_TIMES.remove(entry.getKey());
|
||||
STICK_CONTACT_KEYS.remove(entry.getKey());
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
private static void releaseEntity(Entity entity) {
|
||||
if (entity == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
STUCK_ENTITIES.remove(entity.getUUID());
|
||||
STICK_SOUND_TIMES.remove(entity.getUUID());
|
||||
STICK_CONTACT_KEYS.remove(entity.getUUID());
|
||||
entity.setNoGravity(false);
|
||||
}
|
||||
|
||||
private static String tapeDecayKey(Level level, BlockPos pos) {
|
||||
return level.dimension().location() + ":" + pos.asLong();
|
||||
}
|
||||
|
||||
private static String tapeKey(Level level, BlockPos pos) {
|
||||
return level.dimension().location() + ":" + pos.asLong();
|
||||
}
|
||||
|
||||
private static boolean shouldPlayStickSound(UUID entityId, String tapeKey, long gameTime) {
|
||||
String lastKey = STICK_CONTACT_KEYS.get(entityId);
|
||||
Long lastContact = STICK_SOUND_TIMES.get(entityId);
|
||||
if (lastKey == null || !tapeKey.equals(lastKey)) {
|
||||
STICK_CONTACT_KEYS.put(entityId, tapeKey);
|
||||
STICK_SOUND_TIMES.put(entityId, gameTime);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (lastContact == null || gameTime - lastContact > STICK_SOUND_COOLDOWN_TICKS) {
|
||||
STICK_SOUND_TIMES.put(entityId, gameTime);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static double clamp(double value, double min, double max) {
|
||||
return Math.max(min, Math.min(max, value));
|
||||
}
|
||||
|
||||
private record StuckContact(
|
||||
String tapeKey,
|
||||
String dimensionId,
|
||||
long blockPos,
|
||||
double offsetX,
|
||||
double offsetY,
|
||||
double offsetZ
|
||||
) {
|
||||
}
|
||||
|
||||
private record TapeHit(BlockPos pos, BlockState state) {
|
||||
}
|
||||
}
|
||||
@@ -26,8 +26,7 @@ public interface PortalGunPortalCellAccess {
|
||||
default boolean portalGun$putFaceRecord(PortalGunPortalFaceRecord record) {
|
||||
PortalGunPortalFaceRecord existing = this.portalGun$getFaceRecord(record.face());
|
||||
if (existing != null
|
||||
&& (!existing.ownerId().equals(record.ownerId())
|
||||
|| !java.util.Objects.equals(existing.channelName(), record.channelName())
|
||||
&& (!java.util.Objects.equals(existing.gunId(), record.gunId())
|
||||
|| existing.side() != record.side())) {
|
||||
return false;
|
||||
}
|
||||
@@ -63,7 +62,7 @@ public interface PortalGunPortalCellAccess {
|
||||
if (record.ownerId() == null || record.portalId() == null) {
|
||||
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());
|
||||
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.PortalGunPortalEntity;
|
||||
import com.craisinlord.antarchy.content.portalgun.PortalGunVariant;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
@@ -14,9 +15,8 @@ import net.minecraft.nbt.Tag;
|
||||
|
||||
public record PortalGunPortalFaceRecord(
|
||||
UUID ownerId,
|
||||
String ownerIdentity,
|
||||
UUID gunId,
|
||||
String channelName,
|
||||
PortalGunVariant variant,
|
||||
UUID portalId,
|
||||
UUID linkedPortalId,
|
||||
PortalGunPortalEntity.PortalSide side,
|
||||
@@ -43,21 +43,20 @@ public record PortalGunPortalFaceRecord(
|
||||
}
|
||||
|
||||
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) {
|
||||
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() {
|
||||
CompoundTag tag = new CompoundTag();
|
||||
tag.putUUID("OwnerId", this.ownerId);
|
||||
tag.putString("OwnerIdentity", this.ownerIdentity == null ? "" : this.ownerIdentity);
|
||||
if (this.gunId != null) {
|
||||
tag.putUUID("GunId", this.gunId);
|
||||
}
|
||||
tag.putString("ChannelName", this.channelName == null ? "" : this.channelName);
|
||||
tag.putInt("Variant", this.variant.id());
|
||||
tag.putUUID("PortalId", this.portalId);
|
||||
if (this.linkedPortalId != null) {
|
||||
tag.putUUID("LinkedPortalId", this.linkedPortalId);
|
||||
@@ -105,9 +104,8 @@ public record PortalGunPortalFaceRecord(
|
||||
int height = net.minecraft.util.Mth.clamp(tag.getInt("PortalHeight"), 2, 16);
|
||||
return new PortalGunPortalFaceRecord(
|
||||
tag.getUUID("OwnerId"),
|
||||
tag.getString("OwnerIdentity"),
|
||||
tag.hasUUID("GunId") ? tag.getUUID("GunId") : null,
|
||||
tag.getString("ChannelName"),
|
||||
PortalGunVariant.byId(tag.getInt("Variant")),
|
||||
tag.getUUID("PortalId"),
|
||||
tag.hasUUID("LinkedPortalId") ? tag.getUUID("LinkedPortalId") : null,
|
||||
side,
|
||||
|
||||
@@ -4,6 +4,7 @@ import com.craisinlord.antarchy.Antarchy;
|
||||
import com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity;
|
||||
import com.craisinlord.antarchy.content.portalgun.PortalGunPlacement;
|
||||
import com.craisinlord.antarchy.content.portalgun.PortalGunSavedData;
|
||||
import com.craisinlord.antarchy.content.portalgun.PortalGunVariant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.EnumMap;
|
||||
import java.util.HashSet;
|
||||
@@ -35,9 +36,8 @@ import net.minecraft.world.phys.Vec3;
|
||||
public class PortalGunPortalMasterBlockEntity extends BlockEntity implements PortalGunPortalCellAccess {
|
||||
private final Map<Direction, PortalGunPortalFaceRecord> faceRecords = new EnumMap<>(Direction.class);
|
||||
private UUID ownerId;
|
||||
private String ownerIdentity;
|
||||
private UUID gunId;
|
||||
private String channelName;
|
||||
private PortalGunVariant variant = PortalGunVariant.DEFAULT;
|
||||
private UUID portalId;
|
||||
private PortalGunPortalEntity.PortalSide side = PortalGunPortalEntity.PortalSide.BLUE;
|
||||
private Direction facing = Direction.NORTH;
|
||||
@@ -57,8 +57,7 @@ public class PortalGunPortalMasterBlockEntity extends BlockEntity implements Por
|
||||
public void configure(
|
||||
UUID ownerId,
|
||||
UUID gunId,
|
||||
String ownerIdentity,
|
||||
String channelName,
|
||||
PortalGunVariant variant,
|
||||
UUID portalId,
|
||||
PortalGunPortalEntity.PortalSide side,
|
||||
Direction facing,
|
||||
@@ -71,9 +70,8 @@ public class PortalGunPortalMasterBlockEntity extends BlockEntity implements Por
|
||||
int portalHeight
|
||||
) {
|
||||
this.ownerId = ownerId;
|
||||
this.ownerIdentity = ownerIdentity;
|
||||
this.gunId = gunId;
|
||||
this.channelName = channelName;
|
||||
this.variant = variant == null ? PortalGunVariant.DEFAULT : variant;
|
||||
this.portalId = portalId;
|
||||
this.side = side;
|
||||
this.facing = facing;
|
||||
@@ -84,7 +82,7 @@ public class PortalGunPortalMasterBlockEntity extends BlockEntity implements Por
|
||||
this.portalHeight = portalHeight;
|
||||
this.compensatedSpots = new HashSet<>(compensatedSpots);
|
||||
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();
|
||||
}
|
||||
|
||||
@@ -105,30 +103,8 @@ public class PortalGunPortalMasterBlockEntity extends BlockEntity implements Por
|
||||
return this.gunId;
|
||||
}
|
||||
|
||||
public String getChannelName() {
|
||||
return this.channelName;
|
||||
}
|
||||
|
||||
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 PortalGunVariant getVariant() {
|
||||
return this.variant;
|
||||
}
|
||||
|
||||
public PortalGunPortalEntity.PortalSide getSide() {
|
||||
@@ -228,12 +204,6 @@ public class PortalGunPortalMasterBlockEntity extends BlockEntity implements Por
|
||||
level.removeBlock(pos, false);
|
||||
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);
|
||||
UUID linkedPortalId = portal.getLinkedPortalId();
|
||||
if ((linkedPortalId == null && blockEntity.linkedPortalId != null)
|
||||
@@ -282,7 +252,7 @@ public class PortalGunPortalMasterBlockEntity extends BlockEntity implements Por
|
||||
if (this.portalId == null || this.ownerId == 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;
|
||||
}
|
||||
if (level.getEntity(this.portalId) instanceof PortalGunPortalEntity portal && !portal.isRemoved()) {
|
||||
@@ -300,18 +270,18 @@ public class PortalGunPortalMasterBlockEntity extends BlockEntity implements Por
|
||||
@SuppressWarnings("unchecked")
|
||||
PortalGunPortalEntity restored = new PortalGunPortalEntity((EntityType<? extends PortalGunPortalEntity>) rawType, level);
|
||||
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);
|
||||
Vec3 center = placement.center();
|
||||
restored.moveTo(center.x, center.y, center.z, placement.yaw(), 0.0F);
|
||||
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;
|
||||
}
|
||||
|
||||
public static PortalGunPortalEntity restorePortalEntity(ServerLevel level, PortalGunPortalFaceRecord record) {
|
||||
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;
|
||||
}
|
||||
if (level.getEntity(record.portalId()) instanceof PortalGunPortalEntity portal && !portal.isRemoved()) {
|
||||
@@ -325,13 +295,13 @@ public class PortalGunPortalMasterBlockEntity extends BlockEntity implements Por
|
||||
@SuppressWarnings("unchecked")
|
||||
PortalGunPortalEntity restored = new PortalGunPortalEntity((EntityType<? extends PortalGunPortalEntity>) rawType, level);
|
||||
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());
|
||||
Vec3 center = placement.center();
|
||||
restored.moveTo(center.x, center.y, center.z, placement.yaw(), 0.0F);
|
||||
level.addFreshEntity(restored);
|
||||
PortalGunSavedData.setPortal(level, record.ownerId(), record.gunId(), record.channelName(), 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);
|
||||
PortalGunSavedData.setPortal(level, record.gunId(), record.side(), restored.getUUID());
|
||||
PortalGunPortalEntity linked = PortalGunSavedData.findLoadedPortal(level, record.gunId(), record.side() == PortalGunPortalEntity.PortalSide.BLUE ? PortalGunPortalEntity.PortalSide.ORANGE : PortalGunPortalEntity.PortalSide.BLUE);
|
||||
if (linked != null && linked != restored && !linked.isRemoved()) {
|
||||
restored.linkTo(linked);
|
||||
linked.linkTo(restored);
|
||||
@@ -345,9 +315,7 @@ public class PortalGunPortalMasterBlockEntity extends BlockEntity implements Por
|
||||
}
|
||||
PortalGunPortalEntity counterpart = PortalGunSavedData.findLoadedPortal(
|
||||
level,
|
||||
this.ownerId,
|
||||
this.gunId,
|
||||
this.channelName,
|
||||
this.side == PortalGunPortalEntity.PortalSide.BLUE ? PortalGunPortalEntity.PortalSide.ORANGE : PortalGunPortalEntity.PortalSide.BLUE
|
||||
);
|
||||
if (counterpart == null || counterpart == portal || counterpart.isRemoved()) {
|
||||
@@ -365,15 +333,10 @@ public class PortalGunPortalMasterBlockEntity extends BlockEntity implements Por
|
||||
if (this.ownerId != null) {
|
||||
tag.putUUID("OwnerId", this.ownerId);
|
||||
}
|
||||
if (this.ownerIdentity != null) {
|
||||
tag.putString("OwnerIdentity", this.ownerIdentity);
|
||||
}
|
||||
if (this.gunId != null) {
|
||||
tag.putUUID("GunId", this.gunId);
|
||||
}
|
||||
if (this.channelName != null) {
|
||||
tag.putString("ChannelName", this.channelName);
|
||||
}
|
||||
tag.putInt("Variant", this.variant.id());
|
||||
if (this.portalId != null) {
|
||||
tag.putUUID("PortalId", this.portalId);
|
||||
}
|
||||
@@ -414,9 +377,8 @@ public class PortalGunPortalMasterBlockEntity extends BlockEntity implements Por
|
||||
protected void loadAdditional(CompoundTag tag, HolderLookup.Provider registries) {
|
||||
super.loadAdditional(tag, registries);
|
||||
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.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.linkedPortalId = tag.hasUUID("LinkedPortalId") ? tag.getUUID("LinkedPortalId") : null;
|
||||
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.portalGun$loadFaceRecords(tag);
|
||||
if (this.faceRecords.isEmpty() && this.ownerId != null && this.portalId != null && this.portalSpots.length > 0) {
|
||||
this.portalGun$putFaceRecord(new PortalGunPortalFaceRecord(this.ownerId, this.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.PortalGunTransformUtil;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.phys.AABB;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
|
||||
public final class PortalGunRollClientState {
|
||||
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_EPSILON = 0.05F;
|
||||
private static final Map<UUID, Long> TELEPORT_COOLDOWNS = new HashMap<>();
|
||||
private static float previousRollDegrees;
|
||||
private static float rollDegrees;
|
||||
|
||||
@@ -34,10 +28,7 @@ public final class PortalGunRollClientState {
|
||||
if (minecraft.level == null || entity == null) {
|
||||
return false;
|
||||
}
|
||||
for (PortalGunPortalEntity portal : minecraft.level.getEntitiesOfClass(
|
||||
PortalGunPortalEntity.class,
|
||||
entity.getBoundingBox().inflate(PORTAL_QUERY_RADIUS),
|
||||
Entity::isAlive)) {
|
||||
for (PortalGunPortalEntity portal : com.craisinlord.antarchy.content.portalgun.PortalGunPortalRegistry.near(minecraft.level, entity.getBoundingBox().inflate(PORTAL_QUERY_RADIUS))) {
|
||||
if (portal.intersectsEntityBounds(entity)) {
|
||||
return true;
|
||||
}
|
||||
@@ -53,7 +44,6 @@ public final class PortalGunRollClientState {
|
||||
public static void clear() {
|
||||
previousRollDegrees = 0.0F;
|
||||
rollDegrees = 0.0F;
|
||||
TELEPORT_COOLDOWNS.clear();
|
||||
}
|
||||
|
||||
public static void tick(Minecraft minecraft) {
|
||||
@@ -61,74 +51,17 @@ public final class PortalGunRollClientState {
|
||||
clear();
|
||||
return;
|
||||
}
|
||||
Entity cameraEntity = minecraft.getCameraEntity();
|
||||
if (cameraEntity == null) {
|
||||
return;
|
||||
}
|
||||
previousRollDegrees = rollDegrees;
|
||||
rollDegrees *= ROLL_DECAY;
|
||||
if (Math.abs(rollDegrees) < ROLL_EPSILON) {
|
||||
rollDegrees = 0.0F;
|
||||
}
|
||||
long gameTime = minecraft.level.getGameTime();
|
||||
TELEPORT_COOLDOWNS.entrySet().removeIf(entry -> entry.getValue() <= gameTime);
|
||||
for (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) {
|
||||
if (!entity.isAlive() || entity instanceof PortalGunPortalEntity || entity.isPassenger() || entity.isVehicle()) {
|
||||
return false;
|
||||
}
|
||||
long cooldownUntil = TELEPORT_COOLDOWNS.getOrDefault(entity.getUUID(), 0L);
|
||||
return cooldownUntil <= gameTime;
|
||||
}
|
||||
|
||||
private static void applyPredictedTeleport(Entity entity, PortalGunPortalEntity sourcePortal, PortalGunPortalEntity destinationPortal, 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);
|
||||
public static void onTransit(PortalGunPortalEntity sourcePortal, PortalGunPortalEntity destinationPortal, Vec3 look) {
|
||||
Vec3 transformedLook = PortalGunTransformUtil.transformVector(sourcePortal, destinationPortal, look).normalize();
|
||||
Vec3 transformedUp = PortalGunTransformUtil.transformVector(sourcePortal, destinationPortal, PortalGunTransformUtil.upVectorFromLookAndRoll(look, rollDegrees)).normalize();
|
||||
rollDegrees = PortalGunTransformUtil.rollFromOrientation(transformedLook, transformedUp);
|
||||
Vec3 exitPos = transit.exitPosition();
|
||||
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;
|
||||
previousRollDegrees = rollDegrees;
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import net.minecraft.client.Options;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
|
||||
public final class PortalGunZoomClientState {
|
||||
private static final int MAX_PROGRESS = 5;
|
||||
private static int progress;
|
||||
private static boolean zooming;
|
||||
private static Double originalSensitivity;
|
||||
@@ -34,7 +35,7 @@ public final class PortalGunZoomClientState {
|
||||
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 (progress == 0) {
|
||||
restoreSensitivity(minecraft.options);
|
||||
@@ -47,7 +48,13 @@ public final class PortalGunZoomClientState {
|
||||
restoreSensitivity(options);
|
||||
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);
|
||||
if (originalSensitivity != null) {
|
||||
options.sensitivity().set(originalSensitivity * zoomFactor);
|
||||
@@ -56,7 +63,8 @@ public final class PortalGunZoomClientState {
|
||||
}
|
||||
|
||||
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) {
|
||||
|
||||
@@ -72,7 +72,7 @@ public final class ElythiaSkyRenderer {
|
||||
float blue = (float) skyColor.z;
|
||||
float rainLevel = level.getRainLevel(partialTick);
|
||||
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)
|
||||
float nightPurpleR = 0.055F;
|
||||
@@ -90,7 +90,7 @@ public final class ElythiaSkyRenderer {
|
||||
RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F);
|
||||
|
||||
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 (coloredStarBuffer == null) {
|
||||
@@ -210,28 +210,31 @@ public final class ElythiaSkyRenderer {
|
||||
RenderSystem.disableBlend();
|
||||
}
|
||||
|
||||
private static void renderCelestialBodies(Matrix4f modelViewMatrix, Matrix4f projectionMatrix, float timeOfDay, float rainLevel) {
|
||||
float t = timeOfDay * 360.0F;
|
||||
private static void renderCelestialBodies(Matrix4f modelViewMatrix, Matrix4f projectionMatrix, double timeDegrees, float rainLevel) {
|
||||
|
||||
// Purple sun
|
||||
float sun1Orbit = t;
|
||||
float sun1Orbit = orbitAngle(timeDegrees, 1.0D, 0.0D);
|
||||
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
|
||||
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);
|
||||
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
|
||||
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);
|
||||
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
|
||||
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);
|
||||
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(
|
||||
|
||||
@@ -15,8 +15,6 @@ import net.minecraft.world.effect.MobEffect;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
|
||||
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;
|
||||
|
||||
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, 1.0F);
|
||||
} else {
|
||||
putPortalVertex(consumer, pose, entity, portalScale, -halfWidth, -halfHeight, 0.0F, 1.0F, false);
|
||||
putPortalVertex(consumer, pose, entity, portalScale, -halfWidth, halfHeight, 0.0F, 0.0F, false);
|
||||
putPortalVertex(consumer, pose, entity, portalScale, halfWidth, halfHeight, 1.0F, 0.0F, false);
|
||||
putPortalVertex(consumer, pose, entity, portalScale, halfWidth, -halfHeight, 1.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, true);
|
||||
putPortalVertex(consumer, pose, entity, portalScale, halfWidth, halfHeight, 1.0F, 0.0F, true);
|
||||
putPortalVertex(consumer, pose, entity, portalScale, halfWidth, -halfHeight, 1.0F, 1.0F, true);
|
||||
}
|
||||
VertexConsumer edge = bufferSource.getBuffer(RenderType.entityTranslucent(this.getTextureLocation(entity)));
|
||||
putPortalVertex(edge, pose, entity, portalScale, -halfWidth - 0.06F, -halfHeight - 0.07F, 0.0F, 1.0F, true);
|
||||
putPortalVertex(edge, pose, entity, portalScale, -halfWidth - 0.06F, halfHeight + 0.07F, 0.0F, 0.0F, true);
|
||||
putPortalVertex(edge, pose, entity, portalScale, halfWidth + 0.06F, halfHeight + 0.07F, 1.0F, 0.0F, true);
|
||||
putPortalVertex(edge, pose, entity, portalScale, halfWidth + 0.06F, -halfHeight - 0.07F, 1.0F, 1.0F, true);
|
||||
VertexConsumer edge = bufferSource.getBuffer(RenderType.entityTranslucent(PortalGunPortalRim.texture(entity)));
|
||||
float rimWidth = halfWidth + PortalGunPortalRim.EXTRA_WIDTH;
|
||||
float rimHeight = halfHeight + PortalGunPortalRim.EXTRA_HEIGHT;
|
||||
putPortalVertex(edge, pose, entity, portalScale, -rimWidth, -rimHeight, 0.0F, 1.0F, true);
|
||||
putPortalVertex(edge, pose, entity, portalScale, -rimWidth, rimHeight, 0.0F, 0.0F, true);
|
||||
putPortalVertex(edge, pose, entity, portalScale, rimWidth, rimHeight, 1.0F, 0.0F, true);
|
||||
putPortalVertex(edge, pose, entity, portalScale, rimWidth, -rimHeight, 1.0F, 1.0F, true);
|
||||
poseStack.popPose();
|
||||
}
|
||||
|
||||
private static ResourceLocation portalTexture(PortalGunPortalEntity entity) {
|
||||
String color = entity.getPortalSide() == PortalGunPortalEntity.PortalSide.BLUE ? "blue" : "orange";
|
||||
String path = isFancyPortalsEnabled()
|
||||
? "textures/entity/portal_gun/portal_" + color + "_vortex.png"
|
||||
: "textures/vfx/portal_gun_portal_" + color + ".png";
|
||||
return ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, path);
|
||||
return isFancyPortalsEnabled()
|
||||
? entity.getVariant().vortexTexture(entity.getPortalSide())
|
||||
: PortalGunPortalRim.texture(entity);
|
||||
}
|
||||
|
||||
private static boolean isFancyPortalsEnabled() {
|
||||
@@ -84,10 +84,11 @@ public class PortalGunPortalRenderer extends GeoEntityRenderer<PortalGunPortalEn
|
||||
Vec3 position = entity.getWidthVec().normalize().scale(horizontal * portalScale)
|
||||
.add(entity.getUpVec().normalize().scale(vertical * portalScale))
|
||||
.add(entity.getNormalVec().normalize().scale(0.03125D));
|
||||
int red = channelTint ? entity.getChannelRed() : 255;
|
||||
int green = channelTint ? entity.getChannelGreen() : 255;
|
||||
int blue = channelTint ? entity.getChannelBlue() : 255;
|
||||
int alpha = channelTint ? 150 : 210;
|
||||
int color = entity.getPortalColor();
|
||||
int red = channelTint ? color >> 16 & 0xFF : 255;
|
||||
int green = channelTint ? color >> 8 & 0xFF : 255;
|
||||
int blue = channelTint ? color & 0xFF : 255;
|
||||
int alpha = channelTint ? PortalGunPortalRim.ENTITY_ALPHA : 210;
|
||||
consumer.addVertex(pose, (float) position.x, (float) position.y, (float) position.z)
|
||||
.setColor(red, green, blue, alpha)
|
||||
.setUv(u, v)
|
||||
|
||||
@@ -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,
|
||||
Matrix4f projection, Matrix4f skyProjection, Frustum frustum,
|
||||
PortalTerrain terrain, Consumer<PoseStack> crossingEntities, String tracePair) {
|
||||
PortalTerrain terrain, Consumer<PoseStack> crossingEntities) {
|
||||
LevelRenderer renderer = minecraft.levelRenderer;
|
||||
LevelRendererPortalViewAreaAccessor state = (LevelRendererPortalViewAreaAccessor) renderer;
|
||||
LevelRendererPortalSceneInvoker scene = (LevelRendererPortalSceneInvoker) renderer;
|
||||
@@ -82,7 +82,7 @@ public final class PortalGunPortalSceneRenderer {
|
||||
boolean foggy = minecraft.level.effects().isFoggyAt(cameraBlockPos.getX(), cameraBlockPos.getY())
|
||||
|| minecraft.gui.getBossOverlay().shouldCreateWorldFog();
|
||||
|
||||
try (PortalSceneRenderTrace.Phase ignored = PortalSceneRenderTrace.begin(tracePair, "background-and-sky", "sections=" + terrain.sectionCount())) {
|
||||
{
|
||||
RenderSystem.setShaderGameTime(minecraft.level.getGameTime(), partialTick);
|
||||
minecraft.getBlockEntityRenderDispatcher().prepare(minecraft.level, camera, minecraft.hitResult);
|
||||
minecraft.getEntityRenderDispatcher().prepare(minecraft.level, camera, minecraft.crosshairPickEntity);
|
||||
@@ -99,7 +99,7 @@ public final class PortalGunPortalSceneRenderer {
|
||||
|
||||
RenderSystem.setProjectionMatrix(projection, VertexSorting.DISTANCE_TO_ORIGIN);
|
||||
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.depthMask(true);
|
||||
terrain.drawLayer(minecraft, RenderType.solid(), cameraPosition, viewMatrix, projection);
|
||||
@@ -118,7 +118,7 @@ public final class PortalGunPortalSceneRenderer {
|
||||
PoseStack poseStack = new PoseStack();
|
||||
MultiBufferSource.BufferSource buffers = bufferSource();
|
||||
try {
|
||||
try (PortalSceneRenderTrace.Phase ignored = PortalSceneRenderTrace.begin(tracePair, "entities", "rendering")) {
|
||||
{
|
||||
for (Entity entity : minecraft.level.entitiesForRendering()) {
|
||||
if (!minecraft.getEntityRenderDispatcher().shouldRender(entity, frustum, cameraX, cameraY, cameraZ)
|
||||
&& !entity.hasIndirectPassenger(minecraft.player)) {
|
||||
@@ -138,20 +138,20 @@ public final class PortalGunPortalSceneRenderer {
|
||||
}
|
||||
buffers.endBatch();
|
||||
|
||||
try (PortalSceneRenderTrace.Phase ignored = PortalSceneRenderTrace.begin(tracePair, "block-entities", "rendering")) {
|
||||
{
|
||||
terrain.forEachBlockEntity(minecraft, frustum, blockEntity ->
|
||||
renderBlockEntity(minecraft, blockEntity, cameraX, cameraY, cameraZ, partialTick, poseStack, buffers));
|
||||
}
|
||||
buffers.endBatch();
|
||||
|
||||
try (PortalSceneRenderTrace.Phase ignored = PortalSceneRenderTrace.begin(tracePair, "translucent-and-particles", "rendering")) {
|
||||
{
|
||||
terrain.drawLayer(minecraft, RenderType.translucent(), cameraPosition, viewMatrix, projection);
|
||||
buffers.endBatch();
|
||||
terrain.drawLayer(minecraft, RenderType.tripwire(), cameraPosition, viewMatrix, projection);
|
||||
minecraft.particleEngine.render(minecraft.gameRenderer.lightTexture(), camera, partialTick);
|
||||
}
|
||||
|
||||
try (PortalSceneRenderTrace.Phase ignored = PortalSceneRenderTrace.begin(tracePair, "weather-and-border", "rendering")) {
|
||||
{
|
||||
RenderSystem.depthMask(false);
|
||||
scene.antarchy$renderPortalWeather(minecraft.gameRenderer.lightTexture(), partialTick, cameraX, cameraY, cameraZ);
|
||||
scene.antarchy$renderPortalWorldBorder(camera);
|
||||
|
||||
@@ -45,19 +45,17 @@ import org.joml.Vector4f;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
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_VIEWS_PER_FRAME = 4;
|
||||
private static final double PORTAL_SEARCH_RADIUS = 128.0D;
|
||||
private static final int PORTAL_TARGET_FILTER = 9729;
|
||||
private static final double SURFACE_OFFSET = 0.04D;
|
||||
private static final double EXIT_CLIP_OFFSET = 0.01D;
|
||||
private static final int APERTURE_SEGMENTS = 32;
|
||||
private static final double APERTURE_RIM_FRACTION = 0.92D;
|
||||
private static TextureTarget offscreenTarget;
|
||||
private static ShaderInstance portalViewShader;
|
||||
private static boolean renderingPortalView;
|
||||
private static boolean loggedRendererActivation;
|
||||
private static boolean loggedMissingPortalViewShader;
|
||||
private static String lastPortalCandidateState = "";
|
||||
private static long lastRenderFailureTick = Long.MIN_VALUE;
|
||||
|
||||
private PortalGunPortalViewRenderer() {
|
||||
@@ -65,7 +63,6 @@ public final class PortalGunPortalViewRenderer {
|
||||
|
||||
public static void setPortalViewShader(ShaderInstance shader) {
|
||||
portalViewShader = shader;
|
||||
Antarchy.LOGGER.info("Portal gun view shader registered");
|
||||
}
|
||||
|
||||
public static void clearPortalViewShader() {
|
||||
@@ -77,7 +74,6 @@ public final class PortalGunPortalViewRenderer {
|
||||
}
|
||||
|
||||
public static void releaseTargets() {
|
||||
PortalSceneRenderTrace.reset();
|
||||
PortalGunPortalVisibilityQueries.clear();
|
||||
PortalGunPortalSectionViews.clear();
|
||||
if (offscreenTarget != null) {
|
||||
@@ -87,18 +83,35 @@ public final class PortalGunPortalViewRenderer {
|
||||
}
|
||||
|
||||
public static void render(Camera camera, Matrix4f poseMatrix, Matrix4f projectionMatrix, DeltaTracker tickCounter) {
|
||||
if (!isEnabled() || renderingPortalView) {
|
||||
if (renderingPortalView) {
|
||||
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();
|
||||
Minecraft minecraft = Minecraft.getInstance();
|
||||
if (minecraft.level == null || minecraft.gameRenderer == null || camera == null || poseMatrix == null || projectionMatrix == null || tickCounter == null) {
|
||||
return;
|
||||
}
|
||||
if (!loggedRendererActivation) {
|
||||
loggedRendererActivation = true;
|
||||
Antarchy.LOGGER.info("Portal gun view renderer hook active (terrain: {})", SodiumCompat.isLoaded() ? "sodium" : "vanilla");
|
||||
}
|
||||
Entity cameraEntity = minecraft.getCameraEntity();
|
||||
if (cameraEntity == null) {
|
||||
return;
|
||||
@@ -127,10 +140,10 @@ public final class PortalGunPortalViewRenderer {
|
||||
}
|
||||
}
|
||||
candidates.sort((left, right) -> Integer.compare(right.clip().area(), left.clip().area()));
|
||||
if (candidates.size() > MAX_ROOT_VIEWS) {
|
||||
candidates = candidates.subList(0, MAX_ROOT_VIEWS);
|
||||
int maxRootViews = AntarchySettings.portalGunMaxLiveViews();
|
||||
if (candidates.size() > maxRootViews) {
|
||||
candidates = candidates.subList(0, maxRootViews);
|
||||
}
|
||||
logCandidateState(portals, candidates);
|
||||
if (candidates.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
@@ -147,7 +160,7 @@ public final class PortalGunPortalViewRenderer {
|
||||
Vector3f rootLook = cameraToWorld.transformDirection(new Vector3f(0.0F, 0.0F, -1.0F)).normalize();
|
||||
Vector3f rootUp = cameraToWorld.transformDirection(new Vector3f(0.0F, 1.0F, 0.0F)).normalize();
|
||||
RenderContext rootContext = new RenderContext(cameraPos, new Vec3(rootLook), new Vec3(rootUp), rootViewMatrix);
|
||||
ViewBudget budget = new ViewBudget(MAX_VIEWS_PER_FRAME);
|
||||
ViewBudget budget = new ViewBudget(maxRootViews * 2);
|
||||
PortalRenderWatchdog.portalViewDrawn();
|
||||
renderingPortalView = true;
|
||||
RenderSystem.backupProjectionMatrix();
|
||||
@@ -202,7 +215,6 @@ public final class PortalGunPortalViewRenderer {
|
||||
if (!PortalTerrain.canView(minecraft, exit.center())) {
|
||||
return;
|
||||
}
|
||||
String pairKey = source.getUUID() + "->" + destination.getUUID();
|
||||
boolean root = parentLevel == 0;
|
||||
boolean visible = !root || PortalGunPortalVisibilityQueries.shouldRender(source.getUUID(), destination.getUUID());
|
||||
RenderTarget target = minecraft.getMainRenderTarget();
|
||||
@@ -224,14 +236,21 @@ public final class PortalGunPortalViewRenderer {
|
||||
Matrix4f clippedProjection = applyPortalClipPlane(rootProjection, transformed, destination);
|
||||
RenderSystem.enableScissor(clip.x(), clip.y(), clip.width(), clip.height());
|
||||
renderScene(minecraft, cameraEntity, partialTick, transformed, rootProjection, clippedProjection,
|
||||
source, destination, clip, target.width, target.height, parentLevel + 1, pairKey);
|
||||
source, destination, clip, target.width, target.height, parentLevel + 1);
|
||||
if (parentLevel + 1 < MAX_VIEW_LEVELS) {
|
||||
renderNestedViews(minecraft, cameraEntity, partialTick, transformed, rootProjection, clippedProjection,
|
||||
destination, parentLevel + 1, clip, budget, target.width, target.height);
|
||||
}
|
||||
} finally {
|
||||
try {
|
||||
if (visible) {
|
||||
PortalStencilTarget.drawInside(parentLevel + 1);
|
||||
PortalGunPortalRim.drawOverView(source, context.cameraPos(), context.viewMatrix(), parentProjection, partialTick);
|
||||
}
|
||||
} finally {
|
||||
PortalStencilTarget.popAperture(parentLevel,
|
||||
() -> restorePortalApertureDepth(context.cameraPos(), context.viewMatrix(), parentProjection, source, partialTick));
|
||||
}
|
||||
if (parentClip == null) {
|
||||
RenderSystem.disableScissor();
|
||||
} else {
|
||||
@@ -275,7 +294,6 @@ public final class PortalGunPortalViewRenderer {
|
||||
TextureTarget target = ensureOffscreenTarget(mainTarget.width, mainTarget.height);
|
||||
RenderContext transformed = transformContext(context, source, destination);
|
||||
Matrix4f clippedProjection = applyPortalClipPlane(rootProjection, transformed, destination);
|
||||
String pairKey = source.getUUID() + "->" + destination.getUUID();
|
||||
MinecraftMainRenderTargetAccessor targetAccessor = (MinecraftMainRenderTargetAccessor) minecraft;
|
||||
targetAccessor.antarchy$setMainRenderTarget(target);
|
||||
try {
|
||||
@@ -285,7 +303,7 @@ public final class PortalGunPortalViewRenderer {
|
||||
target.bindWrite(true);
|
||||
RenderSystem.enableScissor(clip.x(), clip.y(), clip.width(), clip.height());
|
||||
renderScene(minecraft, cameraEntity, partialTick, transformed, rootProjection, clippedProjection,
|
||||
source, destination, clip, target.width, target.height, 1, pairKey);
|
||||
source, destination, clip, target.width, target.height, 1);
|
||||
} finally {
|
||||
RenderSystem.disableScissor();
|
||||
targetAccessor.antarchy$setMainRenderTarget(mainTarget);
|
||||
@@ -298,7 +316,7 @@ public final class PortalGunPortalViewRenderer {
|
||||
private static void renderScene(Minecraft minecraft, Entity cameraEntity, float partialTick, RenderContext transformed,
|
||||
Matrix4f rootProjection, Matrix4f clippedProjection,
|
||||
PortalGunPortalEntity source, PortalGunPortalEntity destination,
|
||||
ScreenClip clip, int width, int height, int level, String pairKey) {
|
||||
ScreenClip clip, int width, int height, int level) {
|
||||
PortalGunWorldPortalShape exit = destination.getWorldPortalShape();
|
||||
PortalCamera portalCamera = new PortalCamera();
|
||||
portalCamera.setup(minecraft.level, cameraEntity, partialTick, transformed.cameraPos(), transformed.look(), transformed.up());
|
||||
@@ -308,42 +326,26 @@ public final class PortalGunPortalViewRenderer {
|
||||
source.getWorldPortalShape(), exit, transformed.cameraPos(), transformed.look(), transformed.up(),
|
||||
transformed.viewMatrix(), clippedProjection, level, false, destination.getUUID()));
|
||||
try {
|
||||
PortalTerrain terrain;
|
||||
try (PortalSceneRenderTrace.Phase ignored = PortalSceneRenderTrace.begin(pairKey, "section-visibility", "level=" + level)) {
|
||||
terrain = PortalTerrain.prepare(minecraft, destination.getUUID(), exit.center(), exit.normal(), frustum);
|
||||
}
|
||||
PortalTerrain terrain = PortalTerrain.prepare(minecraft, destination.getUUID(), exit.center(), exit.normal(), frustum);
|
||||
PortalGunPortalSceneRenderer.render(minecraft, portalCamera, partialTick, transformed.viewMatrix(), clippedProjection,
|
||||
rootProjection, frustum, terrain,
|
||||
poseStack -> renderCrossingEntities(minecraft, transformed.cameraPos(), partialTick, source, destination, poseStack),
|
||||
pairKey);
|
||||
poseStack -> renderCrossingEntities(minecraft, transformed.cameraPos(), partialTick, source, destination, poseStack));
|
||||
} finally {
|
||||
PortalGunPortalRenderState.popPortalView(false);
|
||||
}
|
||||
}
|
||||
|
||||
private static List<PortalGunPortalEntity> collectPortals(Minecraft minecraft, Vec3 cameraPos) {
|
||||
List<PortalGunPortalEntity> portals = minecraft.level.getEntitiesOfClass(PortalGunPortalEntity.class,
|
||||
new AABB(cameraPos, cameraPos).inflate(PORTAL_SEARCH_RADIUS), Entity::isAlive);
|
||||
portals.removeIf(portal -> portal.distanceToSqr(cameraPos) > PORTAL_SEARCH_RADIUS * PORTAL_SEARCH_RADIUS);
|
||||
List<PortalGunPortalEntity> portals = new ArrayList<>();
|
||||
for (PortalGunPortalEntity portal : com.craisinlord.antarchy.content.portalgun.PortalGunPortalRegistry.all(minecraft.level)) {
|
||||
if (portal.isAlive() && portal.distanceToSqr(cameraPos) <= PORTAL_SEARCH_RADIUS * PORTAL_SEARCH_RADIUS) {
|
||||
portals.add(portal);
|
||||
}
|
||||
}
|
||||
portals.sort((left, right) -> Double.compare(left.distanceToSqr(cameraPos), right.distanceToSqr(cameraPos)));
|
||||
return portals;
|
||||
}
|
||||
|
||||
private static void 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) {
|
||||
long gameTime = minecraft.level == null ? Long.MIN_VALUE : minecraft.level.getGameTime();
|
||||
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,
|
||||
PortalGunPortalEntity portal, float partialTick, int width, int height) {
|
||||
List<Vector4f> clipVertices = projectPortalPolygon(new Matrix4f(projectionMatrix).mul(viewMatrix), cameraPos,
|
||||
portal.getWorldPortalShape().scaleAperture(portal.getPortalVisualScale(partialTick)));
|
||||
List<Vector4f> clipVertices = projectPortalPolygon(new Matrix4f(projectionMatrix).mul(viewMatrix), cameraPos, portal, partialTick);
|
||||
if (clipVertices.size() < 3) {
|
||||
return null;
|
||||
}
|
||||
@@ -456,8 +457,8 @@ public final class PortalGunPortalViewRenderer {
|
||||
return nearPlane ? vertex.z + vertex.w : vertex.w - 0.001F;
|
||||
}
|
||||
|
||||
private static List<Vector4f> projectPortalPolygon(Matrix4f combined, Vec3 cameraPos, PortalGunWorldPortalShape shape) {
|
||||
Vec3[] corners = shape.getCorners(SURFACE_OFFSET);
|
||||
private static List<Vector4f> projectPortalPolygon(Matrix4f combined, Vec3 cameraPos, PortalGunPortalEntity portal, float partialTick) {
|
||||
Vec3[] corners = apertureOutline(portal, partialTick);
|
||||
List<Vector4f> polygon = new ArrayList<>(corners.length);
|
||||
for (Vec3 corner : corners) {
|
||||
Vec3 relative = corner.subtract(cameraPos);
|
||||
@@ -467,10 +468,17 @@ public final class PortalGunPortalViewRenderer {
|
||||
return clipPolygon(polygon, true);
|
||||
}
|
||||
|
||||
static Vec3[] apertureOutline(PortalGunPortalEntity portal, float partialTick) {
|
||||
float scale = portal.getPortalVisualScale(partialTick);
|
||||
PortalGunWorldPortalShape shape = portal.getWorldPortalShape().scaleAperture(scale);
|
||||
double radiusHorizontal = Math.min(shape.halfWidth(), (shape.halfWidth() + PortalGunPortalRim.EXTRA_WIDTH * scale) * APERTURE_RIM_FRACTION);
|
||||
double radiusVertical = Math.min(shape.halfHeight(), (shape.halfHeight() + PortalGunPortalRim.EXTRA_HEIGHT * scale) * APERTURE_RIM_FRACTION);
|
||||
return shape.getOutline(SURFACE_OFFSET, APERTURE_SEGMENTS, radiusHorizontal, radiusVertical);
|
||||
}
|
||||
|
||||
private static MeshData buildAperture(Vec3 cameraPos, Matrix4f viewMatrix, Matrix4f projectionMatrix,
|
||||
PortalGunPortalEntity portal, float partialTick, boolean withScreenUv) {
|
||||
PortalGunWorldPortalShape shape = portal.getWorldPortalShape().scaleAperture(portal.getPortalVisualScale(partialTick));
|
||||
List<Vector4f> polygon = projectPortalPolygon(new Matrix4f(projectionMatrix).mul(viewMatrix), cameraPos, shape);
|
||||
List<Vector4f> polygon = projectPortalPolygon(new Matrix4f(projectionMatrix).mul(viewMatrix), cameraPos, portal, partialTick);
|
||||
if (polygon.size() < 3) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -22,7 +22,6 @@ public final class PortalGunPortalVisibilityQueries {
|
||||
state.visible = GL15.glGetQueryObjecti(state.query, GL15.GL_QUERY_RESULT) != 0;
|
||||
state.hasResult = true;
|
||||
state.inFlight = false;
|
||||
PortalSceneRenderTrace.event(source + "->" + destination, "visibility-query-result", "visible=" + 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.mojang.blaze3d.vertex.PoseStack;
|
||||
import com.mojang.blaze3d.vertex.VertexConsumer;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.MultiBufferSource;
|
||||
import net.minecraft.client.renderer.RenderType;
|
||||
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.core.BlockPos;
|
||||
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;
|
||||
|
||||
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 ORANGE_TEXTURE = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "textures/vfx/portal_gun_portal_orange.png");
|
||||
private static final ResourceLocation SHOT_TEXTURE = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "textures/vfx/portal_gun_shot.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) {
|
||||
super(context);
|
||||
@@ -23,37 +38,103 @@ public class PortalGunProjectileRenderer extends EntityRenderer<PortalGunProject
|
||||
|
||||
@Override
|
||||
public void render(PortalGunProjectileEntity entity, float entityYaw, float partialTick, PoseStack poseStack, MultiBufferSource bufferSource, int packedLight) {
|
||||
poseStack.pushPose();
|
||||
poseStack.mulPose(this.entityRenderDispatcher.cameraOrientation());
|
||||
float age = entity.tickCount + partialTick;
|
||||
float syncedDistance = entity.getSyncedDistance() + partialTick;
|
||||
float pulse = 0.8F + 0.2F * (float) Math.sin(age * 0.9F);
|
||||
float travelPulse = 0.82F + 0.18F * (float) Math.sin(syncedDistance * 0.55F);
|
||||
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;
|
||||
ResourceLocation texture = entity.getPortalSide() == com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity.PortalSide.BLUE ? BLUE_TEXTURE : ORANGE_TEXTURE;
|
||||
float red = entity.getPortalSide() == com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity.PortalSide.BLUE ? 0.55F : 1.0F;
|
||||
float green = entity.getPortalSide() == com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity.PortalSide.BLUE ? 0.82F : 0.56F;
|
||||
float blue = entity.getPortalSide() == com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity.PortalSide.BLUE ? 1.0F : 0.18F;
|
||||
Vec3 entityPos = entity.getPosition(partialTick);
|
||||
Vec3 renderPos = this.muzzleBlendedPosition(entity, entityPos, partialTick);
|
||||
Vec3 cameraPos = this.entityRenderDispatcher.camera.getPosition();
|
||||
double cameraDistance = renderPos.distanceTo(cameraPos);
|
||||
if (cameraDistance < HIDDEN_DISTANCE) {
|
||||
return;
|
||||
}
|
||||
float fade = (float) Mth.clamp((cameraDistance - HIDDEN_DISTANCE) / (FADE_DISTANCE - HIDDEN_DISTANCE), 0.0D, 1.0D);
|
||||
int color = entity.getVariant().color(entity.getPortalSide());
|
||||
float red = (color >> 16 & 0xFF) / 255.0F;
|
||||
float green = (color >> 8 & 0xFF) / 255.0F;
|
||||
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();
|
||||
Matrix4f matrix = pose.pose();
|
||||
VertexConsumer core = bufferSource.getBuffer(RenderType.entityTranslucent(texture));
|
||||
addQuad(core, pose, matrix, red, green, blue, 0.92F, packedLight, -0.9F, -0.5F, 0.9F, 0.5F);
|
||||
VertexConsumer glow = bufferSource.getBuffer(RenderType.entityTranslucentEmissive(texture));
|
||||
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);
|
||||
VertexConsumer glow = bufferSource.getBuffer(RenderType.entityTranslucentEmissive(SHOT_TEXTURE));
|
||||
addQuad(glow, pose, matrix, red, green, blue, 0.55F * fade, GLOW_SIZE);
|
||||
addQuad(glow, pose, matrix, 1.0F, 1.0F, 1.0F, 0.9F * fade, CORE_SIZE);
|
||||
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
|
||||
protected int getBlockLightLevel(PortalGunProjectileEntity entity, BlockPos pos) {
|
||||
return 15;
|
||||
@@ -61,22 +142,22 @@ public class PortalGunProjectileRenderer extends EntityRenderer<PortalGunProject
|
||||
|
||||
@Override
|
||||
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) {
|
||||
addVertex(consumer, pose, matrix, minX, minY, 0.0F, 1.0F, red, green, blue, alpha, packedLight);
|
||||
addVertex(consumer, pose, matrix, maxX, minY, 1.0F, 1.0F, red, green, blue, alpha, packedLight);
|
||||
addVertex(consumer, pose, matrix, maxX, maxY, 1.0F, 0.0F, red, green, blue, alpha, packedLight);
|
||||
addVertex(consumer, pose, matrix, minX, maxY, 0.0F, 0.0F, red, green, blue, alpha, packedLight);
|
||||
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, -halfSize, -halfSize, 0.0F, 1.0F, red, green, blue, alpha);
|
||||
addVertex(consumer, pose, matrix, halfSize, -halfSize, 1.0F, 1.0F, red, green, blue, alpha);
|
||||
addVertex(consumer, pose, matrix, halfSize, halfSize, 1.0F, 0.0F, red, green, blue, alpha);
|
||||
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)
|
||||
.setColor(red, green, blue, alpha)
|
||||
.setUv(u, v)
|
||||
.setOverlay(OverlayTexture.NO_OVERLAY)
|
||||
.setLight(packedLight)
|
||||
.setLight(0xF000F0)
|
||||
.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);
|
||||
} catch (IllegalStateException exception) {
|
||||
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));
|
||||
}
|
||||
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) {
|
||||
GL30.glDeleteRenderbuffers(renderbuffer);
|
||||
throw exception;
|
||||
|
||||
@@ -101,8 +101,6 @@ public final class RoyalIndicatorRenderer {
|
||||
}
|
||||
|
||||
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++) {
|
||||
double angle = rotation + Math.PI * 0.5D * i;
|
||||
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.item.GiantFryingPanToolHelper;
|
||||
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.ultimate.UltimateGearHelper;
|
||||
import java.util.HashMap;
|
||||
@@ -427,6 +426,8 @@ public final class CreativeTabContents {
|
||||
entry("growth_ray", "combat"),
|
||||
entry("gravity_gun", "combat"),
|
||||
entry("portal_gun", "combat"),
|
||||
entry("portal_gun_atlas", "combat"),
|
||||
entry("portal_gun_p_body", "combat"),
|
||||
entry("eye_of_the_storm", "combat"),
|
||||
entry("water_cannon", "combat"),
|
||||
entry("attitude_adjuster", "combat"),
|
||||
@@ -641,14 +642,6 @@ public final class CreativeTabContents {
|
||||
}
|
||||
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()));
|
||||
}
|
||||
for (String potionPath : ANTARCHY_POTION_FAMILIES) {
|
||||
@@ -678,14 +671,6 @@ public final class CreativeTabContents {
|
||||
output.insertAfter(vanillaItem(entry.insertAfterPath()), item(entry.path()));
|
||||
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 (isUltimateArmor(entry.path())) {
|
||||
output.accept(UltimateGearHelper.createUltimateArmorStack(item(entry.path()), holders));
|
||||
|
||||
@@ -115,7 +115,6 @@ public class HushProjectileEntity extends ThrowableItemProjectile {
|
||||
return;
|
||||
}
|
||||
|
||||
// Smoothly steer toward the target each tick
|
||||
Vec3 desired = toTarget.normalize().scale(TRAVEL_SPEED);
|
||||
this.setDeltaMovement(this.getDeltaMovement().lerp(desired, 0.1D));
|
||||
}
|
||||
|
||||
@@ -167,7 +167,7 @@ public class MantisEntity extends Monster implements GeoEntity {
|
||||
MantisEntity.class,
|
||||
new AABB(pos).inflate(64.0D),
|
||||
Entity::isAlive
|
||||
).size() < 3;
|
||||
).size() < 4;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -284,9 +284,10 @@ public final class AntTeleportHelper {
|
||||
// The bounded search can fail near rough or sparsely-generated terrain.
|
||||
// Retry once with a wider radius before giving up.
|
||||
loadArrivalSearchArea(destination, preferredPos, EXPANDED_ARRIVAL_SEARCH_RADIUS);
|
||||
return findSafeArrivalPositionInYRange(
|
||||
Vec3 expandedSafePos = findSafeArrivalPositionInYRange(
|
||||
player, destination, preferredPos, yRange[0], yRange[1],
|
||||
EXPANDED_ARRIVAL_SEARCH_RADIUS, EXPANDED_ARRIVAL_VERTICAL_SEARCH);
|
||||
return expandedSafePos != null ? expandedSafePos : createEmergencyArrivalPlatform(destination, preferredPos, yRange[0], yRange[1]);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -141,9 +141,9 @@ public class BasiliskEntity extends Monster implements GeoEntity {
|
||||
}
|
||||
|
||||
return level.getDifficulty() != Difficulty.PEACEFUL
|
||||
&& pos.getY() >= 60 && pos.getY() <= 90
|
||||
&& pos.getY() >= 60 && pos.getY() <= 100
|
||||
&& 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);
|
||||
}
|
||||
|
||||
|
||||
@@ -277,12 +277,10 @@ public class BrutalflyEntity extends Monster implements GeoEntity {
|
||||
this.cocoonHitCooldownTicks--;
|
||||
}
|
||||
|
||||
// Pin position to hang from anchor
|
||||
if (this.anchorPos != null) {
|
||||
this.setPos(this.anchorPos.getX() + 0.5, this.anchorPos.getY() - 1.5, this.anchorPos.getZ() + 0.5);
|
||||
this.setDeltaMovement(Vec3.ZERO);
|
||||
|
||||
// Hatch immediately if anchor log was broken
|
||||
if (!this.isHatching && !this.level().getBlockState(this.anchorPos).is(BlockTags.LOGS)) {
|
||||
this.startHatching();
|
||||
return;
|
||||
@@ -303,7 +301,6 @@ public class BrutalflyEntity extends Monster implements GeoEntity {
|
||||
return;
|
||||
}
|
||||
|
||||
// Count down hit animation
|
||||
if (this.cocoonAnimTicks > 0) {
|
||||
this.cocoonAnimTicks--;
|
||||
if (this.cocoonAnimTicks <= 0) {
|
||||
@@ -312,7 +309,6 @@ public class BrutalflyEntity extends Monster implements GeoEntity {
|
||||
return;
|
||||
}
|
||||
|
||||
// Occasionally play idle animation
|
||||
if (this.getAnimationState() == ANIM_COCOON_IDLE) {
|
||||
// Will be reset to COCOON after idle ticks counted down
|
||||
} 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.client.model.ResourceBackedGeoItemModel;
|
||||
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.portalgun.PortalGunBlackHoleEntity;
|
||||
import com.craisinlord.antarchy.content.portalgun.PortalGunPlacement;
|
||||
import com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity;
|
||||
import com.craisinlord.antarchy.content.portalgun.PortalGunProjectileEntity;
|
||||
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.config.AntarchySettings;
|
||||
import java.util.HashSet;
|
||||
@@ -20,7 +22,6 @@ import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
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 MOON_SIDE_TAG = "antarchy.portal_gun_moon_side";
|
||||
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_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_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_HEIGHT = 16;
|
||||
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 final Supplier<? extends EntityType<? extends PortalGunPortalEntity>> portalType;
|
||||
private final Supplier<? extends EntityType<? extends PortalGunBlackHoleEntity>> blackHoleType;
|
||||
private final Supplier<? extends EntityType<? extends PortalGunProjectileEntity>> projectileType;
|
||||
private final Supplier<? extends Block> portalMasterBlock;
|
||||
private final Supplier<? extends Block> portalBaseBlock;
|
||||
private final PortalGunVariant variant;
|
||||
private final AnimatableInstanceCache geoCache = GeckoLibUtil.createInstanceCache(this);
|
||||
|
||||
public PortalGunItem(
|
||||
@@ -98,9 +95,11 @@ public class PortalGunItem extends Item implements GeoItem {
|
||||
Supplier<? extends EntityType<? extends PortalGunBlackHoleEntity>> blackHoleType,
|
||||
Supplier<? extends EntityType<? extends PortalGunProjectileEntity>> projectileType,
|
||||
Supplier<? extends Block> portalMasterBlock,
|
||||
Supplier<? extends Block> portalBaseBlock
|
||||
Supplier<? extends Block> portalBaseBlock,
|
||||
PortalGunVariant variant
|
||||
) {
|
||||
super(properties);
|
||||
this.variant = variant;
|
||||
this.portalType = portalType;
|
||||
this.blackHoleType = blackHoleType;
|
||||
this.projectileType = projectileType;
|
||||
@@ -109,6 +108,10 @@ public class PortalGunItem extends Item implements GeoItem {
|
||||
GeoItem.registerSyncedAnimatable(this);
|
||||
}
|
||||
|
||||
public PortalGunVariant getVariant() {
|
||||
return this.variant;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InteractionResultHolder<ItemStack> use(Level level, Player player, InteractionHand usedHand) {
|
||||
ItemStack stack = player.getItemInHand(usedHand);
|
||||
@@ -134,23 +137,21 @@ public class PortalGunItem extends Item implements GeoItem {
|
||||
}
|
||||
|
||||
public void resetPortals(ServerPlayer player, ItemStack stack) {
|
||||
UUID gunId = this.ensureGunId(stack, player.getUUID(), player.getGameProfile().getName());
|
||||
UUID ownerId = getPortalOwnerId(stack, player.getUUID());
|
||||
PortalGunSavedData.clearAllPortals(player.serverLevel(), ownerId, gunId, getChannelName(stack));
|
||||
UUID gunId = this.ensureGunId(stack, player.getUUID());
|
||||
PortalGunSavedData.clearAllPortals(player.serverLevel(), gunId);
|
||||
this.setLastSide(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);
|
||||
}
|
||||
|
||||
public void resetPortalSide(ServerPlayer player, ItemStack stack, PortalGunPortalEntity.PortalSide side) {
|
||||
UUID gunId = this.ensureGunId(stack, player.getUUID(), player.getGameProfile().getName());
|
||||
UUID ownerId = getPortalOwnerId(stack, player.getUUID());
|
||||
PortalGunSavedData.clearPortalSide(player.serverLevel(), ownerId, gunId, getChannelName(stack), side);
|
||||
UUID gunId = this.ensureGunId(stack, player.getUUID());
|
||||
PortalGunSavedData.clearPortalSide(player.serverLevel(), gunId, side);
|
||||
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) {
|
||||
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";
|
||||
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);
|
||||
@@ -163,7 +164,7 @@ public class PortalGunItem extends Item implements GeoItem {
|
||||
}
|
||||
}
|
||||
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);
|
||||
player.awardStat(Stats.ITEM_USED.get(this));
|
||||
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) {
|
||||
UUID portalOwnerId = getPortalOwnerId(stack, shooter.getUUID());
|
||||
String portalOwnerIdentity = getPortalOwnerIdentity(stack, shooter.getUUID());
|
||||
String channelName = getChannelName(stack);
|
||||
PortalGunPortalEntity existing = this.findPlacedPortal(level, portalOwnerId, gunId, channelName, side);
|
||||
PortalGunPlacement placement = this.findPlacement(level, shooter, portalOwnerId, channelName, side, hitResult, existing, portalWidth, portalHeight);
|
||||
PortalGunPortalEntity existing = this.findPlacedPortal(level, gunId, side);
|
||||
PortalGunPlacement placement = this.findPlacement(level, shooter, gunId, side, hitResult, existing, portalWidth, portalHeight);
|
||||
if (placement == null) {
|
||||
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);
|
||||
@@ -204,16 +203,16 @@ public class PortalGunItem extends Item implements GeoItem {
|
||||
return;
|
||||
}
|
||||
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);
|
||||
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());
|
||||
level.playSound(null, impactPos.x, impactPos.y, impactPos.z, PortalGunPortalEntity.sound("portal_gun_invalid_surface"), SoundSource.PLAYERS, 0.55F, 1.0F);
|
||||
return;
|
||||
}
|
||||
level.addFreshEntity(portal);
|
||||
PortalGunSavedData.setPortal(level, portalOwnerId, gunId, channelName, side, portal.getUUID());
|
||||
PortalGunPortalEntity other = this.findCounterpart(level, portalOwnerId, gunId, channelName, side);
|
||||
PortalGunSavedData.setPortal(level, gunId, side, portal.getUUID());
|
||||
PortalGunPortalEntity other = this.findCounterpart(level, gunId, side);
|
||||
if (other != null) {
|
||||
portal.linkTo(other);
|
||||
other.linkTo(portal);
|
||||
@@ -261,8 +260,7 @@ public class PortalGunItem extends Item implements GeoItem {
|
||||
if (!level.dimensionType().hasSkyLight() || !level.isNight() || player.getXRot() > -40.0F) {
|
||||
return false;
|
||||
}
|
||||
UUID ownerId = getPortalOwnerId(stack, player.getUUID());
|
||||
PortalGunPortalEntity existing = this.findPlacedPortal(level, ownerId, this.ensureGunId(stack, player.getUUID(), player.getGameProfile().getName()), getChannelName(stack), side);
|
||||
PortalGunPortalEntity existing = this.findPlacedPortal(level, this.ensureGunId(stack, player.getUUID()), side);
|
||||
if (existing != null) {
|
||||
existing.discard();
|
||||
}
|
||||
@@ -273,7 +271,7 @@ public class PortalGunItem extends Item implements GeoItem {
|
||||
player.awardStat(Stats.ITEM_USED.get(this));
|
||||
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);
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -301,10 +299,10 @@ public class PortalGunItem extends Item implements GeoItem {
|
||||
}
|
||||
|
||||
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);
|
||||
projectile.configure(side, gunId, stack);
|
||||
Vec3 look = shooter.getLookAngle();
|
||||
Vec3 look = this.worldLook(shooter);
|
||||
Vec3 start = shooter.getEyePosition();
|
||||
double launchSpeed = PortalGunProjectileEntity.MIN_LAUNCH_SPEED + level.getRandom().nextDouble() * PortalGunProjectileEntity.LAUNCH_SPEED_VARIANCE;
|
||||
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);
|
||||
}
|
||||
|
||||
public static int getGrabStrength(ItemStack stack) {
|
||||
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) {
|
||||
private PortalGunPortalEntity findCounterpart(ServerLevel level, UUID gunId, PortalGunPortalEntity.PortalSide side) {
|
||||
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()) {
|
||||
return null;
|
||||
}
|
||||
@@ -339,8 +332,8 @@ public class PortalGunItem extends Item implements GeoItem {
|
||||
return entity instanceof PortalGunPortalEntity portal ? portal : null;
|
||||
}
|
||||
|
||||
private PortalGunPortalEntity findPlacedPortal(ServerLevel level, UUID owner, UUID gunId, String channelName, PortalGunPortalEntity.PortalSide side) {
|
||||
Optional<UUID> portalId = PortalGunSavedData.getPortalId(level.getServer(), owner, gunId, channelName, side, level.dimension().location());
|
||||
private PortalGunPortalEntity findPlacedPortal(ServerLevel level, UUID gunId, PortalGunPortalEntity.PortalSide side) {
|
||||
Optional<UUID> portalId = PortalGunSavedData.getPortalId(level.getServer(), gunId, side, level.dimension().location());
|
||||
if (portalId.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
@@ -354,11 +347,19 @@ public class PortalGunItem extends Item implements GeoItem {
|
||||
|
||||
private BlockHitResult raycast(Level level, Player player) {
|
||||
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));
|
||||
}
|
||||
|
||||
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 maxHeight = Mth.clamp(requestedHeight, 2, 16);
|
||||
boolean canResize = AntarchySettings.portalGunCanPortalsResizeWhenCreated();
|
||||
@@ -367,7 +368,7 @@ public class PortalGunItem extends Item implements GeoItem {
|
||||
if (!canResize && (width != maxWidth || height != maxHeight)) {
|
||||
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) {
|
||||
return placement;
|
||||
}
|
||||
@@ -376,7 +377,7 @@ public class PortalGunItem extends Item implements GeoItem {
|
||||
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 heightAxis = this.resolveUpAxis(shooter, facing);
|
||||
Direction widthAxis = PortalGunPlacement.widthAxis(facing, heightAxis);
|
||||
@@ -410,7 +411,7 @@ public class PortalGunItem extends Item implements GeoItem {
|
||||
BlockState supportState = level.getBlockState(supportPos);
|
||||
BlockState portalState = level.getBlockState(portalPos);
|
||||
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;
|
||||
break;
|
||||
}
|
||||
@@ -505,13 +506,13 @@ public class PortalGunItem extends Item implements GeoItem {
|
||||
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);
|
||||
if (blockEntity instanceof PortalGunPortalCellAccess portalCell) {
|
||||
PortalGunPortalFaceRecord record = portalCell.portalGun$getFaceRecord(face);
|
||||
if (record == null) {
|
||||
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) {
|
||||
BlockEntity masterEntity = level.getBlockEntity(base.getMasterPos());
|
||||
@@ -519,23 +520,21 @@ public class PortalGunItem extends Item implements GeoItem {
|
||||
return false;
|
||||
}
|
||||
return master.getFacing() != face
|
||||
|| (ownerId != null
|
||||
&& ownerId.equals(base.getOwnerId())
|
||||
&& channelName.equals(master.getChannelName())
|
||||
|| (gunId != null
|
||||
&& gunId.equals(master.getGunId())
|
||||
&& base.getSide() == side
|
||||
&& master.getSide() == side);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return ownerId != null
|
||||
&& ownerId.equals(record.ownerId())
|
||||
&& channelName.equals(record.channelName())
|
||||
return gunId != null
|
||||
&& gunId.equals(record.gunId())
|
||||
&& record.side() == side;
|
||||
}
|
||||
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();
|
||||
for (int i = 0; i < placement.portalSpots().length; i++) {
|
||||
BlockPos spot = placement.portalSpots()[i];
|
||||
@@ -557,9 +556,8 @@ public class PortalGunItem extends Item implements GeoItem {
|
||||
if (!(existing instanceof PortalGunPortalCellAccess cell)
|
||||
|| !cell.portalGun$putFaceRecord(new PortalGunPortalFaceRecord(
|
||||
ownerId,
|
||||
ownerIdentity,
|
||||
gunId,
|
||||
channelName,
|
||||
this.variant,
|
||||
portal.getUUID(),
|
||||
portal.getLinkedPortalId(),
|
||||
side,
|
||||
@@ -578,7 +576,7 @@ public class PortalGunItem extends Item implements GeoItem {
|
||||
}
|
||||
if (masterRole && existing instanceof PortalGunPortalMasterBlockEntity master
|
||||
&& (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
|
||||
&& (base.getPortalId() == null || base.getPortalId().equals(portal.getUUID()))) {
|
||||
base.configure(ownerId, portal.getUUID(), side, placement.masterPos());
|
||||
@@ -656,12 +654,8 @@ public class PortalGunItem extends Item implements GeoItem {
|
||||
} else {
|
||||
gunId = UUID.randomUUID();
|
||||
}
|
||||
String channelName = "Random Channel #" + gunId.hashCode();
|
||||
net.minecraft.world.item.component.CustomData.update(DataComponents.CUSTOM_DATA, stack, tag -> {
|
||||
tag.putUUID(GUN_ID_TAG, gunId);
|
||||
if (!tag.contains(CHANNEL_NAME_TAG)) {
|
||||
tag.putString(CHANNEL_NAME_TAG, channelName);
|
||||
}
|
||||
if (!tag.contains(LAST_SIDE_TAG)) {
|
||||
tag.putString(LAST_SIDE_TAG, PortalGunPortalEntity.PortalSide.BLUE.name());
|
||||
}
|
||||
@@ -669,30 +663,16 @@ public class PortalGunItem extends Item implements GeoItem {
|
||||
return gunId;
|
||||
}
|
||||
|
||||
private UUID ensureGunId(ItemStack stack, UUID ownerId, String ownerName) {
|
||||
private UUID ensureGunId(ItemStack stack, UUID ownerId) {
|
||||
UUID gunId = this.ensureGunId(stack);
|
||||
net.minecraft.world.item.component.CustomData.update(DataComponents.CUSTOM_DATA, stack, tag -> {
|
||||
if (!tag.contains(OWNER_ID_TAG)) {
|
||||
tag.putString(OWNER_ID_TAG, ownerId.toString());
|
||||
tag.putString(OWNER_NAME_TAG, ownerName);
|
||||
}
|
||||
});
|
||||
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) {
|
||||
if (stack.isEmpty() || !(stack.getItem() instanceof PortalGunItem item)) {
|
||||
return null;
|
||||
@@ -704,7 +684,7 @@ public class PortalGunItem extends Item implements GeoItem {
|
||||
if (stack.isEmpty() || !(stack.getItem() instanceof PortalGunItem item)) {
|
||||
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) {
|
||||
@@ -713,9 +693,6 @@ public class PortalGunItem extends Item implements GeoItem {
|
||||
return fallback;
|
||||
}
|
||||
String owner = customData.copyTag().getString(OWNER_ID_TAG);
|
||||
if ("Global".equals(owner)) {
|
||||
return GLOBAL_OWNER_ID;
|
||||
}
|
||||
try {
|
||||
return owner.isEmpty() ? fallback : UUID.fromString(owner);
|
||||
} 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) {
|
||||
if (stack.isEmpty() || stack.getItem() != this) {
|
||||
return false;
|
||||
@@ -767,21 +718,16 @@ public class PortalGunItem extends Item implements GeoItem {
|
||||
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
|
||||
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.secondary").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.primary", this.portalColorName(PortalGunPortalEntity.PortalSide.BLUE)).withStyle(ChatFormatting.GRAY));
|
||||
tooltipComponents.add(Component.translatable("tooltip.antarchy.portal_gun.secondary", this.portalColorName(PortalGunPortalEntity.PortalSide.ORANGE)).withStyle(ChatFormatting.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
|
||||
@@ -809,7 +755,7 @@ public class PortalGunItem extends Item implements GeoItem {
|
||||
@Override
|
||||
public net.minecraft.client.renderer.BlockEntityWithoutLevelRenderer getGeoItemRenderer() {
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
PortalGunWorldPortalShape shape = portal.getWorldPortalShape();
|
||||
if (canWalkIntoFloorPortal(startBox, movement, portal, shape)) {
|
||||
return movement;
|
||||
}
|
||||
Vec3 normal = portal.getNormalVec().normalize();
|
||||
double desiredNormal = movement.dot(normal);
|
||||
double collidedNormal = collided.dot(normal);
|
||||
@@ -60,29 +57,6 @@ public final class PortalGunCollisionHelper {
|
||||
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) {
|
||||
AABB endBox = startBox.move(movement);
|
||||
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()) {
|
||||
return;
|
||||
}
|
||||
UUID ownerId = PortalGunItem.getPortalOwnerId(stack, player.getUUID());
|
||||
boolean blue = PortalGunSavedData.getPortalId(
|
||||
player.serverLevel().getServer(),
|
||||
ownerId,
|
||||
request.gunId(),
|
||||
PortalGunItem.getChannelName(stack),
|
||||
PortalGunPortalEntity.PortalSide.BLUE,
|
||||
player.serverLevel().dimension().location()
|
||||
).isPresent();
|
||||
boolean orange = PortalGunSavedData.getPortalId(
|
||||
player.serverLevel().getServer(),
|
||||
ownerId,
|
||||
request.gunId(),
|
||||
PortalGunItem.getChannelName(stack),
|
||||
PortalGunPortalEntity.PortalSide.ORANGE,
|
||||
player.serverLevel().dimension().location()
|
||||
).isPresent();
|
||||
|
||||
@@ -1,22 +1,26 @@
|
||||
package com.craisinlord.antarchy.content.portalgun;
|
||||
|
||||
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.PortalGunPortalCellAccess;
|
||||
import com.craisinlord.antarchy.content.block.entity.PortalGunPortalFaceRecord;
|
||||
import com.craisinlord.antarchy.content.block.entity.PortalGunPortalMasterBlockEntity;
|
||||
import com.craisinlord.antarchy.mixins.AbstractArrowAccessor;
|
||||
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.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.awt.Color;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
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.player.Player;
|
||||
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.level.Level;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.phys.AABB;
|
||||
import net.minecraft.world.phys.BlockHitResult;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import software.bernie.geckolib.animatable.GeoEntity;
|
||||
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<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<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> 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<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> 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> VARIANT = 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> 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;
|
||||
private static final String DEBUG_TRAVERSAL_PROPERTY = "antarchy.portalGun.debugTraversal";
|
||||
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 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 Map<UUID, Integer> teleportCooldowns = new HashMap<>();
|
||||
private UUID ownerId;
|
||||
private String ownerIdentity;
|
||||
private UUID gunId;
|
||||
private String channelName;
|
||||
private UUID linkedPortalId;
|
||||
private int ageTicks;
|
||||
private int lastOpeningAnimationId;
|
||||
@@ -98,6 +101,12 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity {
|
||||
private BlockPos basePos = BlockPos.ZERO;
|
||||
private BlockPos[] portalSpots = new BlockPos[] {BlockPos.ZERO, BlockPos.ZERO};
|
||||
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) {
|
||||
super(entityType, level);
|
||||
@@ -111,36 +120,27 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity {
|
||||
builder.define(SIDE, PortalSide.BLUE.ordinal());
|
||||
builder.define(OWNER_ID, Optional.empty());
|
||||
builder.define(GUN_ID, Optional.empty());
|
||||
builder.define(CHANNEL_NAME, "");
|
||||
builder.define(FACING, Direction.NORTH.get3DDataValue());
|
||||
builder.define(UP_AXIS, Direction.UP.get3DDataValue());
|
||||
builder.define(LINKED_PORTAL, Optional.empty());
|
||||
builder.define(LINKED_PORTAL_ENTITY_ID, -1);
|
||||
builder.define(CHANNEL_RED, 58);
|
||||
builder.define(CHANNEL_GREEN, 166);
|
||||
builder.define(CHANNEL_BLUE, 255);
|
||||
builder.define(VARIANT, PortalGunVariant.DEFAULT.id());
|
||||
builder.define(PORTAL_WIDTH, 1);
|
||||
builder.define(PORTAL_HEIGHT, 2);
|
||||
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.ownerIdentity = ownerIdentity;
|
||||
this.gunId = gunId;
|
||||
this.channelName = channelName;
|
||||
this.entityData.set(OWNER_ID, Optional.ofNullable(ownerId));
|
||||
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(FACING, placement.facing().get3DDataValue());
|
||||
this.entityData.set(UP_AXIS, placement.upAxis().get3DDataValue());
|
||||
this.entityData.set(PORTAL_WIDTH, placement.width());
|
||||
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.masterPos = placement.masterPos().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);
|
||||
}
|
||||
|
||||
public String getChannelName() {
|
||||
String synchronizedName = this.entityData.get(CHANNEL_NAME);
|
||||
return synchronizedName.isEmpty() ? this.channelName : synchronizedName;
|
||||
public PortalGunVariant getVariant() {
|
||||
return PortalGunVariant.byId(this.entityData.get(VARIANT));
|
||||
}
|
||||
|
||||
public void adoptChannelIdentity(UUID gunId, String channelName) {
|
||||
if (this.gunId == null && gunId != null) {
|
||||
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 int getPortalColor() {
|
||||
return this.getVariant().color(this.getPortalSide());
|
||||
}
|
||||
|
||||
public BlockPos getBasePos() {
|
||||
@@ -278,17 +240,10 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity {
|
||||
if (linkedId == null) {
|
||||
return null;
|
||||
}
|
||||
Entity linkedEntity;
|
||||
if (this.level() instanceof ServerLevel serverLevel) {
|
||||
linkedEntity = serverLevel.getEntity(linkedId);
|
||||
} else {
|
||||
int linkedEntityId = this.entityData.get(LINKED_PORTAL_ENTITY_ID);
|
||||
if (linkedEntityId < 0) {
|
||||
return null;
|
||||
return serverLevel.getEntity(linkedId) instanceof PortalGunPortalEntity portal && !portal.isRemoved() ? portal : null;
|
||||
}
|
||||
linkedEntity = this.level().getEntity(linkedEntityId);
|
||||
}
|
||||
return linkedEntity instanceof PortalGunPortalEntity portal && !portal.isRemoved() ? portal : null;
|
||||
return PortalGunPortalRegistry.find(this.level(), linkedId);
|
||||
}
|
||||
|
||||
public Vec3 getNormalVec() {
|
||||
@@ -304,7 +259,23 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity {
|
||||
}
|
||||
|
||||
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() {
|
||||
@@ -354,72 +325,66 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity {
|
||||
return entity instanceof LivingEntity living ? living.getEyeHeight(living.getPose()) : entity.getEyeHeight();
|
||||
}
|
||||
|
||||
public PortalCrossing resolveCrossing(Entity entity, AABB previousBox, AABB currentBox) {
|
||||
Vec3 previousProbe = this.teleportProbePosition(entity, previousBox);
|
||||
Vec3 currentProbe = this.teleportProbePosition(entity, currentBox);
|
||||
if (entity instanceof Player) {
|
||||
public PortalCrossing resolveCrossing(Entity entity, AABB previousBox, AABB currentBox, Vec3 attemptedMovement) {
|
||||
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()) {
|
||||
if (!PortalGunTransitMath.crossesWithBody(shape, previousBox, currentBox, attemptedMovement)) {
|
||||
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) {
|
||||
AABB currentBox = entity.getBoundingBox();
|
||||
AABB previousBox = currentBox.move(entity.xo - entity.getX(), entity.yo - entity.getY(), entity.zo - entity.getZ());
|
||||
PortalCrossing crossing = this.resolveCrossing(entity, previousBox, currentBox);
|
||||
return crossing == null ? null : crossing.probe();
|
||||
public PortalTransit computeTransit(PortalGunPortalEntity destination, AABB box, Vec3 velocity, Vec3 look) {
|
||||
PortalGunWorldPortalShape source = this.getWorldPortalShape();
|
||||
PortalGunWorldPortalShape target = destination.getWorldPortalShape();
|
||||
AABB exitBox = PortalGunTransitMath.exitBox(source, target, box);
|
||||
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) {
|
||||
Vec3 sourcePlaneCenter = this.position().add(this.getNormalVec().normalize().scale(crossing.normalOffset()));
|
||||
Vec3 destinationPlaneCenter = destination.position().add(destination.getNormalVec().normalize().scale(crossing.normalOffset()));
|
||||
Vec3 relativeProbe = crossing.probe().subtract(sourcePlaneCenter);
|
||||
Vec3 transformedProbe = PortalGunTransformUtil.transformPosition(this, destination, relativeProbe);
|
||||
Vec3 entityOffset = PortalGunTransformUtil.transformVector(this, destination, entity.position().subtract(crossing.probe()));
|
||||
Vec3 requestedPosition = destinationPlaneCenter.add(transformedProbe).add(entityOffset);
|
||||
Vec3 transformedMovement = PortalGunTransformUtil.transformVector(this, destination, movement);
|
||||
Vec3 exitPosition = putEntityWithinExitBounds(entity, requestedPosition, legacyExitBounds(destination, entity, destination.getNormalVec().normalize()));
|
||||
return new PortalTransitTransform(requestedPosition, exitPosition, transformedMovement);
|
||||
public PortalTransit computeTransit(Entity entity, PortalGunPortalEntity destination, AABB box, Vec3 velocity, Vec3 look) {
|
||||
if (!AntarchyGravityApi.isGravityInverted(entity)) {
|
||||
return computeTransit(destination, box, velocity, look);
|
||||
}
|
||||
PortalGunWorldPortalShape source = this.getWorldPortalShape();
|
||||
PortalGunWorldPortalShape target = destination.getWorldPortalShape();
|
||||
AABB exitBox = PortalGunTransitMath.exitBox(source, target, box);
|
||||
Vec3 worldVelocity = PortalGunTransitMath.entityVelocityToWorld(entity, velocity);
|
||||
Vec3 worldExitVelocity = PortalGunTransitMath.exitVelocity(source, target, worldVelocity,
|
||||
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) {
|
||||
if (entity instanceof ServerPlayer) {
|
||||
public static void teleportEntityAfterMovement(Entity entity, AABB previousBox, Vec3 velocity) {
|
||||
if (entity instanceof Player) {
|
||||
return;
|
||||
}
|
||||
teleportAfterMovement(entity, previousBox, entity.getBoundingBox(), resolvedMovement);
|
||||
teleportAfterMovement(entity, previousBox, entity.getBoundingBox(), velocity);
|
||||
}
|
||||
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
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)) {
|
||||
if (!(portal.level() instanceof ServerLevel serverLevel) || !portal.canTeleportEntity(entity)) {
|
||||
continue;
|
||||
@@ -428,28 +393,132 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity {
|
||||
if (linked == null || !linked.isAlive()) {
|
||||
continue;
|
||||
}
|
||||
PortalCrossing crossing = portal.resolveCrossing(entity, previousBox, currentBox);
|
||||
PortalCrossing crossing = portal.resolveCrossing(entity, previousBox, currentBox, attemptedMovement);
|
||||
if (crossing != null) {
|
||||
if (entity instanceof Player && Boolean.getBoolean(DEBUG_TRAVERSAL_PROPERTY)) {
|
||||
Antarchy.LOGGER.info("Portal gun traversal accepted path=movement portal={} player={} probe={} offset={} linked={}",
|
||||
portal.getUUID(), entity.getUUID(), crossing.probe(), crossing.normalOffset(), linked.getUUID());
|
||||
Antarchy.LOGGER.info("Portal gun traversal accepted path=server portal={} player={} probe={} linked={}",
|
||||
portal.getUUID(), entity.getUUID(), crossing.probe(), linked.getUUID());
|
||||
}
|
||||
logCrossingPath(entity, "movement");
|
||||
portal.teleportEntity(entity, linked, crossing.probe(), resolvedMovement, crossing.normalOffset());
|
||||
portal.teleportEntity(entity, linked, currentBox, velocity, false);
|
||||
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) {
|
||||
List<PortalGunPortalEntity> portals = new java.util.ArrayList<>();
|
||||
AABB searchBounds = bounds.inflate(10.0D);
|
||||
for (PortalGunPortalEntity portal : level.getEntitiesOfClass(PortalGunPortalEntity.class, searchBounds, PortalGunPortalEntity::isAlive)) {
|
||||
if (portal.getWorldPortalShape().getBoundsForCulling().intersects(bounds)) {
|
||||
List<PortalGunPortalEntity> portals = null;
|
||||
for (PortalGunPortalEntity portal : PortalGunPortalRegistry.all(level)) {
|
||||
if (portal.isAlive() && portal.getWorldPortalShape().getBoundsForCulling().intersects(bounds)) {
|
||||
if (portals == null) {
|
||||
portals = new java.util.ArrayList<>(2);
|
||||
}
|
||||
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) {
|
||||
@@ -490,6 +559,9 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity {
|
||||
@Override
|
||||
public void tick() {
|
||||
super.tick();
|
||||
if (!this.isRemoved()) {
|
||||
PortalGunPortalRegistry.track(this);
|
||||
}
|
||||
int openingAnimationId = this.entityData.get(OPENING_ANIMATION_ID);
|
||||
if (openingAnimationId != this.lastOpeningAnimationId) {
|
||||
this.lastOpeningAnimationId = openingAnimationId;
|
||||
@@ -505,16 +577,8 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity {
|
||||
if (!(this.level() instanceof ServerLevel serverLevel)) {
|
||||
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();
|
||||
if (this.ownerId == null || !this.isStillRegistered(serverLevel)) {
|
||||
if (this.gunId == null || !this.isStillRegistered(serverLevel)) {
|
||||
this.discard();
|
||||
return;
|
||||
}
|
||||
@@ -540,7 +604,7 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity {
|
||||
}
|
||||
|
||||
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() {
|
||||
@@ -576,36 +640,15 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity {
|
||||
}
|
||||
|
||||
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) {
|
||||
AABB currentBox = entity.getBoundingBox();
|
||||
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 (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;
|
||||
}
|
||||
if (entity instanceof Player && Boolean.getBoolean(DEBUG_TRAVERSAL_PROPERTY)) {
|
||||
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);
|
||||
this.teleportEntity(entity, linked, currentBox, entity.getDeltaMovement(), false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -620,56 +663,81 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity {
|
||||
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<>();
|
||||
collectTeleportGroup(entity, null, group);
|
||||
Vec3 rootOriginalPosition = entity.position();
|
||||
PortalTransitTransform transitTransform = this.previewTransit(entity, destination, new PortalCrossing(currentProbe, normalOffset), entryMotion);
|
||||
Vec3 exitNormal = destination.getNormalVec().normalize();
|
||||
Vec3 requestedExitPos = transitTransform.requestedPosition();
|
||||
Vec3 exitPos = transitTransform.exitPosition();
|
||||
Vec3 transformedMotion = transitTransform.movement();
|
||||
PortalTransit transit = this.computeTransit(entity, destination, crossingBox, velocity, entity.getLookAngle());
|
||||
Vec3 exitPos = transit.position();
|
||||
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) {
|
||||
Entity member = state.entity();
|
||||
member.stopRiding();
|
||||
state.entity().stopRiding();
|
||||
}
|
||||
for (EntityTeleportState state : group) {
|
||||
Entity member = state.entity();
|
||||
Vec3 memberOffset = PortalGunTransformUtil.transformVector(this, destination, state.position().subtract(rootOriginalPosition));
|
||||
Vec3 memberExitPos = putEntityWithinExitBounds(member, requestedExitPos.add(memberOffset), legacyExitBounds(destination, member, exitNormal));
|
||||
Vec3 memberLook = PortalGunTransformUtil.transformVector(this, destination, state.look()).normalize();
|
||||
Vec3 memberMotion = member == entity
|
||||
? transformedMotion
|
||||
: PortalGunTransformUtil.transformVector(this, destination, state.motion());
|
||||
boolean root = member == entity;
|
||||
Vec3 memberExitPos = root
|
||||
? exitPos
|
||||
: exitPos.add(PortalGunTransformUtil.transformVector(this, destination, state.position().subtract(rootOriginalPosition)));
|
||||
Vec3 memberLook;
|
||||
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 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.setDeltaMovement(memberMotion);
|
||||
player.hasImpulse = true;
|
||||
player.connection.send(new ClientboundSetEntityMotionPacket(player));
|
||||
} else {
|
||||
member.teleportTo(memberExitPos.x, memberExitPos.y, memberExitPos.z);
|
||||
member.setYRot(yaw);
|
||||
member.setXRot(pitch);
|
||||
member.setYHeadRot(yaw);
|
||||
member.setYBodyRot(yaw);
|
||||
}
|
||||
member.setDeltaMovement(memberMotion);
|
||||
member.hasImpulse = true;
|
||||
member.hurtMarked = true;
|
||||
if (member instanceof ServerPlayer player) {
|
||||
player.connection.send(new ClientboundSetEntityMotionPacket(player));
|
||||
}
|
||||
member.xo = member.getX();
|
||||
member.yo = member.getY();
|
||||
member.zo = member.getZ();
|
||||
member.yRotO = yaw;
|
||||
member.xRotO = pitch;
|
||||
if (member instanceof Projectile) {
|
||||
orientAlongVelocity(member, memberMotion);
|
||||
}
|
||||
member.setOnGround(false);
|
||||
member.horizontalCollision = false;
|
||||
member.verticalCollision = false;
|
||||
member.verticalCollisionBelow = false;
|
||||
float verticalMotion = (float) memberMotion.y;
|
||||
member.fallDistance = 0.1F * (verticalMotion / -0.1F * verticalMotion / -0.1F);
|
||||
member.fallDistance = fallDistance;
|
||||
this.applyTeleportCooldown(member, destination);
|
||||
this.handleSpecialEntityPostTeleport(member);
|
||||
}
|
||||
@@ -684,12 +752,22 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity {
|
||||
if (!(member instanceof ServerPlayer)) {
|
||||
destinationLevel.getChunkSource().broadcastAndSend(member, new ClientboundTeleportEntityPacket(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);
|
||||
}
|
||||
|
||||
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) {
|
||||
group.add(new EntityTeleportState(entity, vehicle, entity.position(), entity.getLookAngle(), entity.getDeltaMovement()));
|
||||
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() {
|
||||
Iterator<Map.Entry<UUID, Integer>> iterator = this.teleportCooldowns.entrySet().iterator();
|
||||
while (iterator.hasNext()) {
|
||||
@@ -783,15 +830,9 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity {
|
||||
if (this.ownerId != null) {
|
||||
tag.putUUID("OwnerId", this.ownerId);
|
||||
}
|
||||
if (this.ownerIdentity != null) {
|
||||
tag.putString("OwnerIdentity", this.ownerIdentity);
|
||||
}
|
||||
if (this.gunId != null) {
|
||||
tag.putUUID("GunId", this.gunId);
|
||||
}
|
||||
if (this.channelName != null) {
|
||||
tag.putString("ChannelName", this.channelName);
|
||||
}
|
||||
if (this.linkedPortalId != null) {
|
||||
tag.putUUID("LinkedPortalId", this.linkedPortalId);
|
||||
}
|
||||
@@ -800,9 +841,7 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity {
|
||||
tag.putInt("Side", this.getPortalSide().ordinal());
|
||||
tag.putInt("Facing", this.getFacingDirection().get3DDataValue());
|
||||
tag.putInt("UpAxis", this.getUpAxis().get3DDataValue());
|
||||
tag.putInt("ChannelRed", this.getChannelRed());
|
||||
tag.putInt("ChannelGreen", this.getChannelGreen());
|
||||
tag.putInt("ChannelBlue", this.getChannelBlue());
|
||||
tag.putInt("Variant", this.getVariant().id());
|
||||
tag.putInt("PortalWidth", this.getPortalWidth());
|
||||
tag.putInt("PortalHeight", this.getPortalHeight());
|
||||
tag.putInt("SupportX", this.supportOrigin.getX());
|
||||
@@ -839,12 +878,9 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity {
|
||||
if (tag.hasUUID("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.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(GUN_ID, Optional.ofNullable(this.gunId));
|
||||
this.entityData.set(CHANNEL_NAME, this.channelName);
|
||||
if (tag.hasUUID("LinkedPortalId")) {
|
||||
this.linkedPortalId = tag.getUUID("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(FACING, tag.getInt("Facing"));
|
||||
this.entityData.set(UP_AXIS, tag.getInt("UpAxis"));
|
||||
if (this.ownerId != null && ("Global".equals(this.ownerIdentity) || !tag.contains("ChannelRed"))) {
|
||||
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.entityData.set(VARIANT, PortalGunVariant.byId(tag.getInt("Variant")).id());
|
||||
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.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;
|
||||
boolean destroyPortal = reason.shouldDestroy();
|
||||
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);
|
||||
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));
|
||||
}
|
||||
|
||||
private static int[] generateChannelColor(String ownerIdentity, String channelName, PortalSide side) {
|
||||
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) {
|
||||
}
|
||||
|
||||
public record PortalCrossing(Vec3 probe, double normalOffset) {
|
||||
}
|
||||
|
||||
public record PortalTransitTransform(Vec3 requestedPosition, Vec3 exitPosition, Vec3 movement) {
|
||||
public record PortalTransit(AABB exitBox, Vec3 position, Vec3 velocity, Vec3 look) {
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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 java.util.Comparator;
|
||||
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<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 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 LAUNCH_SPEED_VARIANCE = 0.02D;
|
||||
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) {
|
||||
AntarchyGravityApi.setAirborneGravityDirection(this, AntarchyGravityDirection.DOWN, false, AntarchyGravityTransition.INSTANT);
|
||||
this.entityData.set(SIDE, side.ordinal());
|
||||
this.gunId = gunId;
|
||||
this.setItem(gunStack.copyWithCount(1));
|
||||
@@ -134,6 +142,10 @@ public class PortalGunProjectileEntity extends ThrowableItemProjectile {
|
||||
return this.entityData.get(PROJECTILE_SPEED);
|
||||
}
|
||||
|
||||
public PortalGunVariant getVariant() {
|
||||
return this.getItem().getItem() instanceof PortalGunItem portalGun ? portalGun.getVariant() : PortalGunVariant.DEFAULT;
|
||||
}
|
||||
|
||||
public PortalGunPortalEntity.PortalSide getPortalSide() {
|
||||
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();
|
||||
if (this.getPortalSide() == PortalGunPortalEntity.PortalSide.BLUE) {
|
||||
this.level().addParticle(net.minecraft.core.particles.ParticleTypes.SOUL_FIRE_FLAME, point.x, point.y, point.z, 0.0D, 0.0D, 0.0D);
|
||||
this.level().addParticle(net.minecraft.core.particles.ParticleTypes.END_ROD, point.x, point.y, point.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);
|
||||
for (int i = 0; i < TRAIL_PARTICLES_PER_TICK; i++) {
|
||||
Vec3 trailPoint = point.subtract(motion.scale((double) i / TRAIL_PARTICLES_PER_TICK));
|
||||
this.level().addParticle(dust, trailPoint.x, trailPoint.y, trailPoint.z, 0.0D, 0.0D, 0.0D);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -368,13 +378,13 @@ public class PortalGunProjectileEntity extends ThrowableItemProjectile {
|
||||
}
|
||||
|
||||
private void spawnImpactParticles(ServerLevel level, Vec3 impactPos) {
|
||||
if (this.getPortalSide() == PortalGunPortalEntity.PortalSide.BLUE) {
|
||||
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;
|
||||
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.FLAME, impactPos.x, impactPos.y, impactPos.z, 10, 0.08D, 0.08D, 0.08D, 0.01D);
|
||||
level.sendParticles(net.minecraft.core.particles.ParticleTypes.CRIT, impactPos.x, impactPos.y, impactPos.z, 6, 0.04D, 0.04D, 0.04D, 0.01D);
|
||||
|
||||
private net.minecraft.core.particles.DustParticleOptions trailDust(float scale) {
|
||||
int color = this.getVariant().color(this.getPortalSide());
|
||||
return new net.minecraft.core.particles.DustParticleOptions(
|
||||
new org.joml.Vector3f((color >> 16 & 0xFF) / 255.0F, (color >> 8 & 0xFF) / 255.0F, (color & 0xFF) / 255.0F), scale);
|
||||
}
|
||||
|
||||
private void syncSpawnPosition(Vec3 pos) {
|
||||
|
||||
@@ -2,9 +2,7 @@ package com.craisinlord.antarchy.content.portalgun;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
@@ -18,17 +16,13 @@ import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.level.saveddata.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 OWNER_KEY = "Owner";
|
||||
private static final String DIMENSION_KEY = "Dimension";
|
||||
private static final String GUN_KEY = "Gun";
|
||||
private static final String BLUE_KEY = "Blue";
|
||||
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<UUID, PairRecord> legacyPairs = new HashMap<>();
|
||||
private final Map<OwnerChannelKey, UUID> channelGunIds = new HashMap<>();
|
||||
|
||||
public static PortalGunSavedData create() {
|
||||
return new PortalGunSavedData();
|
||||
@@ -38,23 +32,13 @@ public final class PortalGunSavedData extends SavedData {
|
||||
PortalGunSavedData data = new PortalGunSavedData();
|
||||
for (net.minecraft.nbt.Tag rawEntry : tag.getList(ENTRIES_KEY, net.minecraft.nbt.Tag.TAG_COMPOUND)) {
|
||||
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 orange = entryTag.hasUUID(ORANGE_KEY) ? entryTag.getUUID(ORANGE_KEY) : null;
|
||||
PairRecord record = 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));
|
||||
}
|
||||
data.pairs.put(new PairKey(dimension, entryTag.getUUID(GUN_KEY)), new PairRecord(blue, orange));
|
||||
}
|
||||
return data;
|
||||
}
|
||||
@@ -64,42 +48,20 @@ public final class PortalGunSavedData extends SavedData {
|
||||
ListTag entries = new ListTag();
|
||||
for (Map.Entry<PairKey, PairRecord> entry : this.pairs.entrySet()) {
|
||||
CompoundTag entryTag = new CompoundTag();
|
||||
entryTag.putUUID(OWNER_KEY, entry.getKey().owner());
|
||||
entryTag.putString(DIMENSION_KEY, entry.getKey().dimension().toString());
|
||||
if (entry.getKey().gunId() != null) {
|
||||
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);
|
||||
}
|
||||
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(CHANNELS_KEY, channels);
|
||||
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) {
|
||||
return server.overworld().getDataStorage().computeIfAbsent(
|
||||
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) {
|
||||
PortalGunSavedData data = get(server);
|
||||
UUID resolvedGunId = data.resolveChannelGunId(owner, channelName, gunId);
|
||||
data.migrateLegacyPair(server, owner);
|
||||
data.adoptLegacyPairs(server, owner, resolvedGunId, channelName);
|
||||
PairRecord record = data.pairs.get(new PairKey(owner, dimension, resolvedGunId));
|
||||
public static Optional<UUID> getPortalId(MinecraftServer server, UUID gunId, PortalGunPortalEntity.PortalSide side, ResourceLocation dimension) {
|
||||
if (gunId == null) {
|
||||
return Optional.empty();
|
||||
}
|
||||
PairRecord record = get(server).pairs.get(new PairKey(dimension, gunId));
|
||||
if (record == null) {
|
||||
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());
|
||||
}
|
||||
|
||||
public static Optional<UUID> getPortalId(MinecraftServer server, UUID owner, UUID gunId, PortalGunPortalEntity.PortalSide side, ResourceLocation dimension) {
|
||||
return getPortalId(server, owner, gunId, String.valueOf(gunId), side, dimension);
|
||||
public static boolean isRegistered(ServerLevel level, UUID gunId, PortalGunPortalEntity.PortalSide side, UUID portalId) {
|
||||
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) {
|
||||
PortalGunSavedData data = get(server);
|
||||
data.migrateLegacyPair(server, owner);
|
||||
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());
|
||||
public static void setPortal(ServerLevel level, UUID gunId, PortalGunPortalEntity.PortalSide side, UUID portalId) {
|
||||
if (gunId == null) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
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());
|
||||
ResourceLocation dimension = level.dimension().location();
|
||||
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);
|
||||
PairKey key = new PairKey(level.dimension().location(), gunId);
|
||||
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(current.bluePortalId(), portalId);
|
||||
if (updated.bluePortalId() == null && updated.orangePortalId() == null) {
|
||||
data.pairs.remove(key);
|
||||
} else {
|
||||
data.pairs.put(key, updated);
|
||||
}
|
||||
data.setDirty();
|
||||
: new PairRecord(current.bluePortalId(), portalId));
|
||||
}
|
||||
|
||||
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 gunId, PortalGunPortalEntity.PortalSide side, UUID portalId, ResourceLocation dimension) {
|
||||
if (gunId == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
public static void clearPortal(MinecraftServer server, UUID owner, UUID gunId, String channelName, PortalGunPortalEntity.PortalSide side, UUID portalId, ResourceLocation dimension) {
|
||||
PortalGunSavedData data = get(server);
|
||||
UUID resolvedGunId = data.resolveChannelGunId(owner, channelName, gunId);
|
||||
data.migrateLegacyPair(server, owner);
|
||||
data.adoptLegacyPairs(server, owner, resolvedGunId, channelName);
|
||||
PairKey key = new PairKey(owner, dimension, resolvedGunId);
|
||||
PairKey key = new PairKey(dimension, gunId);
|
||||
PairRecord current = data.pairs.get(key);
|
||||
if (current == null) {
|
||||
return;
|
||||
}
|
||||
UUID blue = current.bluePortalId();
|
||||
UUID orange = current.orangePortalId();
|
||||
if (side == PortalGunPortalEntity.PortalSide.BLUE && portalId.equals(blue)) {
|
||||
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();
|
||||
UUID blue = side == PortalGunPortalEntity.PortalSide.BLUE && portalId.equals(current.bluePortalId()) ? null : current.bluePortalId();
|
||||
UUID orange = side == PortalGunPortalEntity.PortalSide.ORANGE && portalId.equals(current.orangePortalId()) ? null : current.orangePortalId();
|
||||
data.put(key, new PairRecord(blue, orange));
|
||||
}
|
||||
|
||||
public static void clearAllPortals(ServerLevel level, UUID owner, UUID gunId) {
|
||||
clearAllPortals(level, owner, gunId, String.valueOf(gunId));
|
||||
public static void clearAllPortals(ServerLevel level, UUID gunId) {
|
||||
if (gunId == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
public static void clearAllPortals(ServerLevel level, UUID owner, UUID gunId, String channelName) {
|
||||
MinecraftServer server = level.getServer();
|
||||
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<>();
|
||||
PairRecord legacy = gunId == null ? data.legacyPairs.remove(owner) : null;
|
||||
if (legacy != null) {
|
||||
addPortalIds(portalIds, legacy);
|
||||
}
|
||||
data.pairs.entrySet().removeIf(entry -> {
|
||||
if (!entry.getKey().owner().equals(owner) || !entry.getKey().dimension().equals(level.dimension().location())
|
||||
|| !Objects.equals(entry.getKey().gunId(), resolvedGunId)) {
|
||||
if (!entry.getKey().gunId().equals(gunId)) {
|
||||
return false;
|
||||
}
|
||||
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) {
|
||||
clearPortalSide(level, owner, gunId, String.valueOf(gunId), side);
|
||||
public static void clearPortalSide(ServerLevel level, UUID gunId, PortalGunPortalEntity.PortalSide side) {
|
||||
if (gunId == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
public static void clearPortalSide(ServerLevel level, UUID owner, UUID gunId, String channelName, PortalGunPortalEntity.PortalSide side) {
|
||||
MinecraftServer server = level.getServer();
|
||||
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<>();
|
||||
for (Map.Entry<PairKey, PairRecord> entry : new HashMap<>(data.pairs).entrySet()) {
|
||||
PairKey key = entry.getKey();
|
||||
if (!key.owner().equals(owner) || !key.dimension().equals(level.dimension().location()) || !Objects.equals(key.gunId(), resolvedGunId)) {
|
||||
if (!entry.getKey().gunId().equals(gunId)) {
|
||||
continue;
|
||||
}
|
||||
PairRecord pair = entry.getValue();
|
||||
@@ -266,23 +148,17 @@ public final class PortalGunSavedData extends SavedData {
|
||||
continue;
|
||||
}
|
||||
portalIds.add(removed);
|
||||
PairRecord remaining = side == PortalGunPortalEntity.PortalSide.BLUE
|
||||
data.put(entry.getKey(), side == PortalGunPortalEntity.PortalSide.BLUE
|
||||
? new PairRecord(null, pair.orangePortalId())
|
||||
: new PairRecord(pair.bluePortalId(), null);
|
||||
if (remaining.bluePortalId() == null && remaining.orangePortalId() == null) {
|
||||
data.pairs.remove(key);
|
||||
} else {
|
||||
data.pairs.put(key, remaining);
|
||||
: new PairRecord(pair.bluePortalId(), null));
|
||||
}
|
||||
}
|
||||
data.setDirty();
|
||||
for (UUID portalId : portalIds) {
|
||||
discardIfLoaded(server, portalId);
|
||||
}
|
||||
}
|
||||
|
||||
public static PortalGunPortalEntity findLoadedPortal(ServerLevel level, UUID owner, UUID gunId, PortalGunPortalEntity.PortalSide side) {
|
||||
Optional<UUID> portalId = getPortalId(level.getServer(), owner, gunId, side, level.dimension().location());
|
||||
public static PortalGunPortalEntity findLoadedPortal(ServerLevel level, UUID gunId, PortalGunPortalEntity.PortalSide side) {
|
||||
Optional<UUID> portalId = getPortalId(level.getServer(), gunId, side, level.dimension().location());
|
||||
if (portalId.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
@@ -290,144 +166,14 @@ public final class PortalGunSavedData extends SavedData {
|
||||
return entity instanceof PortalGunPortalEntity portal && !portal.isRemoved() ? portal : null;
|
||||
}
|
||||
|
||||
public static PortalGunPortalEntity findLoadedPortal(ServerLevel level, UUID owner, UUID gunId, String channelName, PortalGunPortalEntity.PortalSide side) {
|
||||
Optional<UUID> portalId = getPortalId(level.getServer(), owner, gunId, channelName, side, level.dimension().location());
|
||||
if (portalId.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
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();
|
||||
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);
|
||||
private void put(PairKey key, PairRecord record) {
|
||||
if (record.bluePortalId() == null && record.orangePortalId() == null) {
|
||||
this.pairs.remove(key);
|
||||
} else {
|
||||
this.legacyPairs.put(owner, new PairRecord(legacyBlue, legacyOrange));
|
||||
this.pairs.put(key, record);
|
||||
}
|
||||
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) {
|
||||
if (pair.bluePortalId() != null) {
|
||||
@@ -439,16 +185,15 @@ public final class PortalGunSavedData extends SavedData {
|
||||
}
|
||||
|
||||
private static void discardIfLoaded(MinecraftServer server, UUID portalId) {
|
||||
ServerLevel level = findPortalLevel(server, portalId);
|
||||
if (level != null && level.getEntity(portalId) instanceof PortalGunPortalEntity portal) {
|
||||
for (ServerLevel level : server.getAllLevels()) {
|
||||
if (level.getEntity(portalId) instanceof PortalGunPortalEntity portal && !portal.isRemoved()) {
|
||||
portal.discard();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private record PairKey(UUID owner, ResourceLocation dimension, UUID gunId) {
|
||||
}
|
||||
|
||||
private record OwnerChannelKey(UUID owner, String channelName) {
|
||||
private record PairKey(ResourceLocation dimension, UUID gunId) {
|
||||
}
|
||||
|
||||
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) {
|
||||
Vec3 relative = position.subtract(this.center);
|
||||
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.Set;
|
||||
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. */
|
||||
public final class KingsTreeGridFeature extends Feature<NoneFeatureConfiguration> {
|
||||
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_MAX_X = 192;
|
||||
private static final int TREE_MIN_Z = -227;
|
||||
@@ -108,45 +110,62 @@ public final class KingsTreeGridFeature extends Feature<NoneFeatureConfiguration
|
||||
}
|
||||
}
|
||||
if (centralTilePlaced) {
|
||||
spawnTreeKing(level, centerX, centerZ, baseY);
|
||||
queueTreeKing(level.getLevel(), centerX, centerZ, baseY);
|
||||
}
|
||||
return placed;
|
||||
}
|
||||
|
||||
private static void spawnTreeKing(WorldGenLevel level, int centerX, int centerZ, int baseY) {
|
||||
var serverLevel = level.getLevel();
|
||||
private static void queueTreeKing(ServerLevel serverLevel, int centerX, int centerZ, int baseY) {
|
||||
long treeKey = ((long) centerX << 32) ^ (centerZ & 0xffffffffL);
|
||||
synchronized (serverLevel) {
|
||||
synchronized (CLAIMED_TREE_KINGS) {
|
||||
if (!CLAIMED_TREE_KINGS.computeIfAbsent(serverLevel, ignored -> new java.util.HashSet<>()).add(treeKey)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
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, level.getMinBuildHeight(), centerZ - 512.0D,
|
||||
centerX + 512.0D, level.getMaxBuildHeight(), centerZ + 512.0D);
|
||||
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()) {
|
||||
// Repair older worlds that already contain duplicate tree-bound Kings.
|
||||
for (int index = 1; index < treeKings.size(); index++) {
|
||||
treeKings.get(index).discard();
|
||||
}
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
KingEntity king = AntarchyObjects.KING.get().create(serverLevel);
|
||||
if (king == null) {
|
||||
synchronized (CLAIMED_TREE_KINGS) {
|
||||
CLAIMED_TREE_KINGS.getOrDefault(serverLevel, Set.of()).remove(treeKey);
|
||||
}
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
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);
|
||||
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,
|
||||
@@ -154,7 +173,7 @@ public final class KingsTreeGridFeature extends Feature<NoneFeatureConfiguration
|
||||
king.setTreePatrolHome(treeCenter, minimumY, maximumY, 0.0D);
|
||||
king.setPersistenceRequired();
|
||||
serverLevel.addFreshEntity(king);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static Vec3 findSafeKingSpawn(ServerLevel level, KingEntity king,
|
||||
@@ -169,7 +188,7 @@ public final class KingsTreeGridFeature extends Feature<NoneFeatureConfiguration
|
||||
for (double heightOffset : heightOffsets) {
|
||||
Vec3 candidate = new Vec3(x, spawnY + heightOffset, z);
|
||||
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;
|
||||
}
|
||||
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) {
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.craisinlord.antarchy.mixins;
|
||||
import com.craisinlord.antarchy.content.portalgun.PortalGunCollisionHelper;
|
||||
import com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.MoverType;
|
||||
import net.minecraft.world.phys.AABB;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
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.CallbackInfo;
|
||||
|
||||
@Mixin(Entity.class)
|
||||
@Mixin(value = Entity.class, priority = 1100)
|
||||
public abstract class PortalGunEntityCollisionMixin {
|
||||
@Unique
|
||||
private AABB antarchy$portalGunStartBox;
|
||||
@Unique
|
||||
private AABB antarchy$portalGunMoveStartBox;
|
||||
@Unique
|
||||
private Vec3 antarchy$portalGunMoveStartPosition;
|
||||
private Vec3 antarchy$portalGunMoveStartVelocity;
|
||||
|
||||
@Inject(method = "move", at = @At("HEAD"))
|
||||
private void antarchy$capturePortalGunMovementStart(net.minecraft.world.entity.MoverType type, Vec3 movement, CallbackInfo ci) {
|
||||
Entity entity = (Entity) (Object) this;
|
||||
this.antarchy$portalGunMoveStartBox = entity.getBoundingBox();
|
||||
this.antarchy$portalGunMoveStartPosition = entity.position();
|
||||
private void antarchy$capturePortalGunMovementStart(MoverType type, Vec3 movement, CallbackInfo ci) {
|
||||
this.antarchy$portalGunMoveStartBox = ((Entity) (Object) this).getBoundingBox();
|
||||
this.antarchy$portalGunMoveStartVelocity = ((Entity) (Object) this).getDeltaMovement();
|
||||
}
|
||||
|
||||
@Inject(method = "move", at = @At("RETURN"))
|
||||
private void antarchy$teleportMobOnPortalCrossing(net.minecraft.world.entity.MoverType type, Vec3 movement, CallbackInfo ci) {
|
||||
Entity entity = (Entity) (Object) this;
|
||||
if (this.antarchy$portalGunMoveStartBox != null && this.antarchy$portalGunMoveStartPosition != null) {
|
||||
PortalGunPortalEntity.teleportEntityAfterMovement(
|
||||
entity,
|
||||
this.antarchy$portalGunMoveStartBox,
|
||||
entity.position().subtract(this.antarchy$portalGunMoveStartPosition)
|
||||
);
|
||||
}
|
||||
private void antarchy$teleportMobOnPortalCrossing(MoverType type, Vec3 movement, CallbackInfo ci) {
|
||||
AABB startBox = this.antarchy$portalGunMoveStartBox;
|
||||
Vec3 startVelocity = this.antarchy$portalGunMoveStartVelocity;
|
||||
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"))
|
||||
@@ -53,8 +50,11 @@ public abstract class PortalGunEntityCollisionMixin {
|
||||
if (this.antarchy$portalGunStartBox == null) {
|
||||
return;
|
||||
}
|
||||
Vec3 adjusted = PortalGunCollisionHelper.resolveCollision(entity, this.antarchy$portalGunStartBox, movement, cir.getReturnValue());
|
||||
Vec3 collided = cir.getReturnValue();
|
||||
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) {
|
||||
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())) {
|
||||
cir.setReturnValue(false);
|
||||
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;
|
||||
|
||||
import com.craisinlord.antarchy.content.entity.DorrieEntity;
|
||||
import com.craisinlord.antarchy.content.entity.HerculesBeetleEntity;
|
||||
import net.minecraft.client.Camera;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
@@ -40,10 +39,5 @@ public abstract class MountedVehicleCameraMixin {
|
||||
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;
|
||||
AABB search = entity.getBoundingBox().move(offset).inflate(entityExtent + PortalGunWorldPortalShape.DEFAULT_SCAN_DISTANCE);
|
||||
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);
|
||||
if (coords.depth() >= -entityExtent - 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.renderer.ScreenEffectRenderer;
|
||||
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.injection.At;
|
||||
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) {
|
||||
Minecraft minecraft = Minecraft.getInstance();
|
||||
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.AntarchyGravityDirection;
|
||||
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.WrapOperation;
|
||||
import java.util.List;
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package com.craisinlord.antarchy.mixins.gravity;
|
||||
|
||||
import net.minecraft.network.protocol.game.VecDeltaCodec;
|
||||
import net.minecraft.server.level.ServerEntity;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
|
||||
@@ -9,6 +11,12 @@ import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
* Accessor for server entity tracking internals.
|
||||
*/
|
||||
public interface ServerEntityAccessor {
|
||||
@Accessor("positionCodec")
|
||||
VecDeltaCodec antarchy$getPositionCodec();
|
||||
|
||||
@Accessor("lastSentMovement")
|
||||
void antarchy$setLastSentMovement(Vec3 lastSentMovement);
|
||||
|
||||
@Accessor("updateInterval")
|
||||
void antarchy$setUpdateInterval(int updateInterval);
|
||||
}
|
||||
@@ -7,7 +7,9 @@
|
||||
"AbstractMinecartMixin",
|
||||
"AbstractArrowAccessor",
|
||||
"PortalGunEntityCollisionMixin",
|
||||
"PortalGunEntityInWallMixin",
|
||||
"PortalGunPlayerMovePacketMixin",
|
||||
"PortalGunProjectileHitMixin",
|
||||
"ServerGamePacketListenerTeleportAccessor",
|
||||
"PortalGunZombieMixin",
|
||||
"FallingBlockEntityAccessor",
|
||||
@@ -71,6 +73,7 @@
|
||||
"gravity.AbstractArrowGravityMixin",
|
||||
"gravity.BehaviorUtilsGravityMixin",
|
||||
"gravity.BucketItemGravityMixin",
|
||||
"gravity.BubbleColumnGravityMixin",
|
||||
"gravity.ChunkMapAccessor",
|
||||
"gravity.CrossbowItemGravityMixin",
|
||||
"gravity.DirectionFacingMixin",
|
||||
@@ -171,6 +174,7 @@
|
||||
"client.MountedVehicleCameraMixin",
|
||||
"client.ParalyzedMouseTurnMixin",
|
||||
"client.LocalPlayerPortalMixin",
|
||||
"client.LocalPlayerPortalTransitMixin",
|
||||
"client.PortalGunEntityTeleportPacketMixin",
|
||||
"client.PortalGunZoomFovMixin",
|
||||
"client.LivingEntityPortalLerpAccessor",
|
||||
|
||||
@@ -671,6 +671,8 @@
|
||||
"tooltip.antarchy.reverie_bottle.release": "Right click to release the Reverie.",
|
||||
"item.antarchy.gravity_gun": "Gravity 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.cogwheel_pet": "Cogwheel 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.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.portal_gun.primary": "Attack: fire a blue portal.",
|
||||
"tooltip.antarchy.portal_gun.secondary": "Use: fire an orange portal.",
|
||||
"tooltip.antarchy.portal_gun.channel": "Channel: %s",
|
||||
"tooltip.antarchy.portal_gun.owner": "Owner: %s",
|
||||
"tooltip.antarchy.portal_gun.primary": "Attack: fire a %s portal.",
|
||||
"tooltip.antarchy.portal_gun.secondary": "Use: fire a %s portal.",
|
||||
"tooltip.antarchy.portal_gun.color.blue": "blue",
|
||||
"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.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.diamond_minecart.forward": "Will be implemented later.",
|
||||
"tooltip.antarchy.diamond_minecart.rails": " ",
|
||||
@@ -1250,7 +1253,6 @@
|
||||
"advancements.antarchy.duct_tape.description": "and fixed it with the power of duct tape!",
|
||||
"key.categories.antarchy": "Antarchy",
|
||||
"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.brutalfly_flap": "Brutalfly Elytra: Flap",
|
||||
"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"
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 704 B |
|
After Width: | Height: | Size: 706 B |
|
After Width: | Height: | Size: 705 B |
|
After Width: | Height: | Size: 701 B |
|
After Width: | Height: | Size: 286 B |
|
After Width: | Height: | Size: 341 B |
|
After Width: | Height: | Size: 534 B |
|
After Width: | Height: | Size: 415 B |
|
After Width: | Height: | Size: 416 B |
|
After Width: | Height: | Size: 3.0 KiB |
|
After Width: | Height: | Size: 3.0 KiB |
|
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",
|
||||
"weight": 24,
|
||||
"weight": 20,
|
||||
"minCount": 1,
|
||||
"maxCount": 3
|
||||
},
|
||||
{
|
||||
"type": "antarchy:golden_apple_cow",
|
||||
"weight": 20,
|
||||
"weight": 16,
|
||||
"minCount": 1,
|
||||
"maxCount": 3
|
||||
},
|
||||
|
||||
@@ -34,6 +34,10 @@
|
||||
}
|
||||
},
|
||||
"spawn_costs": {
|
||||
"antarchy:basilisk": {
|
||||
"energy_budget": 0.1,
|
||||
"charge": 14.0
|
||||
},
|
||||
"antarchy:glimmer": {
|
||||
"energy_budget": 0.1,
|
||||
"charge": 9
|
||||
@@ -86,7 +90,14 @@
|
||||
],
|
||||
"creature_spawn_probability": 0.07,
|
||||
"spawners": {
|
||||
"monster": [],
|
||||
"monster": [
|
||||
{
|
||||
"type": "antarchy:basilisk",
|
||||
"weight": 10,
|
||||
"minCount": 1,
|
||||
"maxCount": 1
|
||||
}
|
||||
],
|
||||
"creature": [
|
||||
{
|
||||
"type": "minecraft:frog",
|
||||
|
||||
@@ -93,7 +93,7 @@
|
||||
"monster": [
|
||||
{
|
||||
"type": "antarchy:basilisk",
|
||||
"weight": 20,
|
||||
"weight": 24,
|
||||
"minCount": 1,
|
||||
"maxCount": 1
|
||||
}
|
||||
|
||||
@@ -96,13 +96,13 @@
|
||||
},
|
||||
{
|
||||
"type": "antarchy:apple_cow",
|
||||
"weight": 24,
|
||||
"weight": 20,
|
||||
"minCount": 1,
|
||||
"maxCount": 3
|
||||
},
|
||||
{
|
||||
"type": "antarchy:golden_apple_cow",
|
||||
"weight": 20,
|
||||
"weight": 16,
|
||||
"minCount": 1,
|
||||
"maxCount": 3
|
||||
},
|
||||
|
||||
@@ -39,8 +39,8 @@
|
||||
],
|
||||
"placement": {
|
||||
"salt": 659843583,
|
||||
"spacing": 42,
|
||||
"separation": 35,
|
||||
"spacing": 37,
|
||||
"separation": 30,
|
||||
"type": "integrated_api:advanced_random_spread"
|
||||
}
|
||||
}
|
||||
@@ -49,7 +49,7 @@
|
||||
"base": 13,
|
||||
"per_level_above_first": 8
|
||||
},
|
||||
"max_level": 10,
|
||||
"max_level": 4,
|
||||
"min_cost": {
|
||||
"base": 5,
|
||||
"per_level_above_first": 8
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
"base": 18,
|
||||
"per_level_above_first": 8
|
||||
},
|
||||
"max_level": 10,
|
||||
"max_level": 4,
|
||||
"min_cost": {
|
||||
"base": 10,
|
||||
"per_level_above_first": 8
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
"base": 9,
|
||||
"per_level_above_first": 6
|
||||
},
|
||||
"max_level": 10,
|
||||
"max_level": 4,
|
||||
"min_cost": {
|
||||
"base": 3,
|
||||
"per_level_above_first": 6
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
"base": 12,
|
||||
"per_level_above_first": 11
|
||||
},
|
||||
"max_level": 10,
|
||||
"max_level": 4,
|
||||
"min_cost": {
|
||||
"base": 1,
|
||||
"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.config.AntarchySettings;
|
||||
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.AttitudeAdjusterSlamManager;
|
||||
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.portal.PermanentPortalManager;
|
||||
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.CaterpillarCommand;
|
||||
import com.craisinlord.antarchy.content.command.DimensionalTearCommand;
|
||||
@@ -78,7 +76,6 @@ public final class AntarchyFabricEvents {
|
||||
|
||||
public static void register() {
|
||||
ServerLifecycleEvents.SERVER_STOPPING.register(server -> PortalGunProjectileEntity.releaseAllProjectileTickets());
|
||||
ServerLifecycleEvents.SERVER_STOPPING.register(PortalGunGrabManager::clear);
|
||||
ServerLifecycleEvents.SERVER_STOPPING.register(com.craisinlord.antarchy.content.portalgun.PortalGunResetManager::clear);
|
||||
ServerPlayConnectionEvents.JOIN.register((handler, sender, server) -> {
|
||||
ChronosphereManager.refresh(handler.player);
|
||||
@@ -209,14 +206,13 @@ public final class AntarchyFabricEvents {
|
||||
});
|
||||
|
||||
ServerTickEvents.END_SERVER_TICK.register(server -> {
|
||||
PortalGunGrabManager.tick(server);
|
||||
Set<UUID> activeThisTick = new HashSet<>();
|
||||
for (ServerLevel level : server.getAllLevels()) {
|
||||
CavarynHordeManager.tick(level);
|
||||
com.craisinlord.antarchy.content.horde.CavarynCreatureSpawner.tick(level);
|
||||
ThoraxisUndersideManager.tick(level);
|
||||
com.craisinlord.antarchy.content.worldgen.elythia.KingsTreeGridFeature.tickPendingSpawns(level);
|
||||
tickInvertedPlayers(level, activeThisTick);
|
||||
tickDuctTapePlayers(level);
|
||||
tickIchorPlayers(level);
|
||||
tickBloodglassRecharge(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) {
|
||||
if (!AntarchySettings.ichorWitherEnabled()) {
|
||||
return;
|
||||
|
||||
@@ -625,15 +625,14 @@ public final class AntarchyFabricClientBootstrap {
|
||||
DorrieJumpHudRenderer.render(guiGraphics);
|
||||
HerculesBeetleChargeHudRenderer.render(guiGraphics);
|
||||
com.craisinlord.antarchy.content.client.KingJudgmentFlashHudRenderer.render(guiGraphics);
|
||||
com.craisinlord.antarchy.content.client.PortalGunCrosshairRenderer.render(guiGraphics);
|
||||
});
|
||||
|
||||
WorldRenderEvents.LAST.register(context -> {
|
||||
BloodCrystalKatanaTrailHandler.render(context);
|
||||
ScorpionWhipTetherRenderHandler.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());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -51,14 +51,8 @@ public final class AntarchyKeyBindings {
|
||||
);
|
||||
public static final KeyMapping PORTAL_GUN_ZOOM = new KeyMapping(
|
||||
"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,
|
||||
GLFW.GLFW_KEY_G,
|
||||
GLFW.GLFW_KEY_Z,
|
||||
CATEGORY
|
||||
);
|
||||
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_BOUNDARY);
|
||||
KeyBindingHelper.registerKeyBinding(PORTAL_GUN_ZOOM);
|
||||
KeyBindingHelper.registerKeyBinding(PORTAL_GUN_GRAB);
|
||||
KeyBindingHelper.registerKeyBinding(PORTAL_GUN_RESET);
|
||||
}
|
||||
|
||||
@@ -110,10 +103,6 @@ public final class AntarchyKeyBindings {
|
||||
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() {
|
||||
return Minecraft.getInstance().screen == null && PORTAL_GUN_RESET.consumeClick();
|
||||
}
|
||||
|
||||
@@ -27,14 +27,12 @@ public final class DorrieJumpClientHandler {
|
||||
return;
|
||||
}
|
||||
|
||||
// Space bar → upward movement.
|
||||
boolean pressingJump = mc.options.keyJump.isDown();
|
||||
if (pressingJump != wasPressingJump) {
|
||||
ClientPlayNetworking.send(new DorrieJumpInputPayload(pressingJump));
|
||||
wasPressingJump = pressingJump;
|
||||
}
|
||||
|
||||
// Left ctrl → charge jump.
|
||||
boolean pressingCharge = AntarchyKeyBindings.isMountSpecialPressed();
|
||||
if (pressingCharge != wasPressingCharge) {
|
||||
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.client.PortalGunZoomClientState;
|
||||
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.networking.v1.ClientPlayNetworking;
|
||||
import net.minecraft.client.Minecraft;
|
||||
@@ -20,23 +19,19 @@ public final class PortalGunClientHandler {
|
||||
|
||||
private static void tickMouse(Minecraft mc) {
|
||||
if (mc.player == null) {
|
||||
com.craisinlord.antarchy.content.client.PortalGunGrabClientState.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 gameplayInput = mc.screen == null;
|
||||
boolean held = hasPortalGun && gameplayInput;
|
||||
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(
|
||||
mc,
|
||||
held,
|
||||
AntarchyKeyBindings.isPortalGunResetDown(),
|
||||
ClientPlayNetworking::send
|
||||
);
|
||||
if (held && AntarchyKeyBindings.consumePortalGunGrabPressed()) {
|
||||
ClientPlayNetworking.send(new PortalGunGrabPayload());
|
||||
}
|
||||
if (mc.player == null || mc.level == null || mc.screen != null) {
|
||||
lastAttackDown = false;
|
||||
return;
|
||||
|
||||
@@ -20,6 +20,8 @@ public final class SeparateLargeItemModels {
|
||||
entry(AntarchyFabricItems.GROWTH_RAY.get(), "grow_ray_gui"),
|
||||
entry(AntarchyFabricItems.KRAKENS_GRASP.get(), "krakens_grasp_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_STAFF.get(), "royal_assailant_staff_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);
|
||||
ClientPlayNetworking.registerGlobalReceiver(PortalGunIndicatorPayload.TYPE, (payload, context) ->
|
||||
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) ->
|
||||
context.client().execute(() -> AntarchyFabricNetworking.handleGravityState(context.player(), payload)));
|
||||
ClientPlayNetworking.registerGlobalReceiver(BloodglassStatePayload.TYPE, (payload, context) ->
|
||||
|
||||
@@ -50,7 +50,6 @@ public final class AntarchyFabricNetworking {
|
||||
registerServerReceivers();
|
||||
com.craisinlord.antarchy.content.network.AntarchyGameNetworkHandler.setResultSender(ServerPlayNetworking::send);
|
||||
com.craisinlord.antarchy.content.portalgun.PortalGunIndicatorSync.setSender(ServerPlayNetworking::send);
|
||||
com.craisinlord.antarchy.content.portalgun.PortalGunGrabManager.setStateSender(ServerPlayNetworking::send);
|
||||
}
|
||||
|
||||
public static void bootstrapMultipartCommon() {
|
||||
@@ -76,7 +75,6 @@ public final class AntarchyFabricNetworking {
|
||||
private static void registerPayloadTypes() {
|
||||
PayloadTypeRegistry.playS2C().register(AntarchyGameResultPayload.TYPE, AntarchyGameResultPayload.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(BloodglassStatePayload.TYPE, BloodglassStatePayload.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(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(PortalGunGrabPayload.TYPE, PortalGunGrabPayload.STREAM_CODEC);
|
||||
PayloadTypeRegistry.playC2S().register(PortalGunResetPayload.TYPE, PortalGunResetPayload.STREAM_CODEC);
|
||||
PayloadTypeRegistry.playC2S().register(EyeOfStormPrimaryPayload.TYPE, EyeOfStormPrimaryPayload.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)));
|
||||
ServerPlayNetworking.registerGlobalReceiver(PortalGunPrimaryPayload.TYPE, (payload, context) ->
|
||||
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) ->
|
||||
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) ->
|
||||
context.server().execute(() -> com.craisinlord.antarchy.content.portalgun.PortalGunResetManager.handleInput(context.player(), payload.action())));
|
||||
ServerPlayNetworking.registerGlobalReceiver(EyeOfStormPrimaryPayload.TYPE, (payload, context) ->
|
||||
|
||||
@@ -1561,7 +1561,28 @@ public final class AntarchyFabricItems {
|
||||
AntarchyFabricEntities.PORTAL_GUN_BLACK_HOLE,
|
||||
AntarchyFabricEntities.PORTAL_GUN_PROJECTILE,
|
||||
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",
|
||||
|
||||
@@ -27,12 +27,12 @@
|
||||
"infinity.AmendmentRareOresMixin"
|
||||
],
|
||||
"client": [
|
||||
"client.RenderTargetDepthStencilMixin",
|
||||
"client.SeparateLargeItemModelsMixin",
|
||||
"client.SeparateLargeItemGuiLightingMixin",
|
||||
"client.CameraShakeMixin",
|
||||
"client.BrutalflyElytraExperienceBarMixin",
|
||||
"client.BloodglassChatOrderMixin",
|
||||
"client.DuctTapeInputMixin",
|
||||
"client.LiquidFogMixin",
|
||||
"client.GravityGunMouseScrollMixin",
|
||||
"client.KeybindingConflictFixMixin",
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"parent": "antarchy:item/portal_gun_held_geo"
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"parent": "antarchy:item/portal_gun_held_geo"
|
||||
}
|
||||
@@ -671,8 +671,9 @@ public final class AntarchyConfigModuleNeoforge {
|
||||
AntarchySettings.setPortalGunSeeThroughPortals(AntarchyMiscConfig.portalGunSeeThroughPortals());
|
||||
AntarchySettings.setPortalGunFancyPortals(AntarchyMiscConfig.portalGunFancyPortals());
|
||||
AntarchySettings.setPortalGunMaxShootDistance(AntarchyMiscConfig.portalGunMaxShootDistance());
|
||||
AntarchySettings.setPortalGunEntityGrabWeightBase(AntarchyMiscConfig.portalGunEntityGrabWeightBase());
|
||||
AntarchySettings.setPortalGunIndicatorSize(AntarchyMiscConfig.portalGunIndicatorSize());
|
||||
AntarchySettings.setPortalGunMinFloorExitSpeed(AntarchyMiscConfig.portalGunMinFloorExitSpeed());
|
||||
AntarchySettings.setPortalGunMaxLiveViews(AntarchyMiscConfig.portalGunMaxLiveViews());
|
||||
AntarchySettings.setPortalGunMaxExitSpeed(AntarchyMiscConfig.portalGunMaxExitSpeed());
|
||||
|
||||
|
||||
// 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_FANCY_PORTALS;
|
||||
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.IntValue PORTAL_GUN_INDICATOR_SIZE;
|
||||
private static final ModConfigSpec.DoubleValue PORTAL_GUN_MIN_FLOOR_EXIT_SPEED;
|
||||
private static final ModConfigSpec.IntValue PORTAL_GUN_MAX_LIVE_VIEWS;
|
||||
private static final ModConfigSpec.DoubleValue PORTAL_GUN_MAX_EXIT_SPEED;
|
||||
|
||||
// Diamond Minecart
|
||||
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_FANCY_PORTALS = b.define("fancyPortals", true);
|
||||
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_INDICATOR_SIZE = b.defineInRange("indicatorSize", 30, 0, 100);
|
||||
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_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();
|
||||
|
||||
|
||||
@@ -224,8 +226,9 @@ public final class AntarchyMiscConfig {
|
||||
public static boolean portalGunSeeThroughPortals() { return PORTAL_GUN_SEE_THROUGH_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 portalGunEntityGrabWeightBase() { return PORTAL_GUN_ENTITY_GRAB_WEIGHT_BASE.get(); }
|
||||
public static int portalGunIndicatorSize() { return PORTAL_GUN_INDICATOR_SIZE.get(); }
|
||||
public static double portalGunMinFloorExitSpeed() { return PORTAL_GUN_MIN_FLOOR_EXIT_SPEED.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 diamondMinecartPlacesRails() { return DIAMOND_MINECART_PLACES_RAILS.get(); }
|
||||
|
||||
@@ -65,13 +65,6 @@ public final class AntarchyKeyBindings {
|
||||
InputConstants.MOUSE_BUTTON_MIDDLE,
|
||||
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(
|
||||
"key.antarchy.portal_gun_reset",
|
||||
KeyConflictContext.IN_GAME,
|
||||
@@ -91,7 +84,6 @@ public final class AntarchyKeyBindings {
|
||||
event.register(ROYAL_INVERSION_TOGGLE);
|
||||
event.register(ROYAL_BOUNDARY);
|
||||
event.register(PORTAL_GUN_ZOOM);
|
||||
event.register(PORTAL_GUN_GRAB);
|
||||
event.register(PORTAL_GUN_RESET);
|
||||
}
|
||||
|
||||
@@ -119,10 +111,6 @@ public final class AntarchyKeyBindings {
|
||||
return PORTAL_GUN_ZOOM.consumeClick();
|
||||
}
|
||||
|
||||
public static boolean consumePortalGunGrabPressed() {
|
||||
return PORTAL_GUN_GRAB.consumeClick();
|
||||
}
|
||||
|
||||
public static boolean consumePortalGunResetPressed() {
|
||||
return PORTAL_GUN_RESET.consumeClick();
|
||||
}
|
||||
|
||||