moved miners dream to seperate mod
new portal gun draft fixed scorched guns mixin sodium compat layer
This commit is contained in:
1 parent
23599c4c59
commit
c7cb48edfb
189 files changed
+6450
-2743
No files matched your search
@@ -2,7 +2,7 @@ https://www.curseforge.com/minecraft/mc-mods/antarchy
|
||||
|
||||
ANTS! ANTS! ANTS!
|
||||
|
||||
Antarchy requires Architectury API, AntOS, Integrated API, GeckoLib, and Sizeable Folia.
|
||||
Antarchy requires Architectury API, AntOS, Integrated API, GeckoLib, and Sizeable Foliage.
|
||||
|
||||
## Credits
|
||||
|
||||
|
||||
+1
-1
@@ -110,7 +110,7 @@ public final class AntarchyConfigCatalog {
|
||||
|| name.startsWith("nightmareSword") || name.startsWith("basiliskDagger")
|
||||
|| name.startsWith("squidzooka") || name.equals("invertProjectilesFromInvertedPlayers")
|
||||
|| name.startsWith("gravityGun") || name.startsWith("critterCage")
|
||||
|| name.startsWith("minersDream") || name.equals("potentNyxiteInvertedDurationSeconds")
|
||||
|| name.equals("potentNyxiteInvertedDurationSeconds")
|
||||
|| name.equals("corneaEarNightVisionSeconds") || name.startsWith("american")
|
||||
|| name.startsWith("mogglesVision") || name.equals("ductTapeRepairPercentPerUse")
|
||||
|| name.startsWith("fallenKingCrown");
|
||||
|
||||
@@ -163,11 +163,6 @@ public final class AntarchySettings {
|
||||
private static double gravityGunMaxHoldDistance = 10.0D;
|
||||
private static double critterCageMaxCapturableWidth = 1.65D;
|
||||
private static double critterCageMaxCapturableHeight = 1.65D;
|
||||
private static boolean minersDreamEnabled = true;
|
||||
private static int minersDreamMinimumRange = 48;
|
||||
private static int minersDreamMaximumRange = 96;
|
||||
private static int minersDreamTorchSpacing = 10;
|
||||
private static int minersDreamBlocksPerTick = 150;
|
||||
private static double lucidAttackRange = 9.0D;
|
||||
private static double lucidPearlInvertedDurationSeconds = 6.0D;
|
||||
private static int corneaEarNightVisionSeconds = 15;
|
||||
@@ -1189,26 +1184,6 @@ public final class AntarchySettings {
|
||||
return critterCageMaxCapturableHeight;
|
||||
}
|
||||
|
||||
public static boolean minersDreamEnabled() {
|
||||
return minersDreamEnabled;
|
||||
}
|
||||
|
||||
public static int minersDreamMinimumRange() {
|
||||
return minersDreamMinimumRange;
|
||||
}
|
||||
|
||||
public static int minersDreamMaximumRange() {
|
||||
return minersDreamMaximumRange;
|
||||
}
|
||||
|
||||
public static int minersDreamTorchSpacing() {
|
||||
return minersDreamTorchSpacing;
|
||||
}
|
||||
|
||||
public static int minersDreamBlocksPerTick() {
|
||||
return minersDreamBlocksPerTick;
|
||||
}
|
||||
|
||||
public static void setDisableInfinityBookPortalCreation(boolean value) {
|
||||
disableInfinityBookPortalCreation = value;
|
||||
}
|
||||
@@ -1731,26 +1706,6 @@ public final class AntarchySettings {
|
||||
critterCageMaxCapturableHeight = value;
|
||||
}
|
||||
|
||||
public static void setMinersDreamEnabled(boolean value) {
|
||||
minersDreamEnabled = value;
|
||||
}
|
||||
|
||||
public static void setMinersDreamMinimumRange(int value) {
|
||||
minersDreamMinimumRange = value;
|
||||
}
|
||||
|
||||
public static void setMinersDreamMaximumRange(int value) {
|
||||
minersDreamMaximumRange = value;
|
||||
}
|
||||
|
||||
public static void setMinersDreamTorchSpacing(int value) {
|
||||
minersDreamTorchSpacing = value;
|
||||
}
|
||||
|
||||
public static void setMinersDreamBlocksPerTick(int value) {
|
||||
minersDreamBlocksPerTick = value;
|
||||
}
|
||||
|
||||
public static double lucidAttackRange() {
|
||||
return lucidAttackRange;
|
||||
}
|
||||
|
||||
@@ -40,7 +40,6 @@ public final class AntarchyTags {
|
||||
public static final TagKey<Block> CAVARYN_PORTAL_FRAMES = TagKey.create(Registries.BLOCK, id("cavaryn_portal_frames"));
|
||||
public static final TagKey<Block> RAINBOW_PORTAL_FRAMES = TagKey.create(Registries.BLOCK, id("rainbow_portal_frames"));
|
||||
public static final TagKey<Block> ROLLY_CAVERN_CARVEABLE = TagKey.create(Registries.BLOCK, id("rolly_cavern_carveable"));
|
||||
public static final TagKey<Block> MINERS_DREAM_CARVABLE = TagKey.create(Registries.BLOCK, id("miners_dream_carvable"));
|
||||
public static final TagKey<Block> BLUESTONE_COMPONENTS = TagKey.create(Registries.BLOCK, id("bluestone_components"));
|
||||
public static final TagKey<Block> BLUESTONE_CONNECTABLE = TagKey.create(Registries.BLOCK, id("bluestone_connectable"));
|
||||
public static final TagKey<Block> ANTIMETAL_INVERSION_BLOCKS = TagKey.create(Registries.BLOCK, id("antimetal_inversion_blocks"));
|
||||
|
||||
+5
@@ -19,6 +19,7 @@ import net.minecraft.world.entity.player.Inventory;
|
||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||
import net.minecraft.world.inventory.HopperMenu;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.block.ChestBlock;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||
import net.minecraft.world.level.block.entity.RandomizableContainerBlockEntity;
|
||||
@@ -118,6 +119,10 @@ public class UpperBlockEntity extends RandomizableContainerBlockEntity {
|
||||
}
|
||||
|
||||
private static Container getContainerAt(ServerLevel level, BlockPos pos) {
|
||||
BlockState state = level.getBlockState(pos);
|
||||
if (state.getBlock() instanceof ChestBlock chestBlock) {
|
||||
return ChestBlock.getContainer(chestBlock, state, level, pos, false);
|
||||
}
|
||||
BlockEntity blockEntity = level.getBlockEntity(pos);
|
||||
return blockEntity instanceof Container container ? container : null;
|
||||
}
|
||||
|
||||
-173
@@ -1,173 +0,0 @@
|
||||
package com.craisinlord.antarchy.content.item;
|
||||
|
||||
import com.craisinlord.antarchy.content.AntarchyTags;
|
||||
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.util.Mth;
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
|
||||
public final class MinersDreamCaveGenerator {
|
||||
private static final double MIN_HORIZONTAL_RADIUS = 2.0D;
|
||||
private static final double MAX_HORIZONTAL_RADIUS = 5.0D;
|
||||
private static final double MIN_VERTICAL_RADIUS = 1.5D;
|
||||
private static final double MAX_VERTICAL_RADIUS = 4.0D;
|
||||
private static final double MAX_VERTICAL_DEVIATION = 8.0D;
|
||||
private static final double SOFT_VERTICAL_DEVIATION = 4.0D;
|
||||
private static final int WORLD_MARGIN = 3;
|
||||
|
||||
private MinersDreamCaveGenerator() {
|
||||
}
|
||||
|
||||
public record CaveNode(BlockPos center, double horizontalRadius, double verticalRadius) {
|
||||
}
|
||||
|
||||
public record CaveGenerationResult(List<CaveNode> nodes, Set<Integer> torchCheckpoints) {
|
||||
}
|
||||
|
||||
public static CaveGenerationResult generate(BlockPos start, Direction initialDirection, int length,
|
||||
int torchSpacing, int minBuildHeight, int maxBuildHeight, RandomSource random) {
|
||||
List<CaveNode> nodes = new ArrayList<>(length);
|
||||
Set<Integer> torchCheckpoints = new HashSet<>();
|
||||
|
||||
double x = start.getX() + 0.5D;
|
||||
double y = start.getY() + 0.5D;
|
||||
double z = start.getZ() + 0.5D;
|
||||
double startY = y;
|
||||
|
||||
double headingX = initialDirection.getStepX();
|
||||
double headingZ = initialDirection.getStepZ();
|
||||
if (headingX == 0.0D && headingZ == 0.0D) {
|
||||
headingX = 1.0D;
|
||||
}
|
||||
|
||||
double verticalVelocity = 0.0D;
|
||||
double horizontalRadius = 3.0D;
|
||||
double verticalRadius = 2.0D;
|
||||
int minY = minBuildHeight + WORLD_MARGIN;
|
||||
int maxY = maxBuildHeight - WORLD_MARGIN;
|
||||
|
||||
int spacing = Math.max(4, torchSpacing);
|
||||
int traveledSinceTorch = 0;
|
||||
int nextTorchDistance = spacing + random.nextInt(5) - 2;
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
BlockPos center = BlockPos.containing(x, Mth.clamp(y, minY, maxY), z);
|
||||
nodes.add(new CaveNode(center, horizontalRadius, verticalRadius));
|
||||
|
||||
traveledSinceTorch++;
|
||||
if (traveledSinceTorch >= Math.max(1, nextTorchDistance)) {
|
||||
torchCheckpoints.add(i);
|
||||
traveledSinceTorch = 0;
|
||||
nextTorchDistance = spacing + random.nextInt(5) - 2;
|
||||
}
|
||||
|
||||
double turnDegrees = random.nextGaussian() * 8.0D;
|
||||
if (random.nextFloat() < 0.1F) {
|
||||
turnDegrees = random.nextGaussian() * 15.0D;
|
||||
}
|
||||
turnDegrees = Mth.clamp(turnDegrees, -25.0D, 25.0D);
|
||||
double turnRadians = Math.toRadians(turnDegrees);
|
||||
double cos = Math.cos(turnRadians);
|
||||
double sin = Math.sin(turnRadians);
|
||||
double newHeadingX = headingX * cos - headingZ * sin;
|
||||
double newHeadingZ = headingX * sin + headingZ * cos;
|
||||
double headingLength = Math.sqrt(newHeadingX * newHeadingX + newHeadingZ * newHeadingZ);
|
||||
headingX = newHeadingX / headingLength;
|
||||
headingZ = newHeadingZ / headingLength;
|
||||
|
||||
verticalVelocity += random.nextGaussian() * 0.15D;
|
||||
double deviation = y - startY;
|
||||
verticalVelocity -= deviation * 0.06D;
|
||||
if (deviation > SOFT_VERTICAL_DEVIATION) {
|
||||
verticalVelocity -= 0.08D;
|
||||
} else if (deviation < -SOFT_VERTICAL_DEVIATION) {
|
||||
verticalVelocity += 0.08D;
|
||||
}
|
||||
verticalVelocity = Mth.clamp(verticalVelocity, -0.6D, 0.6D);
|
||||
|
||||
x += headingX;
|
||||
z += headingZ;
|
||||
y = Mth.clamp(y + verticalVelocity, startY - MAX_VERTICAL_DEVIATION, startY + MAX_VERTICAL_DEVIATION);
|
||||
|
||||
horizontalRadius = Mth.clamp(horizontalRadius + random.nextGaussian() * 0.15D, MIN_HORIZONTAL_RADIUS, MAX_HORIZONTAL_RADIUS);
|
||||
verticalRadius = Mth.clamp(verticalRadius + random.nextGaussian() * 0.12D, MIN_VERTICAL_RADIUS, MAX_VERTICAL_RADIUS);
|
||||
if (random.nextFloat() < 0.05F) {
|
||||
horizontalRadius = Mth.clamp(horizontalRadius + 0.6D, MIN_HORIZONTAL_RADIUS, MAX_HORIZONTAL_RADIUS);
|
||||
verticalRadius = Mth.clamp(verticalRadius + 0.5D, MIN_VERTICAL_RADIUS, MAX_VERTICAL_RADIUS);
|
||||
}
|
||||
}
|
||||
|
||||
return new CaveGenerationResult(nodes, torchCheckpoints);
|
||||
}
|
||||
|
||||
public static void collectNodeCandidates(ServerLevel level, CaveNode node, LongOpenHashSet visited, RandomSource random, List<BlockPos> out) {
|
||||
BlockPos center = node.center();
|
||||
int hr = Mth.ceil(node.horizontalRadius());
|
||||
double upExtent = node.verticalRadius() * 1.25D;
|
||||
double downExtent = node.verticalRadius() * 0.65D;
|
||||
int upR = Mth.ceil(upExtent);
|
||||
int downR = Mth.ceil(downExtent);
|
||||
int minBuildHeight = level.getMinBuildHeight();
|
||||
int maxBuildHeight = level.getMaxBuildHeight();
|
||||
|
||||
for (int dy = -downR; dy <= upR; dy++) {
|
||||
double vExtent = dy >= 0 ? upExtent : downExtent;
|
||||
if (vExtent <= 0.0D) {
|
||||
continue;
|
||||
}
|
||||
double vTerm = (dy * dy) / (vExtent * vExtent);
|
||||
if (vTerm > 1.05D) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int dx = -hr; dx <= hr; dx++) {
|
||||
double hxTerm = (dx * dx) / (node.horizontalRadius() * node.horizontalRadius());
|
||||
for (int dz = -hr; dz <= hr; dz++) {
|
||||
double hzTerm = (dz * dz) / (node.horizontalRadius() * node.horizontalRadius());
|
||||
double distance = hxTerm + vTerm + hzTerm;
|
||||
if (distance > 1.0D) {
|
||||
continue;
|
||||
}
|
||||
if (distance > 0.7D) {
|
||||
double edgeChance = (1.0D - distance) / 0.3D * 0.85D;
|
||||
if (random.nextFloat() > edgeChance) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
BlockPos pos = center.offset(dx, dy, dz);
|
||||
if (pos.getY() < minBuildHeight || pos.getY() >= maxBuildHeight) {
|
||||
continue;
|
||||
}
|
||||
|
||||
long packed = pos.asLong();
|
||||
if (visited.contains(packed)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
BlockState state = level.getBlockState(pos);
|
||||
if (!state.is(AntarchyTags.Blocks.MINERS_DREAM_CARVABLE)) {
|
||||
continue;
|
||||
}
|
||||
if (level.getBlockEntity(pos) != null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
visited.add(packed);
|
||||
out.add(pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isChunkLoaded(ServerLevel level, BlockPos pos) {
|
||||
return level.isLoaded(pos);
|
||||
}
|
||||
}
|
||||
-152
@@ -1,152 +0,0 @@
|
||||
package com.craisinlord.antarchy.content.item;
|
||||
|
||||
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.particles.BlockParticleOption;
|
||||
import net.minecraft.core.particles.DustParticleOptions;
|
||||
import net.minecraft.core.particles.ParticleTypes;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.sounds.SoundEvents;
|
||||
import net.minecraft.sounds.SoundSource;
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import org.joml.Vector3f;
|
||||
|
||||
public final class MinersDreamExcavation {
|
||||
private static final Vector3f TRIFFID_GOO_COLOR = new Vector3f(0.35F, 0.85F, 0.4F);
|
||||
private static final int SLICES_BETWEEN_RUMBLE = 4;
|
||||
|
||||
private final UUID owner;
|
||||
private final ResourceKey<Level> dimension;
|
||||
private final List<MinersDreamCaveGenerator.CaveNode> nodes;
|
||||
private final Set<Integer> torchCheckpoints;
|
||||
private final LongOpenHashSet visited = new LongOpenHashSet();
|
||||
|
||||
private int nodeIndex = 0;
|
||||
private List<BlockPos> currentNodePositions = null;
|
||||
private int positionCursor = 0;
|
||||
private int slicesSinceRumble = 0;
|
||||
private boolean finished = false;
|
||||
|
||||
public MinersDreamExcavation(UUID owner, ResourceKey<Level> dimension, List<MinersDreamCaveGenerator.CaveNode> nodes, Set<Integer> torchCheckpoints) {
|
||||
this.owner = owner;
|
||||
this.dimension = dimension;
|
||||
this.nodes = nodes;
|
||||
this.torchCheckpoints = torchCheckpoints;
|
||||
}
|
||||
|
||||
public UUID owner() {
|
||||
return owner;
|
||||
}
|
||||
|
||||
public ResourceKey<Level> dimension() {
|
||||
return dimension;
|
||||
}
|
||||
|
||||
public boolean isFinished() {
|
||||
return finished;
|
||||
}
|
||||
|
||||
public void tick(ServerLevel level, int blockBudget) {
|
||||
RandomSource random = level.getRandom();
|
||||
int remaining = blockBudget;
|
||||
|
||||
while (remaining > 0 && !finished) {
|
||||
if (currentNodePositions == null) {
|
||||
if (nodeIndex >= nodes.size()) {
|
||||
finished = true;
|
||||
playCompletionSound(level);
|
||||
return;
|
||||
}
|
||||
|
||||
MinersDreamCaveGenerator.CaveNode node = nodes.get(nodeIndex);
|
||||
if (!MinersDreamCaveGenerator.isChunkLoaded(level, node.center())) {
|
||||
advanceNode(level, node);
|
||||
continue;
|
||||
}
|
||||
|
||||
currentNodePositions = new ArrayList<>();
|
||||
MinersDreamCaveGenerator.collectNodeCandidates(level, node, visited, random, currentNodePositions);
|
||||
positionCursor = 0;
|
||||
}
|
||||
|
||||
if (currentNodePositions.isEmpty()) {
|
||||
MinersDreamCaveGenerator.CaveNode node = nodes.get(nodeIndex);
|
||||
advanceNode(level, node);
|
||||
continue;
|
||||
}
|
||||
|
||||
int endIndex = Math.min(currentNodePositions.size(), positionCursor + remaining);
|
||||
for (; positionCursor < endIndex; positionCursor++) {
|
||||
BlockPos pos = currentNodePositions.get(positionCursor);
|
||||
BlockState removedState = level.getBlockState(pos);
|
||||
if (removedState.isAir()) {
|
||||
continue;
|
||||
}
|
||||
level.setBlock(pos, Blocks.AIR.defaultBlockState(), 3);
|
||||
remaining--;
|
||||
if (random.nextFloat() < 0.12F) {
|
||||
spawnBreakParticles(level, pos, removedState, random);
|
||||
}
|
||||
}
|
||||
|
||||
if (positionCursor >= currentNodePositions.size()) {
|
||||
MinersDreamCaveGenerator.CaveNode node = nodes.get(nodeIndex);
|
||||
advanceNode(level, node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void advanceNode(ServerLevel level, MinersDreamCaveGenerator.CaveNode node) {
|
||||
RandomSource random = level.getRandom();
|
||||
spawnFrontDust(level, node, random);
|
||||
slicesSinceRumble++;
|
||||
if (slicesSinceRumble >= SLICES_BETWEEN_RUMBLE) {
|
||||
slicesSinceRumble = 0;
|
||||
playRumble(level, node);
|
||||
}
|
||||
|
||||
if (torchCheckpoints.contains(nodeIndex)) {
|
||||
MinersDreamTorchPlacer.tryPlaceTorch(level, node.center(), random);
|
||||
}
|
||||
|
||||
currentNodePositions = null;
|
||||
positionCursor = 0;
|
||||
nodeIndex++;
|
||||
}
|
||||
|
||||
private void spawnBreakParticles(ServerLevel level, BlockPos pos, BlockState state, RandomSource random) {
|
||||
level.sendParticles(new BlockParticleOption(ParticleTypes.BLOCK, state),
|
||||
pos.getX() + 0.5D, pos.getY() + 0.5D, pos.getZ() + 0.5D, 2, 0.25D, 0.25D, 0.25D, 0.02D);
|
||||
if (random.nextFloat() < 0.2F) {
|
||||
level.sendParticles(new DustParticleOptions(TRIFFID_GOO_COLOR, 1.0F),
|
||||
pos.getX() + 0.5D, pos.getY() + 0.5D, pos.getZ() + 0.5D, 1, 0.2D, 0.2D, 0.2D, 0.0D);
|
||||
}
|
||||
}
|
||||
|
||||
private void spawnFrontDust(ServerLevel level, MinersDreamCaveGenerator.CaveNode node, RandomSource random) {
|
||||
BlockPos center = node.center();
|
||||
level.sendParticles(ParticleTypes.CLOUD, center.getX() + 0.5D, center.getY() + 0.5D, center.getZ() + 0.5D,
|
||||
3, node.horizontalRadius() * 0.4D, node.verticalRadius() * 0.3D, node.horizontalRadius() * 0.4D, 0.01D);
|
||||
}
|
||||
|
||||
private void playRumble(ServerLevel level, MinersDreamCaveGenerator.CaveNode node) {
|
||||
BlockPos center = node.center();
|
||||
level.playSound(null, center, SoundEvents.STONE_BREAK, SoundSource.BLOCKS, 0.5F, 0.7F);
|
||||
}
|
||||
|
||||
private void playCompletionSound(ServerLevel level) {
|
||||
if (nodes.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
BlockPos center = nodes.get(nodes.size() - 1).center();
|
||||
level.playSound(null, center, SoundEvents.BEACON_ACTIVATE, SoundSource.PLAYERS, 0.4F, 1.4F);
|
||||
}
|
||||
}
|
||||
-81
@@ -1,81 +0,0 @@
|
||||
package com.craisinlord.antarchy.content.item;
|
||||
|
||||
import com.craisinlord.antarchy.config.AntarchySettings;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.util.Mth;
|
||||
import net.minecraft.util.RandomSource;
|
||||
|
||||
public final class MinersDreamExcavationManager {
|
||||
private static final int MAX_ACTIVE_TASKS = 8;
|
||||
private static final int SAFE_MIN_RANGE = 8;
|
||||
private static final int SAFE_MAX_RANGE = 256;
|
||||
private static final Map<UUID, MinersDreamExcavation> ACTIVE = new ConcurrentHashMap<>();
|
||||
|
||||
private MinersDreamExcavationManager() {
|
||||
}
|
||||
|
||||
public static boolean hasActiveExcavation(UUID owner) {
|
||||
return ACTIVE.containsKey(owner);
|
||||
}
|
||||
|
||||
public static MinersDreamExcavation tryStart(ServerLevel level, UUID owner, BlockPos startPos, Direction direction) {
|
||||
if (ACTIVE.containsKey(owner) || ACTIVE.size() >= MAX_ACTIVE_TASKS) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int minRange = Mth.clamp(AntarchySettings.minersDreamMinimumRange(), SAFE_MIN_RANGE, SAFE_MAX_RANGE);
|
||||
int maxRange = Mth.clamp(AntarchySettings.minersDreamMaximumRange(), SAFE_MIN_RANGE, SAFE_MAX_RANGE);
|
||||
if (minRange > maxRange) {
|
||||
int swap = minRange;
|
||||
minRange = maxRange;
|
||||
maxRange = swap;
|
||||
}
|
||||
|
||||
RandomSource random = level.getRandom();
|
||||
int length = minRange == maxRange ? minRange : minRange + random.nextInt(maxRange - minRange + 1);
|
||||
|
||||
MinersDreamCaveGenerator.CaveGenerationResult result = MinersDreamCaveGenerator.generate(
|
||||
startPos,
|
||||
direction,
|
||||
length,
|
||||
AntarchySettings.minersDreamTorchSpacing(),
|
||||
level.getMinBuildHeight(),
|
||||
level.getMaxBuildHeight(),
|
||||
random
|
||||
);
|
||||
|
||||
if (result.nodes().isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
MinersDreamExcavation excavation = new MinersDreamExcavation(owner, level.dimension(), result.nodes(), result.torchCheckpoints());
|
||||
ACTIVE.put(owner, excavation);
|
||||
return excavation;
|
||||
}
|
||||
|
||||
public static void tick(ServerLevel level) {
|
||||
if (ACTIVE.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
int budget = Math.max(1, AntarchySettings.minersDreamBlocksPerTick());
|
||||
Iterator<Map.Entry<UUID, MinersDreamExcavation>> iterator = ACTIVE.entrySet().iterator();
|
||||
while (iterator.hasNext()) {
|
||||
MinersDreamExcavation excavation = iterator.next().getValue();
|
||||
if (!excavation.dimension().equals(level.dimension())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
excavation.tick(level, budget);
|
||||
if (excavation.isFinished()) {
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-79
@@ -1,79 +0,0 @@
|
||||
package com.craisinlord.antarchy.content.item;
|
||||
|
||||
import com.craisinlord.antarchy.config.AntarchySettings;
|
||||
import java.util.List;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.sounds.SoundEvents;
|
||||
import net.minecraft.sounds.SoundSource;
|
||||
import net.minecraft.world.InteractionResult;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.TooltipFlag;
|
||||
import net.minecraft.world.item.context.UseOnContext;
|
||||
import net.minecraft.world.level.Level;
|
||||
|
||||
public final class MinersDreamItem extends Item {
|
||||
public MinersDreamItem(Properties properties) {
|
||||
super(properties);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InteractionResult useOn(UseOnContext context) {
|
||||
Level level = context.getLevel();
|
||||
Player player = context.getPlayer();
|
||||
if (player == null) {
|
||||
return InteractionResult.PASS;
|
||||
}
|
||||
|
||||
if (!AntarchySettings.minersDreamEnabled()) {
|
||||
if (!level.isClientSide()) {
|
||||
player.displayClientMessage(Component.translatable("message.antarchy.miners_dream_disabled"), true);
|
||||
}
|
||||
return InteractionResult.FAIL;
|
||||
}
|
||||
|
||||
if (level.isClientSide()) {
|
||||
return InteractionResult.SUCCESS;
|
||||
}
|
||||
|
||||
ServerLevel serverLevel = (ServerLevel) level;
|
||||
BlockPos clickedPos = context.getClickedPos();
|
||||
Direction clickedFace = context.getClickedFace();
|
||||
Direction travelDirection = clickedFace.getAxis().isHorizontal() ? clickedFace : player.getDirection();
|
||||
BlockPos startPos = clickedPos.relative(travelDirection);
|
||||
|
||||
if (!serverLevel.isInWorldBounds(startPos) || !MinersDreamCaveGenerator.isChunkLoaded(serverLevel, startPos)) {
|
||||
return InteractionResult.FAIL;
|
||||
}
|
||||
|
||||
if (MinersDreamExcavationManager.hasActiveExcavation(player.getUUID())) {
|
||||
player.displayClientMessage(Component.translatable("message.antarchy.miners_dream_busy"), true);
|
||||
return InteractionResult.FAIL;
|
||||
}
|
||||
|
||||
MinersDreamExcavation excavation = MinersDreamExcavationManager.tryStart(serverLevel, player.getUUID(), startPos, travelDirection);
|
||||
if (excavation == null) {
|
||||
return InteractionResult.FAIL;
|
||||
}
|
||||
|
||||
serverLevel.playSound(null, clickedPos, SoundEvents.TNT_PRIMED, SoundSource.PLAYERS, 0.8F, 1.1F);
|
||||
|
||||
if (!player.getAbilities().instabuild) {
|
||||
context.getItemInHand().shrink(1);
|
||||
}
|
||||
|
||||
return InteractionResult.CONSUME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appendHoverText(ItemStack stack, Level level, List<Component> tooltipComponents, TooltipFlag tooltipFlag) {
|
||||
tooltipComponents.add(Component.translatable("tooltip.antarchy.miners_dream.line1").withStyle(ChatFormatting.GRAY));
|
||||
tooltipComponents.add(Component.translatable("tooltip.antarchy.miners_dream.line2").withStyle(ChatFormatting.GRAY));
|
||||
super.appendHoverText(stack, level, tooltipComponents, tooltipFlag);
|
||||
}
|
||||
}
|
||||
-89
@@ -1,89 +0,0 @@
|
||||
package com.craisinlord.antarchy.content.item;
|
||||
|
||||
import java.util.List;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.sounds.SoundSource;
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
|
||||
|
||||
public final class MinersDreamTorchPlacer {
|
||||
private MinersDreamTorchPlacer() {
|
||||
}
|
||||
|
||||
public static void tryPlaceTorch(ServerLevel level, BlockPos center, RandomSource random) {
|
||||
BlockPos floorSpot = findFloorSpot(level, center);
|
||||
boolean floorFirst = random.nextBoolean();
|
||||
|
||||
if (floorFirst) {
|
||||
if (floorSpot != null && placeFloorTorch(level, floorSpot)) {
|
||||
return;
|
||||
}
|
||||
placeWallTorch(level, center, random);
|
||||
return;
|
||||
}
|
||||
|
||||
if (placeWallTorch(level, center, random)) {
|
||||
return;
|
||||
}
|
||||
if (floorSpot != null) {
|
||||
placeFloorTorch(level, floorSpot);
|
||||
}
|
||||
}
|
||||
|
||||
private static BlockPos findFloorSpot(ServerLevel level, BlockPos center) {
|
||||
for (int dy = -2; dy <= 1; dy++) {
|
||||
BlockPos candidate = center.offset(0, dy, 0);
|
||||
if (!isOpenForTorch(level, candidate)) {
|
||||
continue;
|
||||
}
|
||||
BlockState below = level.getBlockState(candidate.below());
|
||||
if (below.isFaceSturdy(level, candidate.below(), Direction.UP)) {
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static boolean placeFloorTorch(ServerLevel level, BlockPos pos) {
|
||||
if (!isOpenForTorch(level, pos)) {
|
||||
return false;
|
||||
}
|
||||
BlockState state = Blocks.TORCH.defaultBlockState();
|
||||
if (!state.canSurvive(level, pos)) {
|
||||
return false;
|
||||
}
|
||||
level.setBlock(pos, state, 3);
|
||||
level.playSound(null, pos, state.getSoundType().getPlaceSound(), SoundSource.BLOCKS, 1.0F, 1.0F);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean placeWallTorch(ServerLevel level, BlockPos center, RandomSource random) {
|
||||
List<Direction> order = Direction.Plane.HORIZONTAL.shuffledCopy(random);
|
||||
for (int dy = -1; dy <= 1; dy++) {
|
||||
BlockPos candidate = center.offset(0, dy, 0);
|
||||
if (!isOpenForTorch(level, candidate)) {
|
||||
continue;
|
||||
}
|
||||
for (Direction wallSide : order) {
|
||||
Direction facing = wallSide.getOpposite();
|
||||
BlockState state = Blocks.WALL_TORCH.defaultBlockState().setValue(BlockStateProperties.HORIZONTAL_FACING, facing);
|
||||
if (!state.canSurvive(level, candidate)) {
|
||||
continue;
|
||||
}
|
||||
level.setBlock(candidate, state, 3);
|
||||
level.playSound(null, candidate, state.getSoundType().getPlaceSound(), SoundSource.BLOCKS, 1.0F, 1.0F);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean isOpenForTorch(ServerLevel level, BlockPos pos) {
|
||||
BlockState state = level.getBlockState(pos);
|
||||
return (state.isAir() || state.canBeReplaced()) && level.getFluidState(pos).isEmpty();
|
||||
}
|
||||
}
|
||||
@@ -411,11 +411,6 @@
|
||||
"tooltip.antarchy.reverie_bottle.capture": "Rechtsklicke mit einer Glasflasche auf eine Reverie, um sie einzufangen.",
|
||||
"tooltip.antarchy.reverie_bottle.release": "Rechtsklick, um die Reverie freizulassen.",
|
||||
"item.antarchy.gravity_gun": "Gravitationskanone",
|
||||
"item.antarchy.miners_dream": "Traum des Bergmanns",
|
||||
"tooltip.antarchy.miners_dream.line1": "Rechtsklick, um eine gewundene Höhle zu graben.",
|
||||
"tooltip.antarchy.miners_dream.line2": "Lässt wertvolle Blöcke unberührt.",
|
||||
"message.antarchy.miners_dream_disabled": "Der Traum des Bergmanns ist deaktiviert.",
|
||||
"message.antarchy.miners_dream_busy": "Dein vorheriger Traum entfaltet sich noch.",
|
||||
"item.antarchy.diamond_minecart": "Die Diamantlore",
|
||||
"item.antarchy.cogwheel_pet": "Zahnrad-Haustier",
|
||||
"item.antarchy.fire_dragon_pet": "Feuerdrachen-Haustier",
|
||||
|
||||
@@ -632,11 +632,6 @@
|
||||
"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.miners_dream": "Miner's Dream",
|
||||
"tooltip.antarchy.miners_dream.line1": "Use to carve a winding cave.",
|
||||
"tooltip.antarchy.miners_dream.line2": "Leaves valuable blocks untouched.",
|
||||
"message.antarchy.miners_dream_disabled": "The Miner's Dream is disabled.",
|
||||
"message.antarchy.miners_dream_busy": "Your previous dream is still unfolding.",
|
||||
"item.antarchy.diamond_minecart": "The Diamond Minecart",
|
||||
"item.antarchy.cogwheel_pet": "Cogwheel Pet",
|
||||
"item.antarchy.fire_dragon_pet": "Fire Dragon Pet",
|
||||
|
||||
@@ -411,11 +411,6 @@
|
||||
"tooltip.antarchy.reverie_bottle.capture": "Haz clic derecho en una Reverie con una botella de cristal para guardarla.",
|
||||
"tooltip.antarchy.reverie_bottle.release": "Clic derecho para liberar a la Reverie.",
|
||||
"item.antarchy.gravity_gun": "Pistola gravitatoria",
|
||||
"item.antarchy.miners_dream": "Sueño del minero",
|
||||
"tooltip.antarchy.miners_dream.line1": "Clic derecho para excavar una cueva sinuosa.",
|
||||
"tooltip.antarchy.miners_dream.line2": "Deja intactos los bloques valiosos.",
|
||||
"message.antarchy.miners_dream_disabled": "El Sueño del minero está desactivado.",
|
||||
"message.antarchy.miners_dream_busy": "Tu sueño anterior todavía se está desarrollando.",
|
||||
"item.antarchy.diamond_minecart": "La vagoneta de diamante",
|
||||
"item.antarchy.cogwheel_pet": "Mascota engranaje",
|
||||
"item.antarchy.fire_dragon_pet": "Mascota de dragón de fuego",
|
||||
|
||||
@@ -411,11 +411,6 @@
|
||||
"tooltip.antarchy.reverie_bottle.capture": "Faites un clic droit sur une Reverie avec une fiole en verre pour la capturer.",
|
||||
"tooltip.antarchy.reverie_bottle.release": "Clic droit pour libérer la Reverie.",
|
||||
"item.antarchy.gravity_gun": "Canon à gravité",
|
||||
"item.antarchy.miners_dream": "Rêve du mineur",
|
||||
"tooltip.antarchy.miners_dream.line1": "Clic droit pour creuser une grotte sinueuse.",
|
||||
"tooltip.antarchy.miners_dream.line2": "Laisse les blocs précieux intacts.",
|
||||
"message.antarchy.miners_dream_disabled": "Le Rêve du mineur est désactivé.",
|
||||
"message.antarchy.miners_dream_busy": "Votre rêve précédent est encore en train de se déployer.",
|
||||
"item.antarchy.diamond_minecart": "Le wagonnet de diamant",
|
||||
"item.antarchy.cogwheel_pet": "Familier engrenage",
|
||||
"item.antarchy.fire_dragon_pet": "Familier dragon de feu",
|
||||
|
||||
@@ -411,11 +411,6 @@
|
||||
"tooltip.antarchy.reverie_bottle.capture": "Fai clic destro su una Reverie con una bottiglia di vetro per catturarla.",
|
||||
"tooltip.antarchy.reverie_bottle.release": "Fai clic destro per liberare la Reverie.",
|
||||
"item.antarchy.gravity_gun": "Cannone gravitazionale",
|
||||
"item.antarchy.miners_dream": "Sogno del minatore",
|
||||
"tooltip.antarchy.miners_dream.line1": "Fai clic destro per scavare una grotta tortuosa.",
|
||||
"tooltip.antarchy.miners_dream.line2": "Lascia intatti i blocchi preziosi.",
|
||||
"message.antarchy.miners_dream_disabled": "Il Sogno del minatore è disattivato.",
|
||||
"message.antarchy.miners_dream_busy": "Il tuo sogno precedente si sta ancora dispiegando.",
|
||||
"item.antarchy.diamond_minecart": "Il carrello di diamante",
|
||||
"item.antarchy.cogwheel_pet": "Famiglio ingranaggio",
|
||||
"item.antarchy.fire_dragon_pet": "Famiglio drago di fuoco",
|
||||
|
||||
@@ -411,11 +411,6 @@
|
||||
"tooltip.antarchy.reverie_bottle.capture": "ガラス瓶を持ってレヴェリーを右クリックすると収納できます。",
|
||||
"tooltip.antarchy.reverie_bottle.release": "右クリックでレヴェリーを解放します。",
|
||||
"item.antarchy.gravity_gun": "重力銃",
|
||||
"item.antarchy.miners_dream": "採掘者の夢",
|
||||
"tooltip.antarchy.miners_dream.line1": "右クリックで曲がりくねった洞窟を掘ります。",
|
||||
"tooltip.antarchy.miners_dream.line2": "貴重なブロックはそのまま残します。",
|
||||
"message.antarchy.miners_dream_disabled": "採掘者の夢は無効になっています。",
|
||||
"message.antarchy.miners_dream_busy": "前の夢はまだ展開中です。",
|
||||
"item.antarchy.diamond_minecart": "ダイヤモンドのトロッコ",
|
||||
"item.antarchy.cogwheel_pet": "歯車ペット",
|
||||
"item.antarchy.fire_dragon_pet": "ファイアドラゴンのペット",
|
||||
|
||||
@@ -411,11 +411,6 @@
|
||||
"tooltip.antarchy.reverie_bottle.capture": "유리병으로 레버리를 우클릭하여 담습니다.",
|
||||
"tooltip.antarchy.reverie_bottle.release": "우클릭하여 레버리를 풀어줍니다.",
|
||||
"item.antarchy.gravity_gun": "중력총",
|
||||
"item.antarchy.miners_dream": "광부의 꿈",
|
||||
"tooltip.antarchy.miners_dream.line1": "우클릭하여 구불구불한 동굴을 뚫습니다.",
|
||||
"tooltip.antarchy.miners_dream.line2": "귀중한 블록은 건드리지 않습니다.",
|
||||
"message.antarchy.miners_dream_disabled": "광부의 꿈이 비활성화되어 있습니다.",
|
||||
"message.antarchy.miners_dream_busy": "이전 꿈이 아직 펼쳐지고 있습니다.",
|
||||
"item.antarchy.diamond_minecart": "다이아몬드 광산 수레",
|
||||
"item.antarchy.cogwheel_pet": "톱니바퀴 펫",
|
||||
"item.antarchy.fire_dragon_pet": "화염 드래곤 펫",
|
||||
|
||||
@@ -411,11 +411,6 @@
|
||||
"tooltip.antarchy.reverie_bottle.capture": "Clique com o botão direito em uma Reverie com um frasco de vidro para guardá-la.",
|
||||
"tooltip.antarchy.reverie_bottle.release": "Clique com o botão direito para soltar a Reverie.",
|
||||
"item.antarchy.gravity_gun": "Arma Gravitacional",
|
||||
"item.antarchy.miners_dream": "Sonho do Minerador",
|
||||
"tooltip.antarchy.miners_dream.line1": "Clique com o botão direito para escavar uma caverna sinuosa.",
|
||||
"tooltip.antarchy.miners_dream.line2": "Deixa blocos valiosos intactos.",
|
||||
"message.antarchy.miners_dream_disabled": "O Sonho do Minerador está desativado.",
|
||||
"message.antarchy.miners_dream_busy": "Seu sonho anterior ainda está se desenrolando.",
|
||||
"item.antarchy.diamond_minecart": "O Carrinho de Diamante",
|
||||
"item.antarchy.cogwheel_pet": "Mascote de Engrenagem",
|
||||
"item.antarchy.fire_dragon_pet": "Mascote de Dragão de Fogo",
|
||||
|
||||
@@ -387,11 +387,6 @@
|
||||
"tooltip.antarchy.reverie_bottle.capture": "Нажмите ПКМ стеклянной бутылочкой по грёзе, чтобы поймать её.",
|
||||
"tooltip.antarchy.reverie_bottle.release": "Нажмите ПКМ, чтобы выпустить грёзу.",
|
||||
"item.antarchy.gravity_gun": "Гравитационная пушка",
|
||||
"item.antarchy.miners_dream": "Мечта шахтёра",
|
||||
"tooltip.antarchy.miners_dream.line1": "Нажмите ПКМ, чтобы вырезать извилистую пещеру.",
|
||||
"tooltip.antarchy.miners_dream.line2": "Оставляет ценные блоки нетронутыми.",
|
||||
"message.antarchy.miners_dream_disabled": "Мечта шахтёра отключена.",
|
||||
"message.antarchy.miners_dream_busy": "Ваша предыдущая мечта всё ещё воплощается.",
|
||||
"item.antarchy.diamond_minecart": "Алмазная вагонетка",
|
||||
"item.antarchy.cogwheel_pet": "Шестерёнка-питомец",
|
||||
"item.antarchy.fire_dragon_pet": "Огненный дракон-питомец",
|
||||
|
||||
@@ -411,11 +411,6 @@
|
||||
"tooltip.antarchy.reverie_bottle.capture": "手持玻璃瓶右键雷弗里即可将其收纳。",
|
||||
"tooltip.antarchy.reverie_bottle.release": "右键释放雷弗里。",
|
||||
"item.antarchy.gravity_gun": "重力枪",
|
||||
"item.antarchy.miners_dream": "矿工之梦",
|
||||
"tooltip.antarchy.miners_dream.line1": "右键挖出一条蜿蜒洞穴。",
|
||||
"tooltip.antarchy.miners_dream.line2": "不会破坏贵重方块。",
|
||||
"message.antarchy.miners_dream_disabled": "矿工之梦已禁用。",
|
||||
"message.antarchy.miners_dream_busy": "你之前的梦仍在展开。",
|
||||
"item.antarchy.diamond_minecart": "钻石矿车",
|
||||
"item.antarchy.cogwheel_pet": "齿轮宠物",
|
||||
"item.antarchy.fire_dragon_pet": "火龙宠物",
|
||||
|
||||
@@ -411,11 +411,6 @@
|
||||
"tooltip.antarchy.reverie_bottle.capture": "手持玻璃瓶右鍵雷弗裡即可將其收納。",
|
||||
"tooltip.antarchy.reverie_bottle.release": "右鍵釋放雷弗裡。",
|
||||
"item.antarchy.gravity_gun": "重力槍",
|
||||
"item.antarchy.miners_dream": "礦工之夢",
|
||||
"tooltip.antarchy.miners_dream.line1": "右鍵挖出一條蜿蜒洞穴。",
|
||||
"tooltip.antarchy.miners_dream.line2": "不會破壞貴重方塊。",
|
||||
"message.antarchy.miners_dream_disabled": "礦工之夢已禁用。",
|
||||
"message.antarchy.miners_dream_busy": "你之前的夢仍在展開。",
|
||||
"item.antarchy.diamond_minecart": "鑽石礦車",
|
||||
"item.antarchy.cogwheel_pet": "齒輪寵物",
|
||||
"item.antarchy.fire_dragon_pet": "火龍寵物",
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
{
|
||||
"parent": "minecraft:item/generated",
|
||||
"textures": {
|
||||
"layer0": "antarchy:item/miners_dream"
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 425 B |
@@ -1,19 +0,0 @@
|
||||
{
|
||||
"type": "minecraft:crafting_shaped",
|
||||
"category": "misc",
|
||||
"pattern": [
|
||||
"GGG",
|
||||
"RDR",
|
||||
"TTT"
|
||||
],
|
||||
"key": {
|
||||
"G": { "item": "antarchy:triffid_goo" },
|
||||
"R": { "item": "minecraft:redstone_block" },
|
||||
"D": { "item": "minecraft:diamond_pickaxe" },
|
||||
"T": { "item": "minecraft:tnt" }
|
||||
},
|
||||
"result": {
|
||||
"id": "antarchy:miners_dream",
|
||||
"count": 1
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
{
|
||||
"type": "minecraft:crafting_shaped",
|
||||
"category": "misc",
|
||||
"pattern": [
|
||||
"GGG",
|
||||
"RDR",
|
||||
"TTT"
|
||||
],
|
||||
"key": {
|
||||
"G": { "item": "antarchy:triffid_goo" },
|
||||
"R": { "item": "minecraft:redstone_block" },
|
||||
"D": { "item": "minecraft:diamond_pickaxe" },
|
||||
"T": { "item": "minecraft:tnt" }
|
||||
},
|
||||
"result": {
|
||||
"item": "antarchy:miners_dream",
|
||||
"count": 1
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
{
|
||||
"replace": false,
|
||||
"values": [
|
||||
"#minecraft:base_stone_overworld",
|
||||
"#minecraft:base_stone_nether",
|
||||
"minecraft:stone",
|
||||
"minecraft:deepslate",
|
||||
"minecraft:granite",
|
||||
"minecraft:diorite",
|
||||
"minecraft:andesite",
|
||||
"minecraft:tuff",
|
||||
"minecraft:calcite",
|
||||
"minecraft:netherrack",
|
||||
"minecraft:blackstone",
|
||||
"minecraft:basalt",
|
||||
"minecraft:smooth_basalt",
|
||||
"minecraft:end_stone",
|
||||
"minecraft:dirt",
|
||||
"minecraft:coarse_dirt",
|
||||
"minecraft:rooted_dirt",
|
||||
"minecraft:podzol",
|
||||
"minecraft:mycelium",
|
||||
"minecraft:gravel",
|
||||
"minecraft:sand",
|
||||
"minecraft:red_sand",
|
||||
"minecraft:sandstone",
|
||||
"minecraft:red_sandstone",
|
||||
"minecraft:clay",
|
||||
"minecraft:soul_sand",
|
||||
"minecraft:soul_soil",
|
||||
"antarchy:nyxite",
|
||||
"antarchy:pale_nyxite",
|
||||
"antarchy:dream_sand",
|
||||
"antarchy:dream_sandstone"
|
||||
]
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
{
|
||||
"replace": false,
|
||||
"values": [
|
||||
"#minecraft:base_stone_overworld",
|
||||
"#minecraft:base_stone_nether",
|
||||
"minecraft:stone",
|
||||
"minecraft:deepslate",
|
||||
"minecraft:granite",
|
||||
"minecraft:diorite",
|
||||
"minecraft:andesite",
|
||||
"minecraft:tuff",
|
||||
"minecraft:calcite",
|
||||
"minecraft:netherrack",
|
||||
"minecraft:blackstone",
|
||||
"minecraft:basalt",
|
||||
"minecraft:smooth_basalt",
|
||||
"minecraft:end_stone",
|
||||
"minecraft:dirt",
|
||||
"minecraft:coarse_dirt",
|
||||
"minecraft:rooted_dirt",
|
||||
"minecraft:podzol",
|
||||
"minecraft:mycelium",
|
||||
"minecraft:gravel",
|
||||
"minecraft:sand",
|
||||
"minecraft:red_sand",
|
||||
"minecraft:sandstone",
|
||||
"minecraft:red_sandstone",
|
||||
"minecraft:clay",
|
||||
"minecraft:soul_sand",
|
||||
"minecraft:soul_soil",
|
||||
"antarchy:nyxite",
|
||||
"antarchy:pale_nyxite",
|
||||
"antarchy:dream_sand",
|
||||
"antarchy:dream_sandstone"
|
||||
]
|
||||
}
|
||||
+8
-1
@@ -137,6 +137,14 @@ public final class AntarchyConfigCatalog {
|
||||
|| name.equals("elythiaPortalEnabled")
|
||||
|| name.equals("thoraxisPortalEnabled")
|
||||
|| name.equals("cavarynPortalEnabled")
|
||||
|| name.startsWith("portalGunCanFireThrough")
|
||||
|| name.startsWith("portalGunSeeThrough")
|
||||
|| name.startsWith("portalGunFancy")
|
||||
|| name.equals("portalGunCanPortalsResizeWhenCreated")
|
||||
|| name.equals("portalGunCanPortalProjectilesChunkload")
|
||||
|| name.equals("portalGunMaxShootDistance")
|
||||
|| name.equals("portalGunEntityGrabWeightBase")
|
||||
|| name.equals("portalGunIndicatorSize")
|
||||
|| name.equals("hushweedSporeLifetimeSeconds")
|
||||
|| name.equals("elythiaFireflyParticlesEnabled")
|
||||
|| name.equals("duplicatorTreeEnabled")
|
||||
@@ -177,7 +185,6 @@ public final class AntarchyConfigCatalog {
|
||||
|| name.equals("invertProjectilesFromInvertedPlayers")
|
||||
|| name.startsWith("gravityGun")
|
||||
|| name.startsWith("critterCage")
|
||||
|| name.startsWith("minersDream")
|
||||
|| name.equals("potentNyxiteInvertedDurationSeconds")
|
||||
|| name.equals("corneaEarNightVisionSeconds")
|
||||
|| name.startsWith("american")
|
||||
|
||||
@@ -167,11 +167,6 @@ public final class AntarchySettings {
|
||||
private static double gravityGunMaxHoldDistance = 10.0D;
|
||||
private static double critterCageMaxCapturableWidth = 1.65D;
|
||||
private static double critterCageMaxCapturableHeight = 1.65D;
|
||||
private static boolean minersDreamEnabled = true;
|
||||
private static int minersDreamMinimumRange = 48;
|
||||
private static int minersDreamMaximumRange = 96;
|
||||
private static int minersDreamTorchSpacing = 10;
|
||||
private static int minersDreamBlocksPerTick = 150;
|
||||
private static double lucidAttackRange = 9.0D;
|
||||
private static double lucidPearlInvertedDurationSeconds = 6.0D;
|
||||
private static int corneaEarNightVisionSeconds = 15;
|
||||
@@ -554,6 +549,15 @@ public final class AntarchySettings {
|
||||
private static boolean elythiaPortalEnabled = true;
|
||||
private static boolean thoraxisPortalEnabled = true;
|
||||
private static boolean cavarynPortalEnabled = true;
|
||||
private static boolean portalGunCanFireThroughGlass = false;
|
||||
private static boolean portalGunCanFireThroughLiquid = false;
|
||||
private static boolean portalGunCanPortalsResizeWhenCreated = true;
|
||||
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 boolean elythiaFireflyParticlesEnabled = true;
|
||||
private static int bloodCrystalArmorShieldRechargeTicks = 600;
|
||||
private static int bloodCrystalAppleShieldCount = 2;
|
||||
@@ -1251,26 +1255,6 @@ public final class AntarchySettings {
|
||||
return critterCageMaxCapturableHeight;
|
||||
}
|
||||
|
||||
public static boolean minersDreamEnabled() {
|
||||
return minersDreamEnabled;
|
||||
}
|
||||
|
||||
public static int minersDreamMinimumRange() {
|
||||
return minersDreamMinimumRange;
|
||||
}
|
||||
|
||||
public static int minersDreamMaximumRange() {
|
||||
return minersDreamMaximumRange;
|
||||
}
|
||||
|
||||
public static int minersDreamTorchSpacing() {
|
||||
return minersDreamTorchSpacing;
|
||||
}
|
||||
|
||||
public static int minersDreamBlocksPerTick() {
|
||||
return minersDreamBlocksPerTick;
|
||||
}
|
||||
|
||||
public static void setDisableInfinityBookPortalCreation(boolean value) {
|
||||
disableInfinityBookPortalCreation = value;
|
||||
}
|
||||
@@ -1327,11 +1311,56 @@ public final class AntarchySettings {
|
||||
cavarynPortalEnabled = value;
|
||||
}
|
||||
|
||||
public static void setPortalGunCanFireThroughGlass(boolean value) {
|
||||
portalGunCanFireThroughGlass = value;
|
||||
}
|
||||
|
||||
public static void setPortalGunCanFireThroughLiquid(boolean value) {
|
||||
portalGunCanFireThroughLiquid = value;
|
||||
}
|
||||
|
||||
public static void setPortalGunCanPortalsResizeWhenCreated(boolean value) {
|
||||
portalGunCanPortalsResizeWhenCreated = value;
|
||||
}
|
||||
|
||||
public static void setPortalGunCanPortalProjectilesChunkload(boolean value) {
|
||||
portalGunCanPortalProjectilesChunkload = value;
|
||||
}
|
||||
|
||||
public static void setPortalGunSeeThroughPortals(boolean value) {
|
||||
portalGunSeeThroughPortals = value;
|
||||
}
|
||||
|
||||
public static void setPortalGunFancyPortals(boolean value) {
|
||||
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 boolean permanentPortalsEnabled() { return permanentPortalsEnabled; }
|
||||
public static boolean permanentPortalsFlintAndSteelEnabled() { return permanentPortalsFlintAndSteelEnabled; }
|
||||
public static boolean elythiaPortalEnabled() { return elythiaPortalEnabled; }
|
||||
public static boolean thoraxisPortalEnabled() { return thoraxisPortalEnabled; }
|
||||
public static boolean cavarynPortalEnabled() { return cavarynPortalEnabled; }
|
||||
public static boolean portalGunCanFireThroughGlass() { return portalGunCanFireThroughGlass; }
|
||||
public static boolean portalGunCanFireThroughLiquid() { return portalGunCanFireThroughLiquid; }
|
||||
public static boolean portalGunCanPortalsResizeWhenCreated() { return portalGunCanPortalsResizeWhenCreated; }
|
||||
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 void setBrownAntRequiresReagent(boolean value) {
|
||||
brownAntRequiresReagent = value;
|
||||
@@ -1809,26 +1838,6 @@ public final class AntarchySettings {
|
||||
critterCageMaxCapturableHeight = value;
|
||||
}
|
||||
|
||||
public static void setMinersDreamEnabled(boolean value) {
|
||||
minersDreamEnabled = value;
|
||||
}
|
||||
|
||||
public static void setMinersDreamMinimumRange(int value) {
|
||||
minersDreamMinimumRange = value;
|
||||
}
|
||||
|
||||
public static void setMinersDreamMaximumRange(int value) {
|
||||
minersDreamMaximumRange = value;
|
||||
}
|
||||
|
||||
public static void setMinersDreamTorchSpacing(int value) {
|
||||
minersDreamTorchSpacing = value;
|
||||
}
|
||||
|
||||
public static void setMinersDreamBlocksPerTick(int value) {
|
||||
minersDreamBlocksPerTick = value;
|
||||
}
|
||||
|
||||
public static double lucidAttackRange() {
|
||||
return lucidAttackRange;
|
||||
}
|
||||
|
||||
@@ -41,7 +41,6 @@ public final class AntarchyTags {
|
||||
public static final TagKey<Block> CAVARYN_PORTAL_FRAMES = TagKey.create(Registries.BLOCK, id("cavaryn_portal_frames"));
|
||||
public static final TagKey<Block> RAINBOW_PORTAL_FRAMES = TagKey.create(Registries.BLOCK, id("rainbow_portal_frames"));
|
||||
public static final TagKey<Block> ROLLY_CAVERN_CARVEABLE = TagKey.create(Registries.BLOCK, id("rolly_cavern_carveable"));
|
||||
public static final TagKey<Block> MINERS_DREAM_CARVABLE = TagKey.create(Registries.BLOCK, id("miners_dream_carvable"));
|
||||
public static final TagKey<Block> BLUESTONE_COMPONENTS = TagKey.create(Registries.BLOCK, id("bluestone_components"));
|
||||
public static final TagKey<Block> BLUESTONE_CONNECTABLE = TagKey.create(Registries.BLOCK, id("bluestone_connectable"));
|
||||
public static final TagKey<Block> ANTIMETAL_INVERSION_BLOCKS = TagKey.create(Registries.BLOCK, id("antimetal_inversion_blocks"));
|
||||
|
||||
+42
-5
@@ -1,6 +1,8 @@
|
||||
package com.craisinlord.antarchy.content.block.entity;
|
||||
|
||||
import com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity;
|
||||
import java.util.EnumMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Supplier;
|
||||
import net.minecraft.core.BlockPos;
|
||||
@@ -14,7 +16,8 @@ import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
|
||||
public class PortalGunPortalBaseBlockEntity extends BlockEntity {
|
||||
public class PortalGunPortalBaseBlockEntity extends BlockEntity implements PortalGunPortalCellAccess {
|
||||
private final Map<net.minecraft.core.Direction, PortalGunPortalFaceRecord> faceRecords = new EnumMap<>(net.minecraft.core.Direction.class);
|
||||
private UUID ownerId;
|
||||
private UUID portalId;
|
||||
private PortalGunPortalEntity.PortalSide side = PortalGunPortalEntity.PortalSide.BLUE;
|
||||
@@ -26,6 +29,11 @@ public class PortalGunPortalBaseBlockEntity extends BlockEntity {
|
||||
super(typeSupplier.get(), pos, state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<net.minecraft.core.Direction, PortalGunPortalFaceRecord> portalGun$getFaceRecords() {
|
||||
return this.faceRecords;
|
||||
}
|
||||
|
||||
public void configure(UUID ownerId, UUID portalId, PortalGunPortalEntity.PortalSide side, BlockPos masterPos) {
|
||||
this.ownerId = ownerId;
|
||||
this.portalId = portalId;
|
||||
@@ -61,6 +69,11 @@ public class PortalGunPortalBaseBlockEntity extends BlockEntity {
|
||||
public void updatePair(UUID linkedPortalId, int pairTime) {
|
||||
this.linkedPortalId = linkedPortalId;
|
||||
this.pairTime = pairTime;
|
||||
for (PortalGunPortalFaceRecord record : java.util.List.copyOf(this.faceRecords.values())) {
|
||||
if (record.portalId().equals(this.portalId)) {
|
||||
this.portalGun$putFaceRecord(record.withPair(linkedPortalId, pairTime));
|
||||
}
|
||||
}
|
||||
this.setChanged();
|
||||
}
|
||||
|
||||
@@ -69,27 +82,49 @@ public class PortalGunPortalBaseBlockEntity extends BlockEntity {
|
||||
}
|
||||
|
||||
public void onBroken() {
|
||||
if (!(this.level instanceof ServerLevel serverLevel) || this.portalId == null) {
|
||||
if (!(this.level instanceof ServerLevel serverLevel)) {
|
||||
return;
|
||||
}
|
||||
if (serverLevel.getEntity(this.portalId) instanceof PortalGunPortalEntity portal && !portal.isRemoved()) {
|
||||
java.util.Set<UUID> portalIds = new java.util.HashSet<>();
|
||||
if (this.portalId != null) {
|
||||
portalIds.add(this.portalId);
|
||||
}
|
||||
for (PortalGunPortalFaceRecord record : this.faceRecords.values()) {
|
||||
portalIds.add(record.portalId());
|
||||
}
|
||||
for (UUID id : portalIds) {
|
||||
if (serverLevel.getEntity(id) instanceof PortalGunPortalEntity portal && !portal.isRemoved()) {
|
||||
portal.discard();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void serverTick(ServerLevel level, BlockPos pos, BlockState state, PortalGunPortalBaseBlockEntity blockEntity) {
|
||||
if (((level.getGameTime() + pos.asLong()) & 15L) != 0L) {
|
||||
return;
|
||||
}
|
||||
if (level.getBlockEntity(blockEntity.masterPos) instanceof PortalGunPortalMasterBlockEntity master
|
||||
&& master.matches(blockEntity.ownerId, blockEntity.portalId, blockEntity.side)) {
|
||||
PortalGunPortalFaceRecord masterRecord = master.portalGun$getFaceRecord(master.getFacing(), blockEntity.portalId);
|
||||
if (masterRecord != null && blockEntity.portalGun$getFaceRecord(masterRecord.face(), masterRecord.portalId()) == null) {
|
||||
blockEntity.portalGun$putFaceRecord(masterRecord.withMaster(false));
|
||||
}
|
||||
}
|
||||
blockEntity.portalGun$restoreMasterFaces(level, pos);
|
||||
if (blockEntity.portalId != null
|
||||
&& !(level.getEntity(blockEntity.portalId) instanceof PortalGunPortalEntity)
|
||||
&& blockEntity.faceRecords.values().stream().anyMatch(record -> !record.portalId().equals(blockEntity.portalId))) {
|
||||
return;
|
||||
}
|
||||
if (!(level.getBlockEntity(blockEntity.masterPos) instanceof PortalGunPortalMasterBlockEntity master)
|
||||
|| !master.matches(blockEntity.ownerId, blockEntity.portalId, blockEntity.side)
|
||||
|| !pos.equals(master.getBasePos())) {
|
||||
|| !master.containsPortalSpot(pos)) {
|
||||
level.removeBlock(pos, false);
|
||||
return;
|
||||
}
|
||||
if (!(level.getEntity(blockEntity.portalId) instanceof PortalGunPortalEntity portal)
|
||||
|| portal.isRemoved()
|
||||
|| !pos.equals(portal.getBasePos())
|
||||
|| !portal.containsPortalSpot(pos)
|
||||
|| blockEntity.ownerId == null
|
||||
|| !blockEntity.ownerId.equals(portal.getOwnerId())
|
||||
|| blockEntity.side != portal.getPortalSide()) {
|
||||
@@ -123,6 +158,7 @@ public class PortalGunPortalBaseBlockEntity extends BlockEntity {
|
||||
tag.putInt("MasterY", this.masterPos.getY());
|
||||
tag.putInt("MasterZ", this.masterPos.getZ());
|
||||
tag.putInt("PairTime", this.pairTime);
|
||||
this.portalGun$saveFaceRecords(tag);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -134,6 +170,7 @@ public class PortalGunPortalBaseBlockEntity extends BlockEntity {
|
||||
this.side = tag.getInt("Side") == PortalGunPortalEntity.PortalSide.ORANGE.ordinal() ? PortalGunPortalEntity.PortalSide.ORANGE : PortalGunPortalEntity.PortalSide.BLUE;
|
||||
this.masterPos = new BlockPos(tag.getInt("MasterX"), tag.getInt("MasterY"), tag.getInt("MasterZ"));
|
||||
this.pairTime = tag.getInt("PairTime");
|
||||
this.portalGun$loadFaceRecords(tag);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
package com.craisinlord.antarchy.content.block.entity;
|
||||
|
||||
import com.craisinlord.antarchy.content.portalgun.PortalGunPlacement;
|
||||
import com.craisinlord.antarchy.content.portalgun.PortalGunSavedData;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.ListTag;
|
||||
import net.minecraft.nbt.Tag;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
|
||||
public interface PortalGunPortalCellAccess {
|
||||
Map<Direction, PortalGunPortalFaceRecord> portalGun$getFaceRecords();
|
||||
|
||||
default PortalGunPortalFaceRecord portalGun$getFaceRecord(Direction face) {
|
||||
return this.portalGun$getFaceRecords().get(face);
|
||||
}
|
||||
|
||||
default PortalGunPortalFaceRecord portalGun$getFaceRecord(Direction face, UUID portalId) {
|
||||
PortalGunPortalFaceRecord record = this.portalGun$getFaceRecord(face);
|
||||
return record != null && record.portalId().equals(portalId) ? record : null;
|
||||
}
|
||||
|
||||
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())
|
||||
|| existing.side() != record.side())) {
|
||||
return false;
|
||||
}
|
||||
this.portalGun$getFaceRecords().put(record.face(), record);
|
||||
if (this instanceof net.minecraft.world.level.block.entity.BlockEntity blockEntity) {
|
||||
blockEntity.setChanged();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
default PortalGunPortalFaceRecord portalGun$removeFaceRecord(Direction face, UUID portalId) {
|
||||
PortalGunPortalFaceRecord existing = this.portalGun$getFaceRecord(face, portalId);
|
||||
if (existing == null) {
|
||||
return null;
|
||||
}
|
||||
PortalGunPortalFaceRecord removed = this.portalGun$getFaceRecords().remove(face);
|
||||
if (this instanceof net.minecraft.world.level.block.entity.BlockEntity blockEntity) {
|
||||
blockEntity.setChanged();
|
||||
}
|
||||
return removed;
|
||||
}
|
||||
|
||||
default void portalGun$updateFacePair(UUID portalId, UUID linkedPortalId, int pairTime) {
|
||||
for (PortalGunPortalFaceRecord record : java.util.List.copyOf(this.portalGun$getFaceRecords().values())) {
|
||||
if (record.portalId().equals(portalId)) {
|
||||
this.portalGun$putFaceRecord(record.withPair(linkedPortalId, pairTime));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
default void portalGun$restoreMasterFaces(ServerLevel level, BlockPos pos) {
|
||||
for (PortalGunPortalFaceRecord record : java.util.List.copyOf(this.portalGun$getFaceRecords().values())) {
|
||||
if (record.ownerId() == null || record.portalId() == null) {
|
||||
continue;
|
||||
}
|
||||
if (!PortalGunSavedData.isRegistered(level, record.ownerId(), record.gunId(), record.channelName(), record.side(), record.portalId())) {
|
||||
this.portalGun$removeFaceRecord(record.face(), record.portalId());
|
||||
continue;
|
||||
}
|
||||
if (!record.master() || !record.masterPos().equals(pos)) {
|
||||
continue;
|
||||
}
|
||||
boolean complete = !record.portalSpots().isEmpty() && record.portalSpots().getFirst().equals(record.masterPos());
|
||||
for (int i = 0; complete && i < record.portalSpots().size(); i++) {
|
||||
BlockPos spot = record.portalSpots().get(i);
|
||||
BlockPos support = spot.relative(record.face().getOpposite());
|
||||
if (!level.hasChunkAt(spot) || !level.hasChunkAt(support)
|
||||
|| !level.getBlockState(support).isFaceSturdy(level, support, record.face())
|
||||
|| !(level.getBlockEntity(spot) instanceof PortalGunPortalCellAccess cell)) {
|
||||
complete = false;
|
||||
continue;
|
||||
}
|
||||
PortalGunPortalFaceRecord footprintRecord = cell.portalGun$getFaceRecord(record.face(), record.portalId());
|
||||
complete = footprintRecord != null && footprintRecord.master() == (i == 0);
|
||||
}
|
||||
complete = complete && PortalGunPlacement.isRectangularFootprint(
|
||||
record.portalSpots().toArray(BlockPos[]::new),
|
||||
record.face(),
|
||||
record.upAxis(),
|
||||
record.portalWidth(),
|
||||
record.portalHeight()
|
||||
);
|
||||
if (complete) {
|
||||
PortalGunPortalMasterBlockEntity.restorePortalEntity(level, record);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
default void portalGun$saveFaceRecords(CompoundTag tag) {
|
||||
ListTag faces = new ListTag();
|
||||
for (PortalGunPortalFaceRecord record : this.portalGun$getFaceRecords().values()) {
|
||||
faces.add(record.save());
|
||||
}
|
||||
tag.put("PortalFaces", faces);
|
||||
}
|
||||
|
||||
default void portalGun$loadFaceRecords(CompoundTag tag) {
|
||||
Map<Direction, PortalGunPortalFaceRecord> records = this.portalGun$getFaceRecords();
|
||||
records.clear();
|
||||
ListTag faces = tag.getList("PortalFaces", Tag.TAG_COMPOUND);
|
||||
for (int i = 0; i < faces.size(); i++) {
|
||||
PortalGunPortalFaceRecord record = PortalGunPortalFaceRecord.load(faces.getCompound(i));
|
||||
if (record != null) {
|
||||
records.put(record.face(), record);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+151
@@ -0,0 +1,151 @@
|
||||
package com.craisinlord.antarchy.content.block.entity;
|
||||
|
||||
import com.craisinlord.antarchy.content.portalgun.PortalGunPlacement;
|
||||
import com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.ListTag;
|
||||
import net.minecraft.nbt.Tag;
|
||||
|
||||
public record PortalGunPortalFaceRecord(
|
||||
UUID ownerId,
|
||||
String ownerIdentity,
|
||||
UUID gunId,
|
||||
String channelName,
|
||||
UUID portalId,
|
||||
UUID linkedPortalId,
|
||||
PortalGunPortalEntity.PortalSide side,
|
||||
Direction face,
|
||||
Direction upAxis,
|
||||
BlockPos masterPos,
|
||||
BlockPos basePos,
|
||||
List<BlockPos> portalSpots,
|
||||
Set<BlockPos> compensatedSpots,
|
||||
int pairTime,
|
||||
int portalWidth,
|
||||
int portalHeight,
|
||||
boolean master
|
||||
) {
|
||||
public PortalGunPortalFaceRecord {
|
||||
masterPos = masterPos.immutable();
|
||||
basePos = basePos.immutable();
|
||||
portalSpots = portalSpots.stream().map(BlockPos::immutable).toList();
|
||||
compensatedSpots = Set.copyOf(compensatedSpots);
|
||||
}
|
||||
|
||||
public PortalGunPlacement placement() {
|
||||
return PortalGunPlacement.fromStored(this.face, this.upAxis, this.masterPos, this.basePos, this.portalSpots.toArray(BlockPos[]::new), this.compensatedSpots, this.portalWidth, this.portalHeight);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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.putUUID("PortalId", this.portalId);
|
||||
if (this.linkedPortalId != null) {
|
||||
tag.putUUID("LinkedPortalId", this.linkedPortalId);
|
||||
}
|
||||
tag.putInt("Side", this.side.ordinal());
|
||||
tag.putInt("Face", this.face.get3DDataValue());
|
||||
tag.putInt("UpAxis", this.upAxis.get3DDataValue());
|
||||
putPos(tag, "Master", this.masterPos);
|
||||
putPos(tag, "Base", this.basePos);
|
||||
tag.putInt("PairTime", this.pairTime);
|
||||
tag.putInt("PortalWidth", this.portalWidth);
|
||||
tag.putInt("PortalHeight", this.portalHeight);
|
||||
tag.putBoolean("MasterRole", this.master);
|
||||
ListTag spots = new ListTag();
|
||||
for (BlockPos pos : this.portalSpots) {
|
||||
spots.add(savePos(pos));
|
||||
}
|
||||
tag.put("PortalSpots", spots);
|
||||
ListTag compensated = new ListTag();
|
||||
for (BlockPos pos : this.compensatedSpots) {
|
||||
compensated.add(savePos(pos));
|
||||
}
|
||||
tag.put("CompensatedSpots", compensated);
|
||||
return tag;
|
||||
}
|
||||
|
||||
public static PortalGunPortalFaceRecord load(CompoundTag tag) {
|
||||
if (!tag.hasUUID("OwnerId") || !tag.hasUUID("PortalId")) {
|
||||
return null;
|
||||
}
|
||||
ListTag spotsTag = tag.getList("PortalSpots", Tag.TAG_COMPOUND);
|
||||
List<BlockPos> spots = new java.util.ArrayList<>(spotsTag.size());
|
||||
for (int i = 0; i < spotsTag.size(); i++) {
|
||||
spots.add(loadPos(spotsTag.getCompound(i)));
|
||||
}
|
||||
ListTag compensatedTag = tag.getList("CompensatedSpots", Tag.TAG_COMPOUND);
|
||||
Set<BlockPos> compensated = new HashSet<>();
|
||||
for (int i = 0; i < compensatedTag.size(); i++) {
|
||||
compensated.add(loadPos(compensatedTag.getCompound(i)));
|
||||
}
|
||||
PortalGunPortalEntity.PortalSide side = tag.getInt("Side") == PortalGunPortalEntity.PortalSide.ORANGE.ordinal()
|
||||
? PortalGunPortalEntity.PortalSide.ORANGE
|
||||
: PortalGunPortalEntity.PortalSide.BLUE;
|
||||
int width = net.minecraft.util.Mth.clamp(tag.getInt("PortalWidth"), 1, 16);
|
||||
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"),
|
||||
tag.getUUID("PortalId"),
|
||||
tag.hasUUID("LinkedPortalId") ? tag.getUUID("LinkedPortalId") : null,
|
||||
side,
|
||||
Direction.from3DDataValue(tag.getInt("Face")),
|
||||
Direction.from3DDataValue(tag.getInt("UpAxis")),
|
||||
getPos(tag, "Master"),
|
||||
getPos(tag, "Base"),
|
||||
spots,
|
||||
compensated,
|
||||
tag.getInt("PairTime"),
|
||||
width,
|
||||
height,
|
||||
tag.getBoolean("MasterRole")
|
||||
);
|
||||
}
|
||||
|
||||
private static void putPos(CompoundTag tag, String key, BlockPos pos) {
|
||||
CompoundTag value = new CompoundTag();
|
||||
value.putInt("X", pos.getX());
|
||||
value.putInt("Y", pos.getY());
|
||||
value.putInt("Z", pos.getZ());
|
||||
tag.put(key, value);
|
||||
}
|
||||
|
||||
private static BlockPos getPos(CompoundTag tag, String key) {
|
||||
CompoundTag value = tag.getCompound(key);
|
||||
return new BlockPos(value.getInt("X"), value.getInt("Y"), value.getInt("Z"));
|
||||
}
|
||||
|
||||
private static CompoundTag savePos(BlockPos pos) {
|
||||
CompoundTag tag = new CompoundTag();
|
||||
tag.putInt("X", pos.getX());
|
||||
tag.putInt("Y", pos.getY());
|
||||
tag.putInt("Z", pos.getZ());
|
||||
return tag;
|
||||
}
|
||||
|
||||
private static BlockPos loadPos(CompoundTag tag) {
|
||||
return new BlockPos(tag.getInt("X"), tag.getInt("Y"), tag.getInt("Z"));
|
||||
}
|
||||
}
|
||||
+175
-16
@@ -5,8 +5,10 @@ import com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity;
|
||||
import com.craisinlord.antarchy.content.portalgun.PortalGunPlacement;
|
||||
import com.craisinlord.antarchy.content.portalgun.PortalGunSavedData;
|
||||
import java.util.ArrayList;
|
||||
import java.util.EnumMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Supplier;
|
||||
@@ -30,14 +32,20 @@ import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
|
||||
public class PortalGunPortalMasterBlockEntity extends BlockEntity {
|
||||
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 UUID portalId;
|
||||
private PortalGunPortalEntity.PortalSide side = PortalGunPortalEntity.PortalSide.BLUE;
|
||||
private Direction facing = Direction.NORTH;
|
||||
private Direction upAxis = Direction.UP;
|
||||
private BlockPos basePos = BlockPos.ZERO;
|
||||
private BlockPos[] portalSpots = new BlockPos[] {BlockPos.ZERO, BlockPos.ZERO};
|
||||
private int portalWidth = 1;
|
||||
private int portalHeight = 2;
|
||||
private Set<BlockPos> compensatedSpots = Set.of();
|
||||
private UUID linkedPortalId;
|
||||
private int pairTime;
|
||||
@@ -48,6 +56,9 @@ public class PortalGunPortalMasterBlockEntity extends BlockEntity {
|
||||
|
||||
public void configure(
|
||||
UUID ownerId,
|
||||
UUID gunId,
|
||||
String ownerIdentity,
|
||||
String channelName,
|
||||
UUID portalId,
|
||||
PortalGunPortalEntity.PortalSide side,
|
||||
Direction facing,
|
||||
@@ -55,20 +66,33 @@ public class PortalGunPortalMasterBlockEntity extends BlockEntity {
|
||||
BlockPos basePos,
|
||||
BlockPos[] portalSpots,
|
||||
Set<BlockPos> compensatedSpots,
|
||||
int pairTime
|
||||
int pairTime,
|
||||
int portalWidth,
|
||||
int portalHeight
|
||||
) {
|
||||
this.ownerId = ownerId;
|
||||
this.ownerIdentity = ownerIdentity;
|
||||
this.gunId = gunId;
|
||||
this.channelName = channelName;
|
||||
this.portalId = portalId;
|
||||
this.side = side;
|
||||
this.facing = facing;
|
||||
this.upAxis = upAxis;
|
||||
this.basePos = basePos.immutable();
|
||||
this.portalSpots = new BlockPos[] {portalSpots[0].immutable(), portalSpots[1].immutable()};
|
||||
this.portalSpots = java.util.Arrays.stream(portalSpots).map(BlockPos::immutable).toArray(BlockPos[]::new);
|
||||
this.portalWidth = portalWidth;
|
||||
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.setChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<Direction, PortalGunPortalFaceRecord> portalGun$getFaceRecords() {
|
||||
return this.faceRecords;
|
||||
}
|
||||
|
||||
public UUID getPortalId() {
|
||||
return this.portalId;
|
||||
}
|
||||
@@ -77,6 +101,36 @@ public class PortalGunPortalMasterBlockEntity extends BlockEntity {
|
||||
return this.ownerId;
|
||||
}
|
||||
|
||||
public UUID getGunId() {
|
||||
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 PortalGunPortalEntity.PortalSide getSide() {
|
||||
return this.side;
|
||||
}
|
||||
@@ -94,7 +148,16 @@ public class PortalGunPortalMasterBlockEntity extends BlockEntity {
|
||||
}
|
||||
|
||||
public BlockPos[] getPortalSpots() {
|
||||
return new BlockPos[] {this.portalSpots[0], this.portalSpots[1]};
|
||||
return this.portalSpots.clone();
|
||||
}
|
||||
|
||||
public boolean containsPortalSpot(BlockPos pos) {
|
||||
for (BlockPos portalSpot : this.portalSpots) {
|
||||
if (portalSpot.equals(pos)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public Set<BlockPos> getCompensatedSpots() {
|
||||
@@ -112,6 +175,10 @@ public class PortalGunPortalMasterBlockEntity extends BlockEntity {
|
||||
public void updatePair(UUID linkedPortalId, int pairTime) {
|
||||
this.linkedPortalId = linkedPortalId;
|
||||
this.pairTime = pairTime;
|
||||
PortalGunPortalFaceRecord record = this.portalGun$getFaceRecord(this.facing, this.portalId);
|
||||
if (record != null) {
|
||||
this.portalGun$putFaceRecord(record.withPair(linkedPortalId, pairTime));
|
||||
}
|
||||
this.setChanged();
|
||||
}
|
||||
|
||||
@@ -120,18 +187,33 @@ public class PortalGunPortalMasterBlockEntity extends BlockEntity {
|
||||
}
|
||||
|
||||
public void onBroken() {
|
||||
if (!(this.level instanceof ServerLevel serverLevel) || this.portalId == null) {
|
||||
if (!(this.level instanceof ServerLevel serverLevel)) {
|
||||
return;
|
||||
}
|
||||
if (serverLevel.getEntity(this.portalId) instanceof PortalGunPortalEntity portal && !portal.isRemoved()) {
|
||||
Set<UUID> portalIds = new HashSet<>();
|
||||
if (this.portalId != null) {
|
||||
portalIds.add(this.portalId);
|
||||
}
|
||||
for (PortalGunPortalFaceRecord record : this.faceRecords.values()) {
|
||||
portalIds.add(record.portalId());
|
||||
}
|
||||
for (UUID id : portalIds) {
|
||||
if (serverLevel.getEntity(id) instanceof PortalGunPortalEntity portal && !portal.isRemoved()) {
|
||||
portal.discard();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void serverTick(ServerLevel level, BlockPos pos, BlockState state, PortalGunPortalMasterBlockEntity blockEntity) {
|
||||
if (((level.getGameTime() + pos.asLong()) & 15L) != 0L) {
|
||||
return;
|
||||
}
|
||||
blockEntity.portalGun$restoreMasterFaces(level, pos);
|
||||
if (blockEntity.portalId != null
|
||||
&& !(level.getEntity(blockEntity.portalId) instanceof PortalGunPortalEntity)
|
||||
&& blockEntity.faceRecords.values().stream().anyMatch(record -> !record.portalId().equals(blockEntity.portalId))) {
|
||||
return;
|
||||
}
|
||||
if (!blockEntity.hasValidFootprint(level, pos)) {
|
||||
level.removeBlock(pos, false);
|
||||
return;
|
||||
@@ -146,6 +228,12 @@ public class PortalGunPortalMasterBlockEntity extends BlockEntity {
|
||||
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)
|
||||
@@ -161,16 +249,24 @@ public class PortalGunPortalMasterBlockEntity extends BlockEntity {
|
||||
if (this.ownerId == null || this.portalId == null || !pos.equals(this.worldPosition)) {
|
||||
return false;
|
||||
}
|
||||
if (!(level.getBlockEntity(this.basePos) instanceof PortalGunPortalBaseBlockEntity base)
|
||||
|| !base.matches(this.ownerId, this.portalId, this.side)
|
||||
|| !pos.equals(base.getMasterPos())) {
|
||||
if (!PortalGunPlacement.isRectangularFootprint(this.portalSpots, this.facing, this.upAxis, this.portalWidth, this.portalHeight)) {
|
||||
return false;
|
||||
}
|
||||
List<BlockPos> supportPositions = new ArrayList<>(2);
|
||||
for (BlockPos portalSpot : this.portalSpots) {
|
||||
List<BlockPos> supportPositions = new ArrayList<>(this.portalSpots.length);
|
||||
for (int i = 0; i < this.portalSpots.length; i++) {
|
||||
BlockPos portalSpot = this.portalSpots[i];
|
||||
if (portalSpot == null || BlockPos.ZERO.equals(portalSpot)) {
|
||||
return false;
|
||||
}
|
||||
if (i == 0) {
|
||||
if (!portalSpot.equals(pos)) {
|
||||
return false;
|
||||
}
|
||||
} else if (!(level.getBlockEntity(portalSpot) instanceof PortalGunPortalBaseBlockEntity base)
|
||||
|| !base.matches(this.ownerId, this.portalId, this.side)
|
||||
|| !pos.equals(base.getMasterPos())) {
|
||||
return false;
|
||||
}
|
||||
supportPositions.add(portalSpot.relative(this.facing.getOpposite()));
|
||||
}
|
||||
for (BlockPos supportPos : supportPositions) {
|
||||
@@ -186,23 +282,60 @@ public class PortalGunPortalMasterBlockEntity extends BlockEntity {
|
||||
if (this.portalId == null || this.ownerId == null) {
|
||||
return null;
|
||||
}
|
||||
if (!PortalGunSavedData.isRegistered(level, this.ownerId, this.gunId, this.channelName, this.side, this.portalId)) {
|
||||
return null;
|
||||
}
|
||||
if (level.getEntity(this.portalId) instanceof PortalGunPortalEntity portal && !portal.isRemoved()) {
|
||||
return portal;
|
||||
}
|
||||
PortalGunPortalFaceRecord record = this.portalGun$getFaceRecord(this.facing, this.portalId);
|
||||
if (record != null) {
|
||||
return restorePortalEntity(level, record);
|
||||
}
|
||||
EntityType<?> rawType = BuiltInRegistries.ENTITY_TYPE.get(ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "portal_gun_portal"));
|
||||
if (!(rawType instanceof EntityType<?>)) {
|
||||
return null;
|
||||
}
|
||||
PortalGunPlacement placement = PortalGunPlacement.fromStored(this.facing, this.upAxis, this.worldPosition, this.basePos, this.compensatedSpots);
|
||||
PortalGunPlacement placement = PortalGunPlacement.fromStored(this.facing, this.upAxis, this.worldPosition, this.basePos, this.portalSpots, this.compensatedSpots, this.portalWidth, this.portalHeight);
|
||||
@SuppressWarnings("unchecked")
|
||||
PortalGunPortalEntity restored = new PortalGunPortalEntity((EntityType<? extends PortalGunPortalEntity>) rawType, level);
|
||||
restored.setUUID(this.portalId);
|
||||
restored.configure(this.ownerId, this.side, placement);
|
||||
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.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.getServer(), this.ownerId, this.side, restored.getUUID());
|
||||
PortalGunSavedData.setPortal(level, this.ownerId, this.gunId, this.channelName, 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())) {
|
||||
return null;
|
||||
}
|
||||
if (level.getEntity(record.portalId()) instanceof PortalGunPortalEntity portal && !portal.isRemoved()) {
|
||||
return portal;
|
||||
}
|
||||
EntityType<?> rawType = BuiltInRegistries.ENTITY_TYPE.get(ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "portal_gun_portal"));
|
||||
if (rawType == null) {
|
||||
return null;
|
||||
}
|
||||
PortalGunPlacement placement = record.placement();
|
||||
@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.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);
|
||||
if (linked != null && linked != restored && !linked.isRemoved()) {
|
||||
restored.linkTo(linked);
|
||||
linked.linkTo(restored);
|
||||
}
|
||||
return restored;
|
||||
}
|
||||
|
||||
@@ -211,8 +344,10 @@ public class PortalGunPortalMasterBlockEntity extends BlockEntity {
|
||||
return;
|
||||
}
|
||||
PortalGunPortalEntity counterpart = PortalGunSavedData.findLoadedPortal(
|
||||
level.getServer(),
|
||||
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()) {
|
||||
@@ -230,6 +365,15 @@ public class PortalGunPortalMasterBlockEntity extends BlockEntity {
|
||||
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.portalId != null) {
|
||||
tag.putUUID("PortalId", this.portalId);
|
||||
}
|
||||
@@ -243,6 +387,8 @@ public class PortalGunPortalMasterBlockEntity extends BlockEntity {
|
||||
tag.putInt("BaseY", this.basePos.getY());
|
||||
tag.putInt("BaseZ", this.basePos.getZ());
|
||||
tag.putInt("PairTime", this.pairTime);
|
||||
tag.putInt("PortalWidth", this.portalWidth);
|
||||
tag.putInt("PortalHeight", this.portalHeight);
|
||||
ListTag spots = new ListTag();
|
||||
for (BlockPos portalSpot : this.portalSpots) {
|
||||
CompoundTag spot = new CompoundTag();
|
||||
@@ -261,12 +407,16 @@ public class PortalGunPortalMasterBlockEntity extends BlockEntity {
|
||||
compensated.add(spot);
|
||||
}
|
||||
tag.put("CompensatedSpots", compensated);
|
||||
this.portalGun$saveFaceRecords(tag);
|
||||
}
|
||||
|
||||
@Override
|
||||
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.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;
|
||||
@@ -275,10 +425,15 @@ public class PortalGunPortalMasterBlockEntity extends BlockEntity {
|
||||
this.basePos = new BlockPos(tag.getInt("BaseX"), tag.getInt("BaseY"), tag.getInt("BaseZ"));
|
||||
this.pairTime = tag.getInt("PairTime");
|
||||
ListTag spots = tag.getList("PortalSpots", Tag.TAG_COMPOUND);
|
||||
for (int i = 0; i < Math.min(2, spots.size()); i++) {
|
||||
if (!spots.isEmpty()) {
|
||||
this.portalSpots = new BlockPos[spots.size()];
|
||||
}
|
||||
for (int i = 0; i < spots.size(); i++) {
|
||||
CompoundTag spot = spots.getCompound(i);
|
||||
this.portalSpots[i] = new BlockPos(spot.getInt("X"), spot.getInt("Y"), spot.getInt("Z"));
|
||||
}
|
||||
this.portalWidth = net.minecraft.util.Mth.clamp(tag.getInt("PortalWidth"), 1, 16);
|
||||
this.portalHeight = net.minecraft.util.Mth.clamp(tag.getInt("PortalHeight"), 2, 16);
|
||||
ListTag compensated = tag.getList("CompensatedSpots", Tag.TAG_COMPOUND);
|
||||
Set<BlockPos> compensatedSpots = new HashSet<>();
|
||||
for (int i = 0; i < compensated.size(); i++) {
|
||||
@@ -286,6 +441,10 @@ public class PortalGunPortalMasterBlockEntity extends BlockEntity {
|
||||
compensatedSpots.add(new BlockPos(spot.getInt("X"), spot.getInt("Y"), spot.getInt("Z")));
|
||||
}
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+5
@@ -20,6 +20,7 @@ import net.minecraft.world.entity.player.Inventory;
|
||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||
import net.minecraft.world.inventory.HopperMenu;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.block.ChestBlock;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||
import net.minecraft.world.level.block.entity.RandomizableContainerBlockEntity;
|
||||
@@ -119,6 +120,10 @@ public class UpperBlockEntity extends RandomizableContainerBlockEntity {
|
||||
}
|
||||
|
||||
private static Container getContainerAt(ServerLevel level, BlockPos pos) {
|
||||
BlockState state = level.getBlockState(pos);
|
||||
if (state.getBlock() instanceof ChestBlock chestBlock) {
|
||||
return ChestBlock.getContainer(chestBlock, state, level, pos, false);
|
||||
}
|
||||
BlockEntity blockEntity = level.getBlockEntity(pos);
|
||||
return blockEntity instanceof Container container ? container : null;
|
||||
}
|
||||
|
||||
+116
@@ -0,0 +1,116 @@
|
||||
package com.craisinlord.antarchy.content.client;
|
||||
|
||||
import com.craisinlord.antarchy.Antarchy;
|
||||
import com.craisinlord.antarchy.content.item.PortalGunItem;
|
||||
import com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity;
|
||||
import com.craisinlord.antarchy.content.client.PortalGunIndicatorClientState;
|
||||
import com.craisinlord.antarchy.content.network.PortalGunIndicatorPayload;
|
||||
import com.craisinlord.antarchy.config.AntarchySettings;
|
||||
import java.util.HashSet;
|
||||
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.entity.Entity;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.phys.AABB;
|
||||
|
||||
public final class PortalGunCrosshairRenderer {
|
||||
private static final ResourceLocation CROSSHAIR = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "textures/gui/portal_gun_crosshair.png");
|
||||
private static final int WIDTH = 34;
|
||||
private static final int HEIGHT = 33;
|
||||
private static ClientLevel cachedLevel;
|
||||
private static long cachedGameTime = Long.MIN_VALUE;
|
||||
private static UUID cachedOwnerId;
|
||||
private static Set<PortalKey> cachedPortals = Set.of();
|
||||
|
||||
private PortalGunCrosshairRenderer() {
|
||||
}
|
||||
|
||||
public static void render(GuiGraphics guiGraphics) {
|
||||
Minecraft minecraft = Minecraft.getInstance();
|
||||
if (PortalGunGrabClientState.isActive()) {
|
||||
return;
|
||||
}
|
||||
if (minecraft.player == 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;
|
||||
}
|
||||
}
|
||||
UUID gunId = PortalGunItem.getGunId(held);
|
||||
UUID ownerId = PortalGunItem.getPortalOwnerId(held, minecraft.player.getUUID());
|
||||
String channelName = PortalGunItem.getChannelName(held);
|
||||
PortalGunIndicatorClientState.requestIfNeeded(minecraft, gunId);
|
||||
Set<PortalKey> portals = portalsFor(minecraft, ownerId, channelName);
|
||||
PortalGunIndicatorPayload serverState = PortalGunIndicatorClientState.get(gunId);
|
||||
boolean blueAvailable = serverState == null
|
||||
? portals.contains(new PortalKey(channelName, PortalGunPortalEntity.PortalSide.BLUE))
|
||||
: serverState.blueAvailable();
|
||||
boolean orangeAvailable = serverState == null
|
||||
? portals.contains(new PortalKey(channelName, PortalGunPortalEntity.PortalSide.ORANGE))
|
||||
: serverState.orangeAvailable();
|
||||
|
||||
int left = (minecraft.getWindow().getGuiScaledWidth() - WIDTH) / 2;
|
||||
int top = (minecraft.getWindow().getGuiScaledHeight() - HEIGHT) / 2;
|
||||
guiGraphics.blit(CROSSHAIR, left + 6, top, 0.0F, 0.0F, 17, HEIGHT, WIDTH, HEIGHT);
|
||||
guiGraphics.blit(CROSSHAIR, left + 11, top, 17.0F, 0.0F, 17, HEIGHT, WIDTH, HEIGHT);
|
||||
if (!blueAvailable) {
|
||||
guiGraphics.fill(left + 6, top, left + 23, top + HEIGHT, 0x99000000);
|
||||
}
|
||||
if (!orangeAvailable) {
|
||||
guiGraphics.fill(left + 11, top, left + 28, top + HEIGHT, 0x99000000);
|
||||
}
|
||||
int indicatorSize = AntarchySettings.portalGunIndicatorSize();
|
||||
if (indicatorSize > 0) {
|
||||
int dot = Math.max(1, Math.round(HEIGHT * indicatorSize / 100.0F));
|
||||
int gap = Math.max(1, dot / 3);
|
||||
int indicatorLeft = left + (WIDTH - dot * 2 - gap) / 2;
|
||||
int indicatorTop = top + HEIGHT + 2;
|
||||
guiGraphics.fill(indicatorLeft, indicatorTop, indicatorLeft + dot, indicatorTop + dot,
|
||||
blueAvailable ? 0xFF21A8FF : 0xFF33414A);
|
||||
int orangeLeft = indicatorLeft + dot + gap;
|
||||
guiGraphics.fill(orangeLeft, indicatorTop, orangeLeft + dot, indicatorTop + dot,
|
||||
orangeAvailable ? 0xFFFF8A24 : 0xFF4A4035);
|
||||
}
|
||||
}
|
||||
|
||||
private static Set<PortalKey> portalsFor(Minecraft minecraft, UUID ownerId, String channelName) {
|
||||
ClientLevel level = minecraft.level;
|
||||
long gameTime = level.getGameTime();
|
||||
if (cachedLevel != level || cachedGameTime != gameTime || !ownerId.equals(cachedOwnerId)) {
|
||||
Set<PortalKey> portals = new HashSet<>();
|
||||
if (minecraft.player != null) {
|
||||
AABB bounds = minecraft.player.getBoundingBox().inflate(128.0D);
|
||||
for (PortalGunPortalEntity portal : level.getEntitiesOfClass(PortalGunPortalEntity.class, bounds, Entity::isAlive)) {
|
||||
UUID portalOwnerId = portal.getOwnerId();
|
||||
String portalChannelName = portal.getChannelName();
|
||||
if (ownerId.equals(portalOwnerId) && portalChannelName != null && !portalChannelName.isEmpty()) {
|
||||
portals.add(new PortalKey(portalChannelName, portal.getPortalSide()));
|
||||
if (portal.getLinkedPortalId() != null) {
|
||||
portals.add(new PortalKey(portalChannelName, PortalGunPortalEntity.PortalSide.BLUE));
|
||||
portals.add(new PortalKey(portalChannelName, PortalGunPortalEntity.PortalSide.ORANGE));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
cachedLevel = level;
|
||||
cachedGameTime = gameTime;
|
||||
cachedOwnerId = ownerId;
|
||||
cachedPortals = Set.copyOf(portals);
|
||||
}
|
||||
if (channelName == null || channelName.isEmpty()) {
|
||||
return Set.of();
|
||||
}
|
||||
return cachedPortals;
|
||||
}
|
||||
|
||||
private record PortalKey(String channelName, PortalGunPortalEntity.PortalSide side) {
|
||||
}
|
||||
}
|
||||
+29
-10
@@ -27,30 +27,43 @@ public final class PortalGunEntityTransformationStack {
|
||||
}
|
||||
|
||||
public Vec3 moveEntity(PortalGunPortalEntity sourcePortal, PortalGunPortalEntity destinationPortal, float partialTick) {
|
||||
Vec3 interpolated = new Vec3(
|
||||
double eyeHeight = this.entity.getEyeHeight();
|
||||
Vec3 interpolatedEye = new Vec3(
|
||||
net.minecraft.util.Mth.lerp(partialTick, this.entity.xo, this.entity.getX()),
|
||||
net.minecraft.util.Mth.lerp(partialTick, this.entity.yo, this.entity.getY()),
|
||||
net.minecraft.util.Mth.lerp(partialTick, this.entity.yo, this.entity.getY()) + eyeHeight,
|
||||
net.minecraft.util.Mth.lerp(partialTick, this.entity.zo, this.entity.getZ())
|
||||
);
|
||||
Vec3 previous = new Vec3(this.entity.xo, this.entity.yo, this.entity.zo);
|
||||
Vec3 transformed = destinationPortal.position().add(PortalGunTransformUtil.transformPosition(sourcePortal, destinationPortal, interpolated.subtract(sourcePortal.position())));
|
||||
Vec3 transformedPrevious = destinationPortal.position().add(PortalGunTransformUtil.transformPosition(sourcePortal, destinationPortal, previous.subtract(sourcePortal.position())));
|
||||
Vec3 previousEye = new Vec3(this.entity.xo, this.entity.yo + eyeHeight, this.entity.zo);
|
||||
Vec3 transformedEye = destinationPortal.position().add(PortalGunTransformUtil.transformPosition(sourcePortal, destinationPortal, interpolatedEye.subtract(sourcePortal.position())));
|
||||
Vec3 transformedPreviousEye = destinationPortal.position().add(PortalGunTransformUtil.transformPosition(sourcePortal, destinationPortal, previousEye.subtract(sourcePortal.position())));
|
||||
Vec3 transformed = transformedEye.subtract(0.0D, eyeHeight, 0.0D);
|
||||
Vec3 transformedPrevious = transformedPreviousEye.subtract(0.0D, eyeHeight, 0.0D);
|
||||
Vec3 look = PortalGunTransformUtil.transformVector(sourcePortal, destinationPortal, this.entity.getLookAngle()).normalize();
|
||||
Vec3 previousLook = PortalGunTransformUtil.transformVector(
|
||||
sourcePortal,
|
||||
destinationPortal,
|
||||
Vec3.directionFromRotation(this.entity.xRotO, this.entity.yRotO)
|
||||
).normalize();
|
||||
Vec3 velocity = PortalGunTransformUtil.transformVector(sourcePortal, destinationPortal, this.entity.getDeltaMovement());
|
||||
float yaw = PortalGunTransformUtil.yawFromLook(look);
|
||||
float pitch = PortalGunTransformUtil.pitchFromLook(look);
|
||||
float previousYaw = PortalGunTransformUtil.yawFromLook(previousLook);
|
||||
float previousPitch = PortalGunTransformUtil.pitchFromLook(previousLook);
|
||||
float yawDelta = net.minecraft.util.Mth.wrapDegrees(yaw - this.entity.getYRot());
|
||||
this.entity.setPos(transformed.x, transformed.y, transformed.z);
|
||||
this.entity.xo = transformedPrevious.x;
|
||||
this.entity.yo = transformedPrevious.y;
|
||||
this.entity.zo = transformedPrevious.z;
|
||||
this.entity.setYRot(yaw);
|
||||
this.entity.setXRot(pitch);
|
||||
this.entity.yRotO = yaw;
|
||||
this.entity.xRotO = pitch;
|
||||
this.entity.yRotO = previousYaw;
|
||||
this.entity.xRotO = previousPitch;
|
||||
this.entity.setDeltaMovement(velocity);
|
||||
if (this.entity instanceof LivingEntity living) {
|
||||
living.setYHeadRot(yaw);
|
||||
living.setYBodyRot(yaw);
|
||||
living.setYHeadRot(living.getYHeadRot() + yawDelta);
|
||||
living.setYBodyRot(living.yBodyRot + yawDelta);
|
||||
living.yHeadRotO += yawDelta;
|
||||
living.yBodyRotO += yawDelta;
|
||||
}
|
||||
return transformed;
|
||||
}
|
||||
@@ -73,13 +86,17 @@ public final class PortalGunEntityTransformationStack {
|
||||
float yRotO,
|
||||
float xRotO,
|
||||
float yHeadRot,
|
||||
float yHeadRotO,
|
||||
float yBodyRot,
|
||||
float yBodyRotO,
|
||||
Vec3 deltaMovement
|
||||
) {
|
||||
private static EntityTransformation capture(Entity entity) {
|
||||
float yHeadRot = entity instanceof LivingEntity living ? living.getYHeadRot() : entity.getYRot();
|
||||
float yHeadRotO = entity instanceof LivingEntity living ? living.yHeadRotO : entity.yRotO;
|
||||
float yBodyRot = entity instanceof LivingEntity living ? living.yBodyRot : entity.getYRot();
|
||||
return new EntityTransformation(entity.getX(), entity.getY(), entity.getZ(), entity.xo, entity.yo, entity.zo, entity.getYRot(), entity.getXRot(), entity.yRotO, entity.xRotO, yHeadRot, yBodyRot, entity.getDeltaMovement());
|
||||
float yBodyRotO = entity instanceof LivingEntity living ? living.yBodyRotO : entity.yRotO;
|
||||
return new EntityTransformation(entity.getX(), entity.getY(), entity.getZ(), entity.xo, entity.yo, entity.zo, entity.getYRot(), entity.getXRot(), entity.yRotO, entity.xRotO, yHeadRot, yHeadRotO, yBodyRot, yBodyRotO, entity.getDeltaMovement());
|
||||
}
|
||||
|
||||
private void restore(Entity entity) {
|
||||
@@ -94,7 +111,9 @@ public final class PortalGunEntityTransformationStack {
|
||||
entity.setDeltaMovement(this.deltaMovement);
|
||||
if (entity instanceof LivingEntity living) {
|
||||
living.setYHeadRot(this.yHeadRot);
|
||||
living.yHeadRotO = this.yHeadRotO;
|
||||
living.setYBodyRot(this.yBodyRot);
|
||||
living.yBodyRotO = this.yBodyRotO;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
package com.craisinlord.antarchy.content.client;
|
||||
|
||||
import com.craisinlord.antarchy.content.network.PortalGunIndicatorPayload;
|
||||
import com.craisinlord.antarchy.content.network.PortalGunIndicatorRequestPayload;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Consumer;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.multiplayer.ClientLevel;
|
||||
|
||||
public final class PortalGunIndicatorClientState {
|
||||
private static final Map<UUID, PortalGunIndicatorPayload> STATES = new HashMap<>();
|
||||
private static ClientLevel level;
|
||||
private static long lastRequestTick = Long.MIN_VALUE;
|
||||
private static UUID lastRequestedGun;
|
||||
private static Consumer<PortalGunIndicatorRequestPayload> requestSender = payload -> {};
|
||||
|
||||
private PortalGunIndicatorClientState() {
|
||||
}
|
||||
|
||||
public static void setRequestSender(Consumer<PortalGunIndicatorRequestPayload> sender) {
|
||||
requestSender = sender;
|
||||
}
|
||||
|
||||
public static void requestIfNeeded(Minecraft minecraft, UUID gunId) {
|
||||
ClientLevel currentLevel = minecraft.level;
|
||||
if (currentLevel == null || gunId == null || minecraft.player == null) {
|
||||
return;
|
||||
}
|
||||
if (level != currentLevel) {
|
||||
level = currentLevel;
|
||||
STATES.clear();
|
||||
lastRequestedGun = null;
|
||||
lastRequestTick = Long.MIN_VALUE;
|
||||
}
|
||||
long currentTick = minecraft.player.tickCount;
|
||||
if (!gunId.equals(lastRequestedGun) || currentTick - lastRequestTick >= 20L) {
|
||||
lastRequestedGun = gunId;
|
||||
lastRequestTick = currentTick;
|
||||
requestSender.accept(new PortalGunIndicatorRequestPayload(gunId));
|
||||
}
|
||||
}
|
||||
|
||||
public static void update(PortalGunIndicatorPayload payload) {
|
||||
STATES.put(payload.gunId(), payload);
|
||||
}
|
||||
|
||||
public static PortalGunIndicatorPayload get(UUID gunId) {
|
||||
return gunId == null ? null : STATES.get(gunId);
|
||||
}
|
||||
|
||||
public static void clear() {
|
||||
STATES.clear();
|
||||
level = null;
|
||||
lastRequestedGun = null;
|
||||
lastRequestTick = Long.MIN_VALUE;
|
||||
}
|
||||
}
|
||||
+3
-1
@@ -3,6 +3,7 @@ package com.craisinlord.antarchy.content.client;
|
||||
import com.craisinlord.antarchy.content.portalgun.PortalGunWorldPortalShape;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Deque;
|
||||
import java.util.UUID;
|
||||
import net.minecraft.world.phys.AABB;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.joml.Matrix4f;
|
||||
@@ -133,7 +134,8 @@ public final class PortalGunPortalRenderState {
|
||||
Matrix4f viewMatrix,
|
||||
Matrix4f projectionMatrix,
|
||||
int renderLevel,
|
||||
boolean renderAll
|
||||
boolean renderAll,
|
||||
UUID destinationId
|
||||
) {
|
||||
}
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
package com.craisinlord.antarchy.content.client;
|
||||
|
||||
import com.craisinlord.antarchy.content.network.PortalGunResetPayload;
|
||||
import java.util.function.Consumer;
|
||||
import net.minecraft.client.Minecraft;
|
||||
|
||||
public final class PortalGunResetClientState {
|
||||
private static boolean resetDown;
|
||||
private static boolean attackDown;
|
||||
private static boolean useDown;
|
||||
private static boolean selectedSide;
|
||||
|
||||
private PortalGunResetClientState() {
|
||||
}
|
||||
|
||||
public static boolean tick(Minecraft minecraft, boolean enabled, boolean resetPressed, Consumer<PortalGunResetPayload> sender) {
|
||||
boolean canInput = enabled && minecraft.player != null && minecraft.level != null && minecraft.screen == null;
|
||||
boolean active = canInput && resetPressed;
|
||||
boolean attack = canInput && minecraft.options.keyAttack.isDown();
|
||||
boolean use = canInput && minecraft.options.keyUse.isDown();
|
||||
if (active && !resetDown) {
|
||||
sender.accept(new PortalGunResetPayload(com.craisinlord.antarchy.content.portalgun.PortalGunResetManager.ARM));
|
||||
selectedSide = false;
|
||||
}
|
||||
if (active && attack && !attackDown) {
|
||||
sender.accept(new PortalGunResetPayload(com.craisinlord.antarchy.content.portalgun.PortalGunResetManager.RESET_BLUE));
|
||||
selectedSide = true;
|
||||
}
|
||||
if (active && use && !useDown) {
|
||||
selectedSide = true;
|
||||
}
|
||||
if (!active && resetDown) {
|
||||
int action = !canInput || selectedSide
|
||||
? com.craisinlord.antarchy.content.portalgun.PortalGunResetManager.RELEASE
|
||||
: com.craisinlord.antarchy.content.portalgun.PortalGunResetManager.RESET_ALL;
|
||||
sender.accept(new PortalGunResetPayload(action));
|
||||
selectedSide = false;
|
||||
}
|
||||
resetDown = active;
|
||||
attackDown = attack;
|
||||
useDown = use;
|
||||
return active;
|
||||
}
|
||||
|
||||
public static void clear() {
|
||||
resetDown = false;
|
||||
attackDown = false;
|
||||
useDown = false;
|
||||
selectedSide = false;
|
||||
}
|
||||
}
|
||||
+34
-23
@@ -7,10 +7,12 @@ 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 int TELEPORT_COOLDOWN_TICKS = 40;
|
||||
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<>();
|
||||
@@ -32,8 +34,11 @@ public final class PortalGunRollClientState {
|
||||
if (minecraft.level == null || entity == null) {
|
||||
return false;
|
||||
}
|
||||
for (Entity renderEntity : minecraft.level.entitiesForRendering()) {
|
||||
if (renderEntity instanceof PortalGunPortalEntity portal && portal.isAlive() && portal.intersectsEntityBounds(entity)) {
|
||||
for (PortalGunPortalEntity portal : minecraft.level.getEntitiesOfClass(
|
||||
PortalGunPortalEntity.class,
|
||||
entity.getBoundingBox().inflate(PORTAL_QUERY_RADIUS),
|
||||
Entity::isAlive)) {
|
||||
if (portal.intersectsEntityBounds(entity)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -67,10 +72,10 @@ public final class PortalGunRollClientState {
|
||||
}
|
||||
long gameTime = minecraft.level.getGameTime();
|
||||
TELEPORT_COOLDOWNS.entrySet().removeIf(entry -> entry.getValue() <= gameTime);
|
||||
for (Entity entity : minecraft.level.entitiesForRendering()) {
|
||||
if (!(entity instanceof PortalGunPortalEntity portal) || !portal.isAlive()) {
|
||||
continue;
|
||||
}
|
||||
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;
|
||||
@@ -81,11 +86,13 @@ public final class PortalGunRollClientState {
|
||||
if (!canTeleport(cameraEntity, gameTime)) {
|
||||
continue;
|
||||
}
|
||||
Vec3 crossingProbe = portal.resolveCrossingProbe(cameraEntity);
|
||||
if (crossingProbe == null) {
|
||||
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, crossingProbe, gameTime);
|
||||
applyPredictedTeleport(cameraEntity, portal, linkedPortal, crossing, gameTime);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -98,26 +105,30 @@ public final class PortalGunRollClientState {
|
||||
return cooldownUntil <= gameTime;
|
||||
}
|
||||
|
||||
private static void applyPredictedTeleport(Entity entity, PortalGunPortalEntity sourcePortal, PortalGunPortalEntity destinationPortal, Vec3 currentProbe, long 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 relativeProbe = currentProbe.subtract(sourcePortal.position());
|
||||
Vec3 transformedProbe = PortalGunTransformUtil.transformPosition(sourcePortal, destinationPortal, relativeProbe);
|
||||
Vec3 transformedEntityOffset = PortalGunTransformUtil.transformVector(sourcePortal, destinationPortal, entity.position().subtract(currentProbe));
|
||||
Vec3 destinationNormal = destinationPortal.getNormalVec().normalize();
|
||||
double exitClearance = Math.max(0.35D, entity.getBbWidth() * 0.5D + 0.16D);
|
||||
Vec3 exitProbe = destinationPortal.position().add(transformedProbe).add(destinationNormal.scale(exitClearance - transformedProbe.dot(destinationNormal)));
|
||||
Vec3 exitPos = exitProbe.add(transformedEntityOffset);
|
||||
Vec3 transformedMotion = PortalGunTransformUtil.transformVector(sourcePortal, destinationPortal, entity.getDeltaMovement());
|
||||
double outward = transformedMotion.dot(destinationNormal);
|
||||
if (outward < 0.42D) {
|
||||
transformedMotion = transformedMotion.add(destinationNormal.scale(0.42D - outward));
|
||||
}
|
||||
Vec3 resolvedMovement = entity.position().subtract(entity.xo, entity.yo, entity.zo);
|
||||
PortalGunPortalEntity.PortalTransitTransform transit = sourcePortal.previewTransit(entity, destinationPortal, crossing, resolvedMovement);
|
||||
Vec3 transformedMotion = transit.movement();
|
||||
transformedMotion = clampPortalMotion(transformedMotion);
|
||||
rollDegrees = PortalGunTransformUtil.rollFromOrientation(transformedLook, transformedUp);
|
||||
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;
|
||||
}
|
||||
}
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
package com.craisinlord.antarchy.content.client;
|
||||
|
||||
import com.craisinlord.antarchy.content.item.PortalGunItem;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.Options;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
|
||||
public final class PortalGunZoomClientState {
|
||||
private static int progress;
|
||||
private static boolean zooming;
|
||||
private static Double originalSensitivity;
|
||||
|
||||
private PortalGunZoomClientState() {
|
||||
}
|
||||
|
||||
public static void tick(Minecraft minecraft, boolean held, boolean togglePressed) {
|
||||
if (minecraft.player == null || minecraft.level == null) {
|
||||
zooming = false;
|
||||
progress = 0;
|
||||
restoreSensitivity(minecraft.options);
|
||||
return;
|
||||
}
|
||||
|
||||
ItemStack main = minecraft.player.getMainHandItem();
|
||||
ItemStack off = minecraft.player.getOffhandItem();
|
||||
boolean holdingPortalGun = held && (main.getItem() instanceof PortalGunItem || off.getItem() instanceof PortalGunItem);
|
||||
if (togglePressed && holdingPortalGun) {
|
||||
zooming = !zooming;
|
||||
if (zooming && originalSensitivity == null) {
|
||||
originalSensitivity = minecraft.options.sensitivity().get();
|
||||
}
|
||||
}
|
||||
if (!holdingPortalGun) {
|
||||
zooming = false;
|
||||
}
|
||||
|
||||
progress = Math.max(0, Math.min(5, progress + (zooming ? 1 : -1)));
|
||||
if (originalSensitivity != null) {
|
||||
if (progress == 0) {
|
||||
restoreSensitivity(minecraft.options);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static double applyFov(double fov, float partialTick, Options options) {
|
||||
if (progress == 0 && !zooming) {
|
||||
restoreSensitivity(options);
|
||||
return fov;
|
||||
}
|
||||
double easedProgress = zooming ? progress - 1.0D + partialTick : progress + 1.0D - partialTick;
|
||||
double zoomFactor = zoomFactor(easedProgress);
|
||||
if (originalSensitivity != null) {
|
||||
options.sensitivity().set(originalSensitivity * zoomFactor);
|
||||
}
|
||||
return fov * zoomFactor;
|
||||
}
|
||||
|
||||
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)))));
|
||||
}
|
||||
|
||||
private static double zoomFactor(int progress) {
|
||||
return zoomFactor((double) progress);
|
||||
}
|
||||
|
||||
private static void restoreSensitivity(Options options) {
|
||||
if (originalSensitivity != null) {
|
||||
options.sensitivity().set(originalSensitivity);
|
||||
originalSensitivity = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
+7
-23
@@ -2,12 +2,8 @@ package com.craisinlord.antarchy.content.client.game;
|
||||
|
||||
import com.craisinlord.antos.api.client.game.ComputerGame;
|
||||
import com.craisinlord.antos.api.client.game.ComputerGameRegistry;
|
||||
import com.craisinlord.antarchy.content.network.AntarchyGameNetworking;
|
||||
import com.craisinlord.antarchy.content.network.AntarchyGamePayload;
|
||||
import com.craisinlord.antarchy.content.client.AntarchyGameClientState;
|
||||
import net.minecraft.client.gui.Font;
|
||||
import net.minecraft.client.gui.GuiGraphics;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import java.util.function.IntPredicate;
|
||||
import java.util.function.Predicate;
|
||||
@@ -17,11 +13,11 @@ public final class AntarchyComputerGames {
|
||||
private AntarchyComputerGames() {}
|
||||
|
||||
public static void register() {
|
||||
registerIfMissing(game("basilisk", "BASILISK", pos -> {
|
||||
BasiliskProgram program = new BasiliskProgram(true, 0, 0, score -> AntarchyGameNetworking.saveBasiliskScore(pos, score));
|
||||
var loaded = new java.util.concurrent.atomic.AtomicBoolean();
|
||||
AntarchyGameNetworking.requestBasiliskState(pos);
|
||||
return new GameSession(() -> { if (!loaded.get()) loaded.set(loadScores(pos, AntarchyGamePayload.BASILISK_STATE, program::setStoredScores)); program.tick(); }, program::render, key -> loaded.get() && program.keyPressed(key), ignored -> false);
|
||||
registerIfMissing(game("basilisk", "BASILISK", () -> {
|
||||
// AntOS 1.2.1 creates game sessions without passing the computer position.
|
||||
// Keep Basilisk playable without a position-bound score callback.
|
||||
BasiliskProgram program = new BasiliskProgram(true, 0, 0, score -> {});
|
||||
return new GameSession(program::tick, program::render, program::keyPressed, ignored -> false);
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -29,18 +25,6 @@ public final class AntarchyComputerGames {
|
||||
if (ComputerGameRegistry.get(game.id()) == null) ComputerGameRegistry.register(game);
|
||||
}
|
||||
|
||||
private static boolean loadScores(BlockPos pos, int action, java.util.function.BiConsumer<Integer, Integer> setter) {
|
||||
String response = AntarchyGameClientState.getGameState(pos, action);
|
||||
if (response == null) return false;
|
||||
if (response.isBlank()) { AntarchyGameClientState.clearGameState(pos, action); return true; }
|
||||
String[] scores = response.split("\0", 2);
|
||||
if (scores.length != 2) return false;
|
||||
try { setter.accept(Integer.parseInt(scores[0]), Integer.parseInt(scores[1])); }
|
||||
catch (NumberFormatException ignored) { return false; }
|
||||
AntarchyGameClientState.clearGameState(pos, action);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static ComputerGame game(String path, String title, ProgramFactory factory) {
|
||||
ResourceLocation id = ResourceLocation.fromNamespaceAndPath("antarchy", path);
|
||||
ResourceLocation disk = ResourceLocation.fromNamespaceAndPath("antarchy", path + "_game");
|
||||
@@ -48,12 +32,12 @@ public final class AntarchyComputerGames {
|
||||
@Override public ResourceLocation id() { return id; }
|
||||
@Override public ResourceLocation diskId() { return disk; }
|
||||
@Override public String title() { return title; }
|
||||
@Override public Session create(BlockPos position) { return factory.create(position); }
|
||||
@Override public Session create() { return factory.create(); }
|
||||
};
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
private interface ProgramFactory { ComputerGame.Session create(BlockPos position); }
|
||||
private interface ProgramFactory { ComputerGame.Session create(); }
|
||||
|
||||
@FunctionalInterface private interface GameRenderer { void render(GuiGraphics graphics, Font font, int x, int y, int width, int height); }
|
||||
private static final class GameSession implements ComputerGame.Session {
|
||||
|
||||
+56
-14
@@ -1,11 +1,15 @@
|
||||
package com.craisinlord.antarchy.content.client.renderer;
|
||||
|
||||
import com.craisinlord.antarchy.Antarchy;
|
||||
import com.craisinlord.antarchy.config.AntarchySettings;
|
||||
import com.craisinlord.antarchy.content.client.model.PortalGunPortalModel;
|
||||
import com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity;
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import com.mojang.blaze3d.vertex.VertexConsumer;
|
||||
import net.minecraft.client.renderer.MultiBufferSource;
|
||||
import net.minecraft.client.GraphicsStatus;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.LightTexture;
|
||||
import net.minecraft.client.renderer.MultiBufferSource;
|
||||
import net.minecraft.client.renderer.RenderType;
|
||||
import net.minecraft.client.renderer.entity.EntityRendererProvider;
|
||||
import net.minecraft.client.renderer.texture.OverlayTexture;
|
||||
@@ -24,23 +28,61 @@ public class PortalGunPortalRenderer extends GeoEntityRenderer<PortalGunPortalEn
|
||||
@Override
|
||||
public void render(PortalGunPortalEntity entity, float entityYaw, float partialTick, PoseStack poseStack, MultiBufferSource bufferSource, int packedLight) {
|
||||
poseStack.pushPose();
|
||||
VertexConsumer consumer = bufferSource.getBuffer(RenderType.entityTranslucent(this.getTextureLocation(entity)));
|
||||
Matrix4f pose = poseStack.last().pose();
|
||||
putPortalVertex(consumer, pose, entity, -0.5F, -1.0F, 0.0F, 1.0F, false);
|
||||
putPortalVertex(consumer, pose, entity, -0.5F, 1.0F, 0.0F, 0.0F, false);
|
||||
putPortalVertex(consumer, pose, entity, 0.5F, 1.0F, 1.0F, 0.0F, false);
|
||||
putPortalVertex(consumer, pose, entity, 0.5F, -1.0F, 1.0F, 1.0F, false);
|
||||
float portalScale = entity.getPortalVisualScale(partialTick);
|
||||
float halfWidth = entity.getPortalWidth() * 0.5F;
|
||||
float halfHeight = entity.getPortalHeight() * 0.5F;
|
||||
ResourceLocation texture = portalTexture(entity);
|
||||
VertexConsumer consumer = bufferSource.getBuffer(RenderType.entityTranslucent(texture));
|
||||
if (isFancyPortalsEnabled()) {
|
||||
float frameV = Math.floorMod((int) ((entity.level().getGameTime() + partialTick) / 5.0F), 4) * 0.25F;
|
||||
putAnimatedPortalVertex(consumer, pose, entity, portalScale, frameV, -halfWidth, -halfHeight, 0.0F, 1.0F);
|
||||
putAnimatedPortalVertex(consumer, pose, entity, portalScale, frameV, -halfWidth, halfHeight, 0.0F, 0.0F);
|
||||
putAnimatedPortalVertex(consumer, pose, entity, portalScale, frameV, halfWidth, halfHeight, 1.0F, 0.0F);
|
||||
putAnimatedPortalVertex(consumer, pose, entity, portalScale, frameV, halfWidth, -halfHeight, 1.0F, 1.0F);
|
||||
} else {
|
||||
putPortalVertex(consumer, pose, entity, portalScale, -halfWidth, -halfHeight, 0.0F, 1.0F, 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);
|
||||
}
|
||||
VertexConsumer edge = bufferSource.getBuffer(RenderType.entityTranslucent(this.getTextureLocation(entity)));
|
||||
putPortalVertex(edge, pose, entity, -0.56F, -1.07F, 0.0F, 1.0F, true);
|
||||
putPortalVertex(edge, pose, entity, -0.56F, 1.07F, 0.0F, 0.0F, true);
|
||||
putPortalVertex(edge, pose, entity, 0.56F, 1.07F, 1.0F, 0.0F, true);
|
||||
putPortalVertex(edge, pose, entity, 0.56F, -1.07F, 1.0F, 1.0F, true);
|
||||
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);
|
||||
poseStack.popPose();
|
||||
}
|
||||
|
||||
private static void putPortalVertex(VertexConsumer consumer, Matrix4f pose, PortalGunPortalEntity entity, float horizontal, float vertical, float u, float v, boolean channelTint) {
|
||||
Vec3 position = entity.getWidthVec().normalize().scale(horizontal)
|
||||
.add(entity.getUpVec().normalize().scale(vertical))
|
||||
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);
|
||||
}
|
||||
|
||||
private static boolean isFancyPortalsEnabled() {
|
||||
Minecraft minecraft = Minecraft.getInstance();
|
||||
return AntarchySettings.portalGunFancyPortals()
|
||||
&& minecraft.options.graphicsMode().get() == GraphicsStatus.FANCY;
|
||||
}
|
||||
|
||||
private static void putAnimatedPortalVertex(VertexConsumer consumer, Matrix4f pose, PortalGunPortalEntity entity, float portalScale, float frameV, float horizontal, float vertical, float u, float v) {
|
||||
Vec3 position = entity.getWidthVec().normalize().scale(horizontal * portalScale)
|
||||
.add(entity.getUpVec().normalize().scale(vertical * portalScale))
|
||||
.add(entity.getNormalVec().normalize().scale(0.03125D));
|
||||
consumer.addVertex(pose, (float) position.x, (float) position.y, (float) position.z)
|
||||
.setColor(255, 255, 255, 210)
|
||||
.setUv(u, frameV + v * 0.25F)
|
||||
.setOverlay(OverlayTexture.NO_OVERLAY)
|
||||
.setLight(LightTexture.FULL_BRIGHT)
|
||||
.setNormal((float) entity.getNormalVec().x, (float) entity.getNormalVec().y, (float) entity.getNormalVec().z);
|
||||
}
|
||||
|
||||
private static void putPortalVertex(VertexConsumer consumer, Matrix4f pose, PortalGunPortalEntity entity, float portalScale, float horizontal, float vertical, float u, float v, boolean channelTint) {
|
||||
Vec3 position = entity.getWidthVec().normalize().scale(horizontal * portalScale)
|
||||
.add(entity.getUpVec().normalize().scale(vertical * portalScale))
|
||||
.add(entity.getNormalVec().normalize().scale(0.03125D));
|
||||
int red = channelTint ? entity.getChannelRed() : 255;
|
||||
int green = channelTint ? entity.getChannelGreen() : 255;
|
||||
@@ -60,6 +102,6 @@ public class PortalGunPortalRenderer extends GeoEntityRenderer<PortalGunPortalEn
|
||||
|
||||
@Override
|
||||
public @Nullable RenderType getRenderType(PortalGunPortalEntity animatable, ResourceLocation texture, @Nullable MultiBufferSource bufferSource, float partialTick) {
|
||||
return RenderType.entityTranslucent(this.getTextureLocation(animatable));
|
||||
return RenderType.entityTranslucent(portalTexture(animatable));
|
||||
}
|
||||
}
|
||||
+148
@@ -0,0 +1,148 @@
|
||||
package com.craisinlord.antarchy.content.client.renderer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.multiplayer.ClientLevel;
|
||||
import net.minecraft.client.renderer.LevelRenderer;
|
||||
import net.minecraft.client.renderer.chunk.RenderRegionCache;
|
||||
import net.minecraft.client.renderer.chunk.SectionRenderDispatcher;
|
||||
|
||||
public final class PortalGunPortalRendererPool {
|
||||
private static final int MAX_RENDERERS = 8;
|
||||
private static final long PORTAL_SECTION_BUDGET_NANOS = 4_166_666L;
|
||||
private static final LinkedHashMap<UUID, PortalRenderer> RENDERERS = new LinkedHashMap<>(8, 0.75F, true);
|
||||
private static final Set<LevelRenderer> PROXY_RENDERERS = java.util.Collections.newSetFromMap(new IdentityHashMap<>());
|
||||
private static final ThreadLocal<Long> SECTION_BUDGET_DEADLINE = ThreadLocal.withInitial(() -> 0L);
|
||||
private static final ThreadLocal<Integer> SCHEDULED_SECTION_REBUILDS = ThreadLocal.withInitial(() -> 0);
|
||||
private static final ThreadLocal<Boolean> SKIP_SET_NOT_DIRTY = ThreadLocal.withInitial(() -> false);
|
||||
private static ClientLevel ownerLevel;
|
||||
|
||||
private PortalGunPortalRendererPool() {
|
||||
}
|
||||
|
||||
public static LevelRenderer acquire(Minecraft minecraft, UUID destinationId, int width, int height) {
|
||||
if (ownerLevel != minecraft.level) {
|
||||
clear();
|
||||
ownerLevel = minecraft.level;
|
||||
}
|
||||
PortalRenderer state = RENDERERS.get(destinationId);
|
||||
if (state == null) {
|
||||
LevelRenderer renderer = new LevelRenderer(
|
||||
minecraft,
|
||||
minecraft.getEntityRenderDispatcher(),
|
||||
minecraft.getBlockEntityRenderDispatcher(),
|
||||
minecraft.renderBuffers()
|
||||
);
|
||||
PROXY_RENDERERS.add(renderer);
|
||||
renderer.setLevel(minecraft.level);
|
||||
renderer.resize(width, height);
|
||||
state = new PortalRenderer(renderer, width, height);
|
||||
RENDERERS.put(destinationId, state);
|
||||
trim();
|
||||
} else if (state.width() != width || state.height() != height) {
|
||||
state.renderer().resize(width, height);
|
||||
RENDERERS.put(destinationId, new PortalRenderer(state.renderer(), width, height));
|
||||
}
|
||||
return state.renderer();
|
||||
}
|
||||
|
||||
public static boolean isProxy(LevelRenderer renderer) {
|
||||
return PROXY_RENDERERS.contains(renderer);
|
||||
}
|
||||
|
||||
public static void beginFrame() {
|
||||
SECTION_BUDGET_DEADLINE.set(System.nanoTime() + PORTAL_SECTION_BUDGET_NANOS);
|
||||
SCHEDULED_SECTION_REBUILDS.set(0);
|
||||
SKIP_SET_NOT_DIRTY.set(false);
|
||||
}
|
||||
|
||||
public static void beginSceneBudget() {
|
||||
SECTION_BUDGET_DEADLINE.set(System.nanoTime() + PORTAL_SECTION_BUDGET_NANOS);
|
||||
SCHEDULED_SECTION_REBUILDS.set(0);
|
||||
SKIP_SET_NOT_DIRTY.set(false);
|
||||
}
|
||||
|
||||
public static int scheduledSectionRebuilds() {
|
||||
return SCHEDULED_SECTION_REBUILDS.get();
|
||||
}
|
||||
|
||||
public static long remainingSectionBudgetNanos() {
|
||||
return Math.max(0L, SECTION_BUDGET_DEADLINE.get() - System.nanoTime());
|
||||
}
|
||||
|
||||
public static void rebuildSection(LevelRenderer renderer, SectionRenderDispatcher dispatcher, SectionRenderDispatcher.RenderSection section, RenderRegionCache cache) {
|
||||
if (!isProxy(renderer)) {
|
||||
dispatcher.rebuildSectionSync(section, cache);
|
||||
SKIP_SET_NOT_DIRTY.set(false);
|
||||
return;
|
||||
}
|
||||
if (remainingSectionBudgetNanos() <= 0L) {
|
||||
SKIP_SET_NOT_DIRTY.set(true);
|
||||
return;
|
||||
}
|
||||
section.rebuildSectionAsync(dispatcher, cache);
|
||||
SCHEDULED_SECTION_REBUILDS.set(SCHEDULED_SECTION_REBUILDS.get() + 1);
|
||||
SKIP_SET_NOT_DIRTY.set(false);
|
||||
}
|
||||
|
||||
public static void rebuildSectionAsync(LevelRenderer renderer, SectionRenderDispatcher dispatcher, SectionRenderDispatcher.RenderSection section, RenderRegionCache cache) {
|
||||
if (!isProxy(renderer)) {
|
||||
section.rebuildSectionAsync(dispatcher, cache);
|
||||
SKIP_SET_NOT_DIRTY.set(false);
|
||||
return;
|
||||
}
|
||||
if (remainingSectionBudgetNanos() <= 0L) {
|
||||
SKIP_SET_NOT_DIRTY.set(true);
|
||||
return;
|
||||
}
|
||||
section.rebuildSectionAsync(dispatcher, cache);
|
||||
SCHEDULED_SECTION_REBUILDS.set(SCHEDULED_SECTION_REBUILDS.get() + 1);
|
||||
SKIP_SET_NOT_DIRTY.set(false);
|
||||
}
|
||||
|
||||
public static void setNotDirty(SectionRenderDispatcher.RenderSection section) {
|
||||
boolean leaveDirty = SKIP_SET_NOT_DIRTY.get();
|
||||
SKIP_SET_NOT_DIRTY.set(false);
|
||||
if (!leaveDirty) {
|
||||
section.setNotDirty();
|
||||
}
|
||||
}
|
||||
|
||||
public static void clear() {
|
||||
for (PortalRenderer state : new ArrayList<>(RENDERERS.values())) {
|
||||
LevelRenderer renderer = state.renderer();
|
||||
PortalGunPortalViewAreaManager.releaseRenderer(renderer);
|
||||
try {
|
||||
renderer.close();
|
||||
} finally {
|
||||
PROXY_RENDERERS.remove(renderer);
|
||||
}
|
||||
}
|
||||
RENDERERS.clear();
|
||||
ownerLevel = null;
|
||||
}
|
||||
|
||||
private static void trim() {
|
||||
Iterator<Map.Entry<UUID, PortalRenderer>> iterator = RENDERERS.entrySet().iterator();
|
||||
while (RENDERERS.size() > MAX_RENDERERS && iterator.hasNext()) {
|
||||
PortalRenderer state = iterator.next().getValue();
|
||||
iterator.remove();
|
||||
LevelRenderer renderer = state.renderer();
|
||||
PortalGunPortalViewAreaManager.releaseRenderer(renderer);
|
||||
try {
|
||||
renderer.close();
|
||||
} finally {
|
||||
PROXY_RENDERERS.remove(renderer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private record PortalRenderer(LevelRenderer renderer, int width, int height) {
|
||||
}
|
||||
}
|
||||
+403
@@ -0,0 +1,403 @@
|
||||
package com.craisinlord.antarchy.content.client.renderer;
|
||||
|
||||
import com.craisinlord.antarchy.mixins.client.LevelRendererPortalViewAreaAccessor;
|
||||
import com.mojang.blaze3d.vertex.VertexBuffer;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import net.minecraft.client.CloudStatus;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.multiplayer.ClientLevel;
|
||||
import net.minecraft.client.renderer.LevelRenderer;
|
||||
import net.minecraft.client.renderer.SectionOcclusionGraph;
|
||||
import net.minecraft.client.renderer.ViewArea;
|
||||
import net.minecraft.client.renderer.blockentity.BlockEntityRenderDispatcher;
|
||||
import net.minecraft.client.renderer.chunk.SectionRenderDispatcher;
|
||||
import net.minecraft.core.SectionPos;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.phys.HitResult;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
|
||||
public final class PortalGunPortalViewAreaManager {
|
||||
private static final int MAX_VIEW_AREAS = 8;
|
||||
private static final LinkedHashMap<ViewKey, ViewState> VIEW_AREAS = new LinkedHashMap<>(8, 0.75F, true);
|
||||
private static final Map<SectionRenderDispatcher.RenderSection, ViewState> SECTION_OWNERS = Collections.synchronizedMap(new IdentityHashMap<>());
|
||||
private static ClientLevel ownerLevel;
|
||||
private static int ownerViewDistance = -1;
|
||||
|
||||
private PortalGunPortalViewAreaManager() {
|
||||
}
|
||||
|
||||
public static Scope enter(Minecraft minecraft, LevelRenderer renderer, UUID destinationId, Vec3 cameraPos, net.minecraft.client.Camera portalCamera) {
|
||||
LevelRendererPortalViewAreaAccessor accessor = (LevelRendererPortalViewAreaAccessor) renderer;
|
||||
int viewDistance = minecraft.options.getEffectiveRenderDistance();
|
||||
if (ownerLevel != minecraft.level || ownerViewDistance != viewDistance) {
|
||||
clear();
|
||||
ownerLevel = minecraft.level;
|
||||
ownerViewDistance = viewDistance;
|
||||
}
|
||||
Scope sodiumScope = SodiumCompat.isLoaded() ? new SodiumScope(SodiumCompat.beginPortalView(portalCamera)) : null;
|
||||
ViewKey viewKey = new ViewKey(renderer, destinationId);
|
||||
ViewState state = VIEW_AREAS.get(viewKey);
|
||||
if (state == null) {
|
||||
ViewArea area = accessor.antarchy$getViewArea();
|
||||
SectionOcclusionGraph graph = accessor.antarchy$getSectionOcclusionGraph();
|
||||
ObjectArrayList<SectionRenderDispatcher.RenderSection> sections = accessor.antarchy$getVisibleSections();
|
||||
state = new ViewState(area, graph, sections, accessor.antarchy$getGlobalBlockEntities());
|
||||
state.cloudBuffer = accessor.antarchy$getCloudBuffer();
|
||||
state.generateClouds = accessor.antarchy$getGenerateClouds();
|
||||
state.prevCloudX = accessor.antarchy$getPrevCloudX();
|
||||
state.prevCloudY = accessor.antarchy$getPrevCloudY();
|
||||
state.prevCloudZ = accessor.antarchy$getPrevCloudZ();
|
||||
state.prevCloudColor = accessor.antarchy$getPrevCloudColor();
|
||||
state.prevCloudsType = accessor.antarchy$getPrevCloudsType();
|
||||
for (SectionRenderDispatcher.RenderSection section : area.sections) {
|
||||
((PortalViewSectionMarker) section).antarchy$markPortalViewSection();
|
||||
SECTION_OWNERS.put(section, state);
|
||||
}
|
||||
VIEW_AREAS.put(viewKey, state);
|
||||
trimOldViews();
|
||||
}
|
||||
state.area.repositionCamera(cameraPos.x, cameraPos.z);
|
||||
int sectionX = SectionPos.posToSectionCoord(cameraPos.x);
|
||||
int sectionY = SectionPos.posToSectionCoord(cameraPos.y);
|
||||
int sectionZ = SectionPos.posToSectionCoord(cameraPos.z);
|
||||
state.cameraPos = cameraPos;
|
||||
state.lastSectionX = sectionX;
|
||||
state.lastSectionY = sectionY;
|
||||
state.lastSectionZ = sectionZ;
|
||||
Scope scope = new VanillaScope(renderer, accessor, state, minecraft.getBlockEntityRenderDispatcher());
|
||||
accessor.antarchy$setViewArea(state.area);
|
||||
accessor.antarchy$setSectionOcclusionGraph(state.graph);
|
||||
accessor.antarchy$setVisibleSections(state.visibleSections);
|
||||
accessor.antarchy$setGlobalBlockEntities(state.globalBlockEntities);
|
||||
accessor.antarchy$setLastCameraSectionX(sectionX);
|
||||
accessor.antarchy$setLastCameraSectionY(sectionY);
|
||||
accessor.antarchy$setLastCameraSectionZ(sectionZ);
|
||||
return sodiumScope == null ? scope : new CombinedScope(scope, sodiumScope);
|
||||
}
|
||||
|
||||
public static boolean routeSectionCompiled(SectionRenderDispatcher.RenderSection section) {
|
||||
ViewState state = SECTION_OWNERS.get(section);
|
||||
if (state == null) {
|
||||
return ((PortalViewSectionMarker) section).antarchy$isPortalViewSection();
|
||||
}
|
||||
state.graph.onSectionCompiled(section);
|
||||
return true;
|
||||
}
|
||||
|
||||
public static Vec3 cameraForSection(SectionRenderDispatcher.RenderSection section, Vec3 fallback) {
|
||||
ViewState state = SECTION_OWNERS.get(section);
|
||||
return state == null || state.cameraPos == null ? fallback : state.cameraPos;
|
||||
}
|
||||
|
||||
public static boolean updateGlobalBlockEntities(SectionRenderDispatcher.RenderSection section, Iterable<BlockEntity> removed, Iterable<BlockEntity> added) {
|
||||
ViewState state = SECTION_OWNERS.get(section);
|
||||
if (state == null) {
|
||||
return ((PortalViewSectionMarker) section).antarchy$isPortalViewSection();
|
||||
}
|
||||
synchronized (state.globalBlockEntities) {
|
||||
for (BlockEntity blockEntity : removed) {
|
||||
state.globalBlockEntities.remove(blockEntity);
|
||||
}
|
||||
for (BlockEntity blockEntity : added) {
|
||||
state.globalBlockEntities.add(blockEntity);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void onSectionDirty(int sectionX, int sectionY, int sectionZ, boolean playerChanged) {
|
||||
for (ViewState state : VIEW_AREAS.values()) {
|
||||
int sectionRange = state.area.getViewDistance() + 1;
|
||||
if (Math.abs((long) sectionX - state.lastSectionX) > sectionRange
|
||||
|| Math.abs((long) sectionZ - state.lastSectionZ) > sectionRange
|
||||
|| sectionY < ownerLevel.getMinSection()
|
||||
|| sectionY > ownerLevel.getMaxSection()) {
|
||||
continue;
|
||||
}
|
||||
state.area.setDirty(sectionX, sectionY, sectionZ, playerChanged);
|
||||
}
|
||||
}
|
||||
|
||||
public static void onChunkLoaded(net.minecraft.world.level.ChunkPos pos) {
|
||||
for (ViewState state : VIEW_AREAS.values()) {
|
||||
state.graph.onChunkLoaded(pos);
|
||||
markChunkDirty(state, pos);
|
||||
}
|
||||
}
|
||||
|
||||
public static void onChunkDropped(net.minecraft.world.level.ChunkPos pos) {
|
||||
for (ViewState state : VIEW_AREAS.values()) {
|
||||
markChunkDirty(state, pos);
|
||||
state.graph.invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
private static void markChunkDirty(ViewState state, net.minecraft.world.level.ChunkPos pos) {
|
||||
int sectionRange = state.area.getViewDistance() + 1;
|
||||
if (Math.abs((long) pos.x - state.lastSectionX) <= sectionRange
|
||||
&& Math.abs((long) pos.z - state.lastSectionZ) <= sectionRange) {
|
||||
for (int sectionY = ownerLevel.getMinSection(); sectionY <= ownerLevel.getMaxSection(); sectionY++) {
|
||||
state.area.setDirty(pos.x, sectionY, pos.z, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void clear() {
|
||||
SodiumCompat.clear();
|
||||
for (ViewState state : VIEW_AREAS.values()) {
|
||||
state.graph.waitAndReset(state.area);
|
||||
}
|
||||
VIEW_AREAS.clear();
|
||||
SECTION_OWNERS.clear();
|
||||
ownerLevel = null;
|
||||
ownerViewDistance = -1;
|
||||
}
|
||||
|
||||
public static void releaseRenderer(LevelRenderer renderer) {
|
||||
Iterator<Map.Entry<ViewKey, ViewState>> iterator = VIEW_AREAS.entrySet().iterator();
|
||||
while (iterator.hasNext()) {
|
||||
Map.Entry<ViewKey, ViewState> entry = iterator.next();
|
||||
if (entry.getKey().renderer() == renderer) {
|
||||
ViewState state = entry.getValue();
|
||||
state.graph.waitAndReset(state.area);
|
||||
removeOwners(state);
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void trimOldViews() {
|
||||
Iterator<Map.Entry<ViewKey, ViewState>> iterator = VIEW_AREAS.entrySet().iterator();
|
||||
while (VIEW_AREAS.size() > MAX_VIEW_AREAS && iterator.hasNext()) {
|
||||
ViewState state = iterator.next().getValue();
|
||||
iterator.remove();
|
||||
state.graph.waitAndReset(state.area);
|
||||
removeOwners(state);
|
||||
}
|
||||
}
|
||||
|
||||
private static void removeOwners(ViewState state) {
|
||||
for (SectionRenderDispatcher.RenderSection section : state.area.sections) {
|
||||
SECTION_OWNERS.remove(section);
|
||||
}
|
||||
}
|
||||
|
||||
private static final class ViewState {
|
||||
private final ViewArea area;
|
||||
private final SectionOcclusionGraph graph;
|
||||
private final ObjectArrayList<SectionRenderDispatcher.RenderSection> visibleSections;
|
||||
private final Set<BlockEntity> globalBlockEntities;
|
||||
private VertexBuffer cloudBuffer;
|
||||
private boolean generateClouds = true;
|
||||
private int prevCloudX = Integer.MIN_VALUE;
|
||||
private int prevCloudY = Integer.MIN_VALUE;
|
||||
private int prevCloudZ = Integer.MIN_VALUE;
|
||||
private Vec3 prevCloudColor = Vec3.ZERO;
|
||||
private CloudStatus prevCloudsType;
|
||||
private int lastSectionX;
|
||||
private int lastSectionY;
|
||||
private int lastSectionZ;
|
||||
private volatile Vec3 cameraPos;
|
||||
private double transparentSortX = Double.NaN;
|
||||
private double transparentSortY = Double.NaN;
|
||||
private double transparentSortZ = Double.NaN;
|
||||
private double prevCamX = Double.NaN;
|
||||
private double prevCamY = Double.NaN;
|
||||
private double prevCamZ = Double.NaN;
|
||||
private double prevCamRotX = Double.NaN;
|
||||
private double prevCamRotY = Double.NaN;
|
||||
|
||||
private ViewState(ViewArea area, SectionOcclusionGraph graph, ObjectArrayList<SectionRenderDispatcher.RenderSection> visibleSections, Set<BlockEntity> globalBlockEntities) {
|
||||
this.area = area;
|
||||
this.graph = graph;
|
||||
this.visibleSections = visibleSections;
|
||||
this.globalBlockEntities = globalBlockEntities;
|
||||
}
|
||||
}
|
||||
|
||||
private record ViewKey(LevelRenderer renderer, UUID destinationId) {
|
||||
}
|
||||
|
||||
public interface Scope extends AutoCloseable {
|
||||
@Override
|
||||
void close();
|
||||
}
|
||||
|
||||
private static final class CombinedScope implements Scope {
|
||||
private final Scope viewAreaScope;
|
||||
private final Scope sodiumScope;
|
||||
|
||||
private CombinedScope(Scope viewAreaScope, Scope sodiumScope) {
|
||||
this.viewAreaScope = viewAreaScope;
|
||||
this.sodiumScope = sodiumScope;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
try {
|
||||
viewAreaScope.close();
|
||||
} finally {
|
||||
sodiumScope.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final class SodiumScope implements Scope {
|
||||
private final SodiumCompat.TerrainScope terrainScope;
|
||||
|
||||
private SodiumScope(SodiumCompat.TerrainScope terrainScope) {
|
||||
this.terrainScope = terrainScope;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
terrainScope.close();
|
||||
}
|
||||
}
|
||||
|
||||
private static final class VanillaScope implements Scope {
|
||||
private final LevelRendererPortalViewAreaAccessor accessor;
|
||||
private final ViewArea previousArea;
|
||||
private final SectionOcclusionGraph previousGraph;
|
||||
private final ObjectArrayList<SectionRenderDispatcher.RenderSection> previousVisibleSections;
|
||||
private final Set<BlockEntity> previousGlobalBlockEntities;
|
||||
private final VertexBuffer previousCloudBuffer;
|
||||
private final boolean previousGenerateClouds;
|
||||
private final int previousCloudX;
|
||||
private final int previousCloudY;
|
||||
private final int previousCloudZ;
|
||||
private final Vec3 previousCloudColor;
|
||||
private final CloudStatus previousCloudsType;
|
||||
private final int previousRainSoundTime;
|
||||
private final int previousSectionX;
|
||||
private final int previousSectionY;
|
||||
private final int previousSectionZ;
|
||||
private final double previousPrevCamX;
|
||||
private final double previousPrevCamY;
|
||||
private final double previousPrevCamZ;
|
||||
private final double previousPrevCamRotX;
|
||||
private final double previousPrevCamRotY;
|
||||
private final double previousTransparentSortX;
|
||||
private final double previousTransparentSortY;
|
||||
private final double previousTransparentSortZ;
|
||||
private final Vec3 previousDispatcherCamera;
|
||||
private final SectionRenderDispatcher dispatcher;
|
||||
private final BlockEntityRenderDispatcher blockEntityDispatcher;
|
||||
private final Level previousBlockEntityLevel;
|
||||
private final net.minecraft.client.Camera previousBlockEntityCamera;
|
||||
private final HitResult previousBlockEntityHitResult;
|
||||
private final ViewState state;
|
||||
private boolean closed;
|
||||
|
||||
private VanillaScope(LevelRenderer renderer, LevelRendererPortalViewAreaAccessor accessor, ViewState state, BlockEntityRenderDispatcher blockEntityDispatcher) {
|
||||
this.accessor = accessor;
|
||||
this.state = state;
|
||||
this.blockEntityDispatcher = blockEntityDispatcher;
|
||||
this.previousBlockEntityLevel = blockEntityDispatcher.level;
|
||||
this.previousBlockEntityCamera = blockEntityDispatcher.camera;
|
||||
this.previousBlockEntityHitResult = blockEntityDispatcher.cameraHitResult;
|
||||
this.previousArea = accessor.antarchy$getViewArea();
|
||||
this.previousGraph = accessor.antarchy$getSectionOcclusionGraph();
|
||||
this.previousVisibleSections = accessor.antarchy$getVisibleSections();
|
||||
this.previousGlobalBlockEntities = accessor.antarchy$getGlobalBlockEntities();
|
||||
this.previousCloudBuffer = accessor.antarchy$getCloudBuffer();
|
||||
this.previousGenerateClouds = accessor.antarchy$getGenerateClouds();
|
||||
this.previousCloudX = accessor.antarchy$getPrevCloudX();
|
||||
this.previousCloudY = accessor.antarchy$getPrevCloudY();
|
||||
this.previousCloudZ = accessor.antarchy$getPrevCloudZ();
|
||||
this.previousCloudColor = accessor.antarchy$getPrevCloudColor();
|
||||
this.previousCloudsType = accessor.antarchy$getPrevCloudsType();
|
||||
this.previousRainSoundTime = accessor.antarchy$getRainSoundTime();
|
||||
this.previousSectionX = accessor.antarchy$getLastCameraSectionX();
|
||||
this.previousSectionY = accessor.antarchy$getLastCameraSectionY();
|
||||
this.previousSectionZ = accessor.antarchy$getLastCameraSectionZ();
|
||||
this.previousPrevCamX = accessor.antarchy$getPrevCamX();
|
||||
this.previousPrevCamY = accessor.antarchy$getPrevCamY();
|
||||
this.previousPrevCamZ = accessor.antarchy$getPrevCamZ();
|
||||
this.previousPrevCamRotX = accessor.antarchy$getPrevCamRotX();
|
||||
this.previousPrevCamRotY = accessor.antarchy$getPrevCamRotY();
|
||||
this.previousTransparentSortX = accessor.antarchy$getTransparentSortX();
|
||||
this.previousTransparentSortY = accessor.antarchy$getTransparentSortY();
|
||||
this.previousTransparentSortZ = accessor.antarchy$getTransparentSortZ();
|
||||
this.dispatcher = renderer.getSectionRenderDispatcher();
|
||||
this.previousDispatcherCamera = dispatcher.getCameraPosition();
|
||||
accessor.antarchy$setLastCameraSectionX(state.lastSectionX);
|
||||
accessor.antarchy$setLastCameraSectionY(state.lastSectionY);
|
||||
accessor.antarchy$setLastCameraSectionZ(state.lastSectionZ);
|
||||
accessor.antarchy$setPrevCamX(state.prevCamX);
|
||||
accessor.antarchy$setPrevCamY(state.prevCamY);
|
||||
accessor.antarchy$setPrevCamZ(state.prevCamZ);
|
||||
accessor.antarchy$setPrevCamRotX(state.prevCamRotX);
|
||||
accessor.antarchy$setPrevCamRotY(state.prevCamRotY);
|
||||
accessor.antarchy$setTransparentSortX(state.transparentSortX);
|
||||
accessor.antarchy$setTransparentSortY(state.transparentSortY);
|
||||
accessor.antarchy$setTransparentSortZ(state.transparentSortZ);
|
||||
accessor.antarchy$setCloudBuffer(state.cloudBuffer);
|
||||
accessor.antarchy$setGenerateClouds(state.generateClouds);
|
||||
accessor.antarchy$setPrevCloudX(state.prevCloudX);
|
||||
accessor.antarchy$setPrevCloudY(state.prevCloudY);
|
||||
accessor.antarchy$setPrevCloudZ(state.prevCloudZ);
|
||||
accessor.antarchy$setPrevCloudColor(state.prevCloudColor);
|
||||
accessor.antarchy$setPrevCloudsType(state.prevCloudsType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
if (closed) {
|
||||
return;
|
||||
}
|
||||
closed = true;
|
||||
state.lastSectionX = accessor.antarchy$getLastCameraSectionX();
|
||||
state.lastSectionY = accessor.antarchy$getLastCameraSectionY();
|
||||
state.lastSectionZ = accessor.antarchy$getLastCameraSectionZ();
|
||||
state.prevCamX = accessor.antarchy$getPrevCamX();
|
||||
state.prevCamY = accessor.antarchy$getPrevCamY();
|
||||
state.prevCamZ = accessor.antarchy$getPrevCamZ();
|
||||
state.prevCamRotX = accessor.antarchy$getPrevCamRotX();
|
||||
state.prevCamRotY = accessor.antarchy$getPrevCamRotY();
|
||||
state.transparentSortX = accessor.antarchy$getTransparentSortX();
|
||||
state.transparentSortY = accessor.antarchy$getTransparentSortY();
|
||||
state.transparentSortZ = accessor.antarchy$getTransparentSortZ();
|
||||
state.cloudBuffer = accessor.antarchy$getCloudBuffer();
|
||||
state.generateClouds = accessor.antarchy$getGenerateClouds();
|
||||
state.prevCloudX = accessor.antarchy$getPrevCloudX();
|
||||
state.prevCloudY = accessor.antarchy$getPrevCloudY();
|
||||
state.prevCloudZ = accessor.antarchy$getPrevCloudZ();
|
||||
state.prevCloudColor = accessor.antarchy$getPrevCloudColor();
|
||||
state.prevCloudsType = accessor.antarchy$getPrevCloudsType();
|
||||
accessor.antarchy$setViewArea(previousArea);
|
||||
accessor.antarchy$setSectionOcclusionGraph(previousGraph);
|
||||
accessor.antarchy$setVisibleSections(previousVisibleSections);
|
||||
accessor.antarchy$setGlobalBlockEntities(previousGlobalBlockEntities);
|
||||
accessor.antarchy$setCloudBuffer(previousCloudBuffer);
|
||||
accessor.antarchy$setGenerateClouds(previousGenerateClouds);
|
||||
accessor.antarchy$setPrevCloudX(previousCloudX);
|
||||
accessor.antarchy$setPrevCloudY(previousCloudY);
|
||||
accessor.antarchy$setPrevCloudZ(previousCloudZ);
|
||||
accessor.antarchy$setPrevCloudColor(previousCloudColor);
|
||||
accessor.antarchy$setPrevCloudsType(previousCloudsType);
|
||||
accessor.antarchy$setRainSoundTime(previousRainSoundTime);
|
||||
accessor.antarchy$setLastCameraSectionX(previousSectionX);
|
||||
accessor.antarchy$setLastCameraSectionY(previousSectionY);
|
||||
accessor.antarchy$setLastCameraSectionZ(previousSectionZ);
|
||||
accessor.antarchy$setPrevCamX(previousPrevCamX);
|
||||
accessor.antarchy$setPrevCamY(previousPrevCamY);
|
||||
accessor.antarchy$setPrevCamZ(previousPrevCamZ);
|
||||
accessor.antarchy$setPrevCamRotX(previousPrevCamRotX);
|
||||
accessor.antarchy$setPrevCamRotY(previousPrevCamRotY);
|
||||
accessor.antarchy$setTransparentSortX(previousTransparentSortX);
|
||||
accessor.antarchy$setTransparentSortY(previousTransparentSortY);
|
||||
accessor.antarchy$setTransparentSortZ(previousTransparentSortZ);
|
||||
dispatcher.setCamera(previousDispatcherCamera);
|
||||
blockEntityDispatcher.prepare(previousBlockEntityLevel, previousBlockEntityCamera, previousBlockEntityHitResult);
|
||||
}
|
||||
}
|
||||
}
|
||||
+771
-243
File diff suppressed because it is too large.
Load diff
+193
@@ -0,0 +1,193 @@
|
||||
package com.craisinlord.antarchy.content.client.renderer;
|
||||
|
||||
import com.craisinlord.antarchy.mixins.client.LevelRendererPortalSceneInvoker;
|
||||
import com.craisinlord.antarchy.mixins.client.LevelRendererPortalViewAreaAccessor;
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
import com.mojang.blaze3d.platform.Lighting;
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import net.minecraft.client.Camera;
|
||||
import net.minecraft.client.CloudStatus;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.FogRenderer;
|
||||
import net.minecraft.client.renderer.LevelRenderer;
|
||||
import net.minecraft.client.renderer.chunk.SectionRenderDispatcher;
|
||||
import net.minecraft.client.renderer.MultiBufferSource;
|
||||
import net.minecraft.client.renderer.RenderType;
|
||||
import net.minecraft.client.particle.ParticleRenderType;
|
||||
import net.minecraft.client.renderer.culling.Frustum;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import net.minecraft.world.phys.AABB;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
||||
import org.joml.Matrix4f;
|
||||
import org.joml.Matrix4fStack;
|
||||
|
||||
public final class PortalGunProxySceneRenderer {
|
||||
private PortalGunProxySceneRenderer() {
|
||||
}
|
||||
|
||||
public static void render(Minecraft minecraft, LevelRenderer renderer, Camera camera, float partialTick, Matrix4f modelViewMatrix, Matrix4f projectionMatrix, String tracePair) {
|
||||
Vec3 cameraPosition = camera.getPosition();
|
||||
double cameraX = cameraPosition.x;
|
||||
double cameraY = cameraPosition.y;
|
||||
double cameraZ = cameraPosition.z;
|
||||
Frustum frustum = new Frustum(modelViewMatrix, projectionMatrix);
|
||||
frustum.prepare(cameraX, cameraY, cameraZ);
|
||||
LevelRendererPortalSceneInvoker scene = (LevelRendererPortalSceneInvoker) renderer;
|
||||
float renderDistance = minecraft.gameRenderer.getRenderDistance();
|
||||
BlockPos cameraBlockPos = BlockPos.containing(cameraX, cameraY, cameraZ);
|
||||
boolean foggy = minecraft.level.effects().isFoggyAt(cameraBlockPos.getX(), cameraBlockPos.getY())
|
||||
|| minecraft.gui.getBossOverlay().shouldCreateWorldFog();
|
||||
|
||||
try (PortalSceneRenderTrace.Phase ignored = PortalSceneRenderTrace.begin(tracePair, "sky", "camera=" + cameraPosition)) {
|
||||
RenderSystem.setShaderGameTime(minecraft.level.getGameTime(), partialTick);
|
||||
minecraft.getBlockEntityRenderDispatcher().prepare(minecraft.level, camera, minecraft.hitResult);
|
||||
minecraft.getEntityRenderDispatcher().prepare(minecraft.level, camera, minecraft.crosshairPickEntity);
|
||||
FogRenderer.setupColor(camera, partialTick, minecraft.level, minecraft.options.getEffectiveRenderDistance(), minecraft.gameRenderer.getDarkenWorldAmount(partialTick));
|
||||
FogRenderer.levelFogColor();
|
||||
FogRenderer.setupFog(camera, FogRenderer.FogMode.FOG_SKY, renderDistance, foggy, partialTick);
|
||||
RenderSystem.setShader(net.minecraft.client.renderer.GameRenderer::getPositionShader);
|
||||
renderer.renderSky(modelViewMatrix, projectionMatrix, partialTick, camera, foggy,
|
||||
() -> FogRenderer.setupFog(camera, FogRenderer.FogMode.FOG_SKY, renderDistance, foggy, partialTick));
|
||||
}
|
||||
|
||||
try (PortalSceneRenderTrace.Phase ignored = PortalSceneRenderTrace.begin(tracePair, "frustum-and-visibility", "renderDistance=" + rendererAccessDistance(renderer))) {
|
||||
FogRenderer.setupFog(camera, FogRenderer.FogMode.FOG_TERRAIN, Math.max(renderDistance, 32.0F), foggy, partialTick);
|
||||
scene.antarchy$setupPortalScene(camera, frustum, false, minecraft.player != null && minecraft.player.isSpectator());
|
||||
}
|
||||
LevelRendererPortalViewAreaAccessor viewAreaAccessor = (LevelRendererPortalViewAreaAccessor) renderer;
|
||||
ObjectArrayList<SectionRenderDispatcher.RenderSection> fullVisibleSections = viewAreaAccessor.antarchy$getVisibleSections();
|
||||
ObjectArrayList<SectionRenderDispatcher.RenderSection> budgetedVisibleSections = new ObjectArrayList<>();
|
||||
PortalGunPortalRendererPool.beginSceneBudget();
|
||||
int scannedVisibleSections = 0;
|
||||
while (scannedVisibleSections < fullVisibleSections.size() && PortalGunPortalRendererPool.remainingSectionBudgetNanos() > 0L) {
|
||||
budgetedVisibleSections.add(fullVisibleSections.get(scannedVisibleSections++));
|
||||
}
|
||||
try (PortalSceneRenderTrace.Phase ignored = PortalSceneRenderTrace.begin(tracePair, "compile-visible-sections", "visibleTotal=" + fullVisibleSections.size() + " visibleScanned=" + scannedVisibleSections + " rebuildBudgetNanos=" + PortalGunPortalRendererPool.remainingSectionBudgetNanos())) {
|
||||
viewAreaAccessor.antarchy$setVisibleSections(budgetedVisibleSections);
|
||||
try {
|
||||
scene.antarchy$compilePortalSections(camera);
|
||||
} finally {
|
||||
viewAreaAccessor.antarchy$setVisibleSections(fullVisibleSections);
|
||||
}
|
||||
}
|
||||
PortalSceneRenderTrace.event(tracePair, "visible-sections-ready", "count=" + viewAreaAccessor.antarchy$getVisibleSections().size() + " scheduledRebuilds=" + PortalGunPortalRendererPool.scheduledSectionRebuilds());
|
||||
|
||||
try (PortalSceneRenderTrace.Phase ignored = PortalSceneRenderTrace.begin(tracePair, "opaque-terrain-layers", "sections=" + viewAreaAccessor.antarchy$getVisibleSections().size())) {
|
||||
RenderSystem.enableDepthTest();
|
||||
RenderSystem.depthMask(true);
|
||||
scene.antarchy$renderPortalSectionLayer(RenderType.solid(), cameraX, cameraY, cameraZ, modelViewMatrix, projectionMatrix);
|
||||
scene.antarchy$renderPortalSectionLayer(RenderType.cutoutMipped(), cameraX, cameraY, cameraZ, modelViewMatrix, projectionMatrix);
|
||||
scene.antarchy$renderPortalSectionLayer(RenderType.cutout(), cameraX, cameraY, cameraZ, modelViewMatrix, projectionMatrix);
|
||||
}
|
||||
|
||||
if (minecraft.level.effects().constantAmbientLight()) {
|
||||
Lighting.setupNetherLevel();
|
||||
} else {
|
||||
Lighting.setupLevel();
|
||||
}
|
||||
|
||||
Matrix4fStack modelViewStack = RenderSystem.getModelViewStack();
|
||||
try (PortalSceneRenderTrace.Phase ignored = PortalSceneRenderTrace.begin(tracePair, "entity-scene-state", "enter-model-view")) {
|
||||
modelViewStack.pushMatrix();
|
||||
modelViewStack.mul(modelViewMatrix);
|
||||
RenderSystem.applyModelViewMatrix();
|
||||
}
|
||||
PoseStack poseStack = new PoseStack();
|
||||
MultiBufferSource.BufferSource buffers = minecraft.renderBuffers().bufferSource();
|
||||
try {
|
||||
int entityCount = 0;
|
||||
try (PortalSceneRenderTrace.Phase ignored = PortalSceneRenderTrace.begin(tracePair, "entities", "dispatch-start")) {
|
||||
for (Entity entity : minecraft.level.entitiesForRendering()) {
|
||||
if (!minecraft.getEntityRenderDispatcher().shouldRender(entity, frustum, cameraX, cameraY, cameraZ)
|
||||
&& !entity.hasIndirectPassenger(minecraft.player)) {
|
||||
continue;
|
||||
}
|
||||
if (entity == camera.getEntity() && !camera.isDetached()
|
||||
&& (!(entity instanceof LivingEntity living) || !living.isSleeping())) {
|
||||
continue;
|
||||
}
|
||||
if (entity instanceof Player player && player == minecraft.player && !player.isSpectator() && entity != camera.getEntity()) {
|
||||
continue;
|
||||
}
|
||||
scene.antarchy$renderPortalEntity(entity, cameraX, cameraY, cameraZ, partialTick, poseStack, buffers);
|
||||
entityCount++;
|
||||
}
|
||||
}
|
||||
buffers.endBatch();
|
||||
PortalSceneRenderTrace.event(tracePair, "entities-dispatched", "count=" + entityCount);
|
||||
|
||||
int blockEntityCount = 0;
|
||||
try (PortalSceneRenderTrace.Phase ignored = PortalSceneRenderTrace.begin(tracePair, "block-entities", "visibleSections=" + viewAreaAccessor.antarchy$getVisibleSections().size())) {
|
||||
for (var section : viewAreaAccessor.antarchy$getVisibleSections()) {
|
||||
for (BlockEntity blockEntity : section.getCompiled().getRenderableBlockEntities()) {
|
||||
renderBlockEntity(minecraft, blockEntity, cameraX, cameraY, cameraZ, partialTick, poseStack, buffers);
|
||||
blockEntityCount++;
|
||||
}
|
||||
}
|
||||
synchronized (viewAreaAccessor.antarchy$getGlobalBlockEntities()) {
|
||||
for (BlockEntity blockEntity : viewAreaAccessor.antarchy$getGlobalBlockEntities()) {
|
||||
if (frustum.isVisible(new AABB(blockEntity.getBlockPos()).inflate(1.0D))) {
|
||||
renderBlockEntity(minecraft, blockEntity, cameraX, cameraY, cameraZ, partialTick, poseStack, buffers);
|
||||
blockEntityCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
buffers.endBatch();
|
||||
PortalSceneRenderTrace.event(tracePair, "block-entities-dispatched", "count=" + blockEntityCount);
|
||||
|
||||
try (PortalSceneRenderTrace.Phase ignored = PortalSceneRenderTrace.begin(tracePair, "translucent-and-particles", "rendering")) {
|
||||
minecraft.particleEngine.render(minecraft.gameRenderer.lightTexture(), camera, partialTick, frustum, type -> !type.isTranslucent());
|
||||
scene.antarchy$renderPortalSectionLayer(RenderType.translucent(), cameraX, cameraY, cameraZ, modelViewMatrix, projectionMatrix);
|
||||
buffers.endBatch(RenderType.lines());
|
||||
buffers.endBatch();
|
||||
scene.antarchy$renderPortalSectionLayer(RenderType.tripwire(), cameraX, cameraY, cameraZ, modelViewMatrix, projectionMatrix);
|
||||
minecraft.particleEngine.render(minecraft.gameRenderer.lightTexture(), camera, partialTick, frustum, ParticleRenderType::isTranslucent);
|
||||
}
|
||||
|
||||
try (PortalSceneRenderTrace.Phase ignored = PortalSceneRenderTrace.begin(tracePair, "clouds", minecraft.options.getCloudsType().toString())) {
|
||||
if (minecraft.options.getCloudsType() != CloudStatus.OFF) {
|
||||
renderer.renderClouds(poseStack, modelViewMatrix, projectionMatrix, partialTick, cameraX, cameraY, cameraZ);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
} finally {
|
||||
try {
|
||||
buffers.endBatch();
|
||||
} finally {
|
||||
modelViewStack.popMatrix();
|
||||
RenderSystem.applyModelViewMatrix();
|
||||
RenderSystem.depthMask(true);
|
||||
RenderSystem.enableDepthTest();
|
||||
RenderSystem.enableCull();
|
||||
RenderSystem.disableBlend();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static int rendererAccessDistance(LevelRenderer renderer) {
|
||||
return ((LevelRendererPortalViewAreaAccessor) renderer).antarchy$getViewArea().getViewDistance();
|
||||
}
|
||||
|
||||
private static void renderBlockEntity(Minecraft minecraft, BlockEntity blockEntity, double cameraX, double cameraY, double cameraZ,
|
||||
float partialTick, PoseStack poseStack, MultiBufferSource.BufferSource buffers) {
|
||||
if (!blockEntity.hasLevel() || !blockEntity.getType().isValid(blockEntity.getBlockState())) {
|
||||
return;
|
||||
}
|
||||
BlockPos pos = blockEntity.getBlockPos();
|
||||
poseStack.pushPose();
|
||||
poseStack.translate(pos.getX() - cameraX, pos.getY() - cameraY, pos.getZ() - cameraZ);
|
||||
minecraft.getBlockEntityRenderDispatcher().render(blockEntity, partialTick, poseStack, buffers);
|
||||
poseStack.popPose();
|
||||
}
|
||||
}
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
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 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) {
|
||||
long currentGeneration;
|
||||
boolean shouldLog;
|
||||
synchronized (PortalSceneRenderTrace.class) {
|
||||
currentGeneration = generation;
|
||||
shouldLog = LOGGED_PHASES.add(currentGeneration + ":" + pair + ":" + phase);
|
||||
}
|
||||
if (shouldLog) {
|
||||
Antarchy.LOGGER.info("Portal scene trace begin generation={} pair={} phase={} detail={}", currentGeneration, pair, phase, detail);
|
||||
}
|
||||
return new Phase(currentGeneration, pair, phase, shouldLog, System.nanoTime());
|
||||
}
|
||||
|
||||
public static void event(String pair, String event, String detail) {
|
||||
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 logCompletion;
|
||||
private final long startNanos;
|
||||
private boolean closed;
|
||||
|
||||
private Phase(long generation, String pair, String phase, boolean logCompletion, long startNanos) {
|
||||
this.generation = generation;
|
||||
this.pair = pair;
|
||||
this.phase = phase;
|
||||
this.logCompletion = logCompletion;
|
||||
this.startNanos = startNanos;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
if (this.closed) {
|
||||
return;
|
||||
}
|
||||
this.closed = true;
|
||||
if (this.logCompletion) {
|
||||
Antarchy.LOGGER.info("Portal scene trace complete generation={} pair={} phase={} durationMs={}",
|
||||
this.generation, this.pair, this.phase, (System.nanoTime() - this.startNanos) / 1_000_000.0D);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+196
@@ -0,0 +1,196 @@
|
||||
package com.craisinlord.antarchy.content.client.renderer;
|
||||
|
||||
import com.mojang.blaze3d.pipeline.RenderTarget;
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
import java.util.Collections;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
import com.mojang.blaze3d.pipeline.TextureTarget;
|
||||
|
||||
public final class PortalStencilTarget {
|
||||
private static final Map<RenderTarget, Attachment> ATTACHMENTS = Collections.synchronizedMap(new IdentityHashMap<>());
|
||||
private static final Set<RenderTarget> DIRECT_TARGETS = Collections.newSetFromMap(new IdentityHashMap<>());
|
||||
|
||||
private PortalStencilTarget() {
|
||||
}
|
||||
|
||||
public static void prepareMainTarget(RenderTarget target) {
|
||||
try {
|
||||
java.lang.reflect.Method enabled = target.getClass().getMethod("isStencilEnabled");
|
||||
if (!Boolean.TRUE.equals(enabled.invoke(target))) {
|
||||
target.getClass().getMethod("enableStencil").invoke(target);
|
||||
}
|
||||
} catch (ReflectiveOperationException ignored) {
|
||||
}
|
||||
target.bindWrite(true);
|
||||
}
|
||||
|
||||
public static Scope beginDirect(RenderTarget target, TextureTarget depthBackup, Runnable drawApertureMask) {
|
||||
target.bindWrite(true);
|
||||
if (GL11.glGetInteger(GL11.GL_STENCIL_BITS) <= 0) {
|
||||
throw new IllegalStateException("Main render target has no stencil attachment");
|
||||
}
|
||||
depthBackup.copyDepthFrom(target);
|
||||
DIRECT_TARGETS.add(target);
|
||||
target.bindWrite(true);
|
||||
RenderSystem.disableScissor();
|
||||
RenderSystem.stencilMask(0xFF);
|
||||
RenderSystem.clearStencil(0);
|
||||
GL11.glClear(GL11.GL_STENCIL_BUFFER_BIT);
|
||||
RenderSystem.stencilFunc(GL11.GL_ALWAYS, 1, 0xFF);
|
||||
RenderSystem.stencilOp(GL11.GL_KEEP, GL11.GL_KEEP, GL11.GL_REPLACE);
|
||||
RenderSystem.colorMask(false, false, false, false);
|
||||
RenderSystem.depthMask(false);
|
||||
RenderSystem.disableDepthTest();
|
||||
GL11.glEnable(GL11.GL_STENCIL_TEST);
|
||||
try {
|
||||
Objects.requireNonNull(drawApertureMask).run();
|
||||
} catch (RuntimeException | Error exception) {
|
||||
resetState();
|
||||
target.copyDepthFrom(depthBackup);
|
||||
DIRECT_TARGETS.remove(target);
|
||||
throw exception;
|
||||
}
|
||||
RenderSystem.colorMask(true, true, true, true);
|
||||
RenderSystem.depthMask(true);
|
||||
RenderSystem.stencilMask(0x00);
|
||||
RenderSystem.stencilFunc(GL11.GL_EQUAL, 1, 0xFF);
|
||||
RenderSystem.stencilOp(GL11.GL_KEEP, GL11.GL_KEEP, GL11.GL_KEEP);
|
||||
GL11.glClear(GL11.GL_DEPTH_BUFFER_BIT);
|
||||
RenderSystem.enableDepthTest();
|
||||
return new Scope(target, depthBackup);
|
||||
}
|
||||
|
||||
public static Scope begin(RenderTarget target, Runnable drawApertureMask) {
|
||||
ensureAttachment(target);
|
||||
target.bindWrite(true);
|
||||
RenderSystem.disableScissor();
|
||||
RenderSystem.stencilMask(0xFF);
|
||||
RenderSystem.clearStencil(0);
|
||||
GL11.glClear(GL11.GL_STENCIL_BUFFER_BIT);
|
||||
RenderSystem.stencilFunc(GL11.GL_ALWAYS, 1, 0xFF);
|
||||
RenderSystem.stencilOp(GL11.GL_KEEP, GL11.GL_KEEP, GL11.GL_REPLACE);
|
||||
RenderSystem.colorMask(false, false, false, false);
|
||||
RenderSystem.depthMask(false);
|
||||
RenderSystem.disableDepthTest();
|
||||
GL11.glEnable(GL11.GL_STENCIL_TEST);
|
||||
try {
|
||||
Objects.requireNonNull(drawApertureMask).run();
|
||||
} catch (RuntimeException | Error exception) {
|
||||
resetState();
|
||||
throw exception;
|
||||
}
|
||||
RenderSystem.colorMask(true, true, true, true);
|
||||
RenderSystem.depthMask(true);
|
||||
RenderSystem.stencilMask(0x00);
|
||||
RenderSystem.stencilFunc(GL11.GL_EQUAL, 1, 0xFF);
|
||||
RenderSystem.stencilOp(GL11.GL_KEEP, GL11.GL_KEEP, GL11.GL_KEEP);
|
||||
GL11.glClear(GL11.GL_DEPTH_BUFFER_BIT);
|
||||
RenderSystem.enableDepthTest();
|
||||
return new Scope(target);
|
||||
}
|
||||
|
||||
public static void activate(RenderTarget target) {
|
||||
if (!ATTACHMENTS.containsKey(target) && !DIRECT_TARGETS.contains(target)) {
|
||||
return;
|
||||
}
|
||||
target.bindWrite(true);
|
||||
GL11.glEnable(GL11.GL_STENCIL_TEST);
|
||||
RenderSystem.stencilMask(0x00);
|
||||
RenderSystem.stencilFunc(GL11.GL_EQUAL, 1, 0xFF);
|
||||
RenderSystem.stencilOp(GL11.GL_KEEP, GL11.GL_KEEP, GL11.GL_KEEP);
|
||||
}
|
||||
|
||||
public static void release(RenderTarget target) {
|
||||
DIRECT_TARGETS.remove(target);
|
||||
Attachment attachment = ATTACHMENTS.remove(target);
|
||||
if (attachment != null) {
|
||||
GL30.glDeleteRenderbuffers(attachment.renderbuffer());
|
||||
}
|
||||
}
|
||||
|
||||
public static void clear() {
|
||||
for (Attachment attachment : ATTACHMENTS.values()) {
|
||||
GL30.glDeleteRenderbuffers(attachment.renderbuffer());
|
||||
}
|
||||
ATTACHMENTS.clear();
|
||||
DIRECT_TARGETS.clear();
|
||||
}
|
||||
|
||||
private static void ensureAttachment(RenderTarget target) {
|
||||
Attachment attachment = ATTACHMENTS.get(target);
|
||||
if (attachment != null
|
||||
&& attachment.framebuffer() == target.frameBufferId
|
||||
&& attachment.width() == target.width
|
||||
&& attachment.height() == target.height) {
|
||||
return;
|
||||
}
|
||||
if (attachment != null) {
|
||||
GL30.glDeleteRenderbuffers(attachment.renderbuffer());
|
||||
}
|
||||
int previousFramebuffer = GL11.glGetInteger(GL30.GL_FRAMEBUFFER_BINDING);
|
||||
int previousRenderbuffer = GL11.glGetInteger(GL30.GL_RENDERBUFFER_BINDING);
|
||||
int renderbuffer = GL30.glGenRenderbuffers();
|
||||
try {
|
||||
GL30.glBindFramebuffer(GL30.GL_FRAMEBUFFER, target.frameBufferId);
|
||||
GL30.glBindRenderbuffer(GL30.GL_RENDERBUFFER, renderbuffer);
|
||||
GL30.glRenderbufferStorage(GL30.GL_RENDERBUFFER, GL30.GL_DEPTH24_STENCIL8, target.width, target.height);
|
||||
GL30.glFramebufferRenderbuffer(GL30.GL_FRAMEBUFFER, GL30.GL_DEPTH_STENCIL_ATTACHMENT, GL30.GL_RENDERBUFFER, renderbuffer);
|
||||
int status = GL30.glCheckFramebufferStatus(GL30.GL_FRAMEBUFFER);
|
||||
if (status != GL30.GL_FRAMEBUFFER_COMPLETE) {
|
||||
GL30.glDeleteRenderbuffers(renderbuffer);
|
||||
throw new IllegalStateException("Portal view framebuffer is incomplete with packed depth/stencil attachment: 0x" + Integer.toHexString(status));
|
||||
}
|
||||
ATTACHMENTS.put(target, new Attachment(target.frameBufferId, renderbuffer, target.width, target.height));
|
||||
} finally {
|
||||
GL30.glBindRenderbuffer(GL30.GL_RENDERBUFFER, previousRenderbuffer);
|
||||
GL30.glBindFramebuffer(GL30.GL_FRAMEBUFFER, previousFramebuffer);
|
||||
}
|
||||
}
|
||||
|
||||
private static void resetState() {
|
||||
GL11.glDisable(GL11.GL_STENCIL_TEST);
|
||||
RenderSystem.stencilMask(0xFF);
|
||||
RenderSystem.stencilFunc(GL11.GL_ALWAYS, 0, 0xFF);
|
||||
RenderSystem.stencilOp(GL11.GL_KEEP, GL11.GL_KEEP, GL11.GL_KEEP);
|
||||
RenderSystem.colorMask(true, true, true, true);
|
||||
RenderSystem.depthMask(true);
|
||||
RenderSystem.enableDepthTest();
|
||||
}
|
||||
|
||||
private record Attachment(int framebuffer, int renderbuffer, int width, int height) {
|
||||
}
|
||||
|
||||
public static final class Scope implements AutoCloseable {
|
||||
private boolean closed;
|
||||
private final RenderTarget target;
|
||||
private final TextureTarget depthBackup;
|
||||
|
||||
private Scope(RenderTarget target) {
|
||||
this(target, null);
|
||||
}
|
||||
|
||||
private Scope(RenderTarget target, TextureTarget depthBackup) {
|
||||
this.target = Objects.requireNonNull(target);
|
||||
this.depthBackup = depthBackup;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
if (closed) {
|
||||
return;
|
||||
}
|
||||
closed = true;
|
||||
if (this.depthBackup != null) {
|
||||
this.target.copyDepthFrom(this.depthBackup);
|
||||
this.target.bindWrite(true);
|
||||
DIRECT_TARGETS.remove(this.target);
|
||||
}
|
||||
resetState();
|
||||
}
|
||||
}
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
package com.craisinlord.antarchy.content.client.renderer;
|
||||
|
||||
public interface PortalViewSectionMarker {
|
||||
void antarchy$markPortalViewSection();
|
||||
|
||||
boolean antarchy$isPortalViewSection();
|
||||
}
|
||||
+117
@@ -0,0 +1,117 @@
|
||||
package com.craisinlord.antarchy.content.client.renderer;
|
||||
|
||||
import com.craisinlord.antarchy.Antarchy;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Deque;
|
||||
import net.minecraft.client.Camera;
|
||||
|
||||
public final class SodiumCompat {
|
||||
private static final String SODIUM_RENDERER_CLASS = "net.caffeinemc.mods.sodium.client.render.SodiumWorldRenderer";
|
||||
private static final ThreadLocal<RenderState> RENDER_STATE = ThreadLocal.withInitial(RenderState::new);
|
||||
private static boolean restoreWarningLogged;
|
||||
|
||||
private SodiumCompat() {
|
||||
}
|
||||
|
||||
public static boolean isLoaded() {
|
||||
String resource = SODIUM_RENDERER_CLASS.replace('.', '/') + ".class";
|
||||
ClassLoader contextLoader = Thread.currentThread().getContextClassLoader();
|
||||
if (contextLoader != null && contextLoader.getResource(resource) != null) {
|
||||
return true;
|
||||
}
|
||||
ClassLoader ownLoader = SodiumCompat.class.getClassLoader();
|
||||
return ownLoader != null && ownLoader.getResource(resource) != null;
|
||||
}
|
||||
|
||||
public static void captureTerrainSetup(Camera camera, Object viewport, boolean spectator, boolean updateChunks) {
|
||||
RENDER_STATE.get().activeSetup = new TerrainSetup(camera, viewport, spectator, updateChunks);
|
||||
}
|
||||
|
||||
public static TerrainScope beginPortalView(Camera portalCamera) {
|
||||
RenderState state = RENDER_STATE.get();
|
||||
TerrainSetup previousSetup = state.activeSetup;
|
||||
state.previousSetups.push(new PreviousSetup(previousSetup));
|
||||
if (previousSetup != null) {
|
||||
restoreTerrainSetup(previousSetup.withCamera(portalCamera), state);
|
||||
}
|
||||
return () -> {
|
||||
PreviousSetup previous = state.previousSetups.pop();
|
||||
if (previous.setup() == null) {
|
||||
state.activeSetup = null;
|
||||
return;
|
||||
}
|
||||
restoreTerrainSetup(previous.setup(), state);
|
||||
};
|
||||
}
|
||||
|
||||
public static void clear() {
|
||||
RENDER_STATE.remove();
|
||||
}
|
||||
|
||||
private static void restoreTerrainSetup(TerrainSetup setup, RenderState state) {
|
||||
try {
|
||||
ClassLoader contextLoader = Thread.currentThread().getContextClassLoader();
|
||||
Class<?> rendererClass = Class.forName(SODIUM_RENDERER_CLASS, false,
|
||||
contextLoader == null ? SodiumCompat.class.getClassLoader() : contextLoader);
|
||||
Object renderer = rendererClass.getMethod("instanceNullable").invoke(null);
|
||||
if (renderer == null) {
|
||||
return;
|
||||
}
|
||||
Method terrainSetup = null;
|
||||
for (Method method : rendererClass.getMethods()) {
|
||||
Class<?>[] parameters = method.getParameterTypes();
|
||||
if (method.getName().equals("setupTerrain")
|
||||
&& parameters.length == 4
|
||||
&& parameters[0].isInstance(setup.camera())
|
||||
&& parameters[1].isInstance(setup.viewport())
|
||||
&& parameters[2] == boolean.class
|
||||
&& parameters[3] == boolean.class) {
|
||||
terrainSetup = method;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (terrainSetup == null) {
|
||||
logRestoreFailure("Sodium terrain setup method was not found", null);
|
||||
state.activeSetup = setup;
|
||||
return;
|
||||
}
|
||||
terrainSetup.invoke(renderer, setup.camera(), setup.viewport(), setup.spectator(), setup.updateChunks());
|
||||
} catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InvocationTargetException | LinkageError exception) {
|
||||
logRestoreFailure("Could not restore Sodium terrain after a portal view", exception);
|
||||
state.activeSetup = setup;
|
||||
}
|
||||
}
|
||||
|
||||
private static void logRestoreFailure(String message, Throwable exception) {
|
||||
if (!restoreWarningLogged) {
|
||||
restoreWarningLogged = true;
|
||||
if (exception == null) {
|
||||
Antarchy.LOGGER.warn(message);
|
||||
} else {
|
||||
Antarchy.LOGGER.warn(message, exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface TerrainScope extends AutoCloseable {
|
||||
@Override
|
||||
void close();
|
||||
}
|
||||
|
||||
private static final class RenderState {
|
||||
private final Deque<PreviousSetup> previousSetups = new ArrayDeque<>();
|
||||
private TerrainSetup activeSetup;
|
||||
}
|
||||
|
||||
private record PreviousSetup(TerrainSetup setup) {
|
||||
}
|
||||
|
||||
private record TerrainSetup(Camera camera, Object viewport, boolean spectator, boolean updateChunks) {
|
||||
private TerrainSetup withCamera(Camera camera) {
|
||||
return new TerrainSetup(camera, this.viewport, this.spectator, this.updateChunks);
|
||||
}
|
||||
}
|
||||
}
|
||||
+10
@@ -2,11 +2,13 @@ package com.craisinlord.antarchy.content.computer;
|
||||
|
||||
import com.craisinlord.antarchy.Antarchy;
|
||||
import com.craisinlord.antarchy.content.command.QueenLocateCommand;
|
||||
import com.craisinlord.antarchy.content.entity.brutalfly.BrutalflyEntity;
|
||||
import com.craisinlord.antarchy.content.worldgen.elythia.KingsTreeGrid;
|
||||
import com.craisinlord.antos.api.archive.ArchiveLocatorRegistry;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.level.levelgen.Heightmap;
|
||||
import net.minecraft.world.phys.AABB;
|
||||
|
||||
/** Archive coordinate lookups for royals that live on grids rather than in vanilla structures. */
|
||||
public final class AntarchyArchiveLocators {
|
||||
@@ -24,6 +26,14 @@ public final class AntarchyArchiveLocators {
|
||||
Heightmap.Types.WORLD_SURFACE_WG, level, level.getChunkSource().randomState());
|
||||
return new BlockPos(center.getX(), surfaceY, center.getZ());
|
||||
});
|
||||
ArchiveLocatorRegistry.register(id("brutalfly"), (level, origin) -> {
|
||||
AABB searchArea = AABB.ofSize(origin.getCenter(), 1_024.0D, 1_024.0D, 1_024.0D);
|
||||
return level.getEntitiesOfClass(BrutalflyEntity.class, searchArea, BrutalflyEntity::isAlive)
|
||||
.stream()
|
||||
.min(java.util.Comparator.comparingDouble(entity -> entity.distanceToSqr(origin.getCenter())))
|
||||
.map(entity -> entity.blockPosition())
|
||||
.orElse(null);
|
||||
});
|
||||
}
|
||||
|
||||
private static ResourceLocation id(String path) {
|
||||
|
||||
+18
-1
@@ -4,6 +4,7 @@ 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;
|
||||
@@ -425,7 +426,7 @@ public final class CreativeTabContents {
|
||||
entry("shrink_ray", "combat"),
|
||||
entry("growth_ray", "combat"),
|
||||
entry("gravity_gun", "combat"),
|
||||
// entry("portal_gun", "combat"), // deferred — out of scope for 2.0
|
||||
entry("portal_gun", "combat"),
|
||||
entry("eye_of_the_storm", "combat"),
|
||||
entry("water_cannon", "combat"),
|
||||
entry("attitude_adjuster", "combat"),
|
||||
@@ -640,6 +641,14 @@ 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) {
|
||||
@@ -669,6 +678,14 @@ 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));
|
||||
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
package com.craisinlord.antarchy.content.gravity;
|
||||
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/** Exposes Scorched Guns' base ProjectileEntity fields to mixins targeting its subclasses. */
|
||||
public interface ScgunsProjectileAccess {
|
||||
@Nullable LivingEntity antarchy$getScgunsShooter();
|
||||
double antarchy$getScgunsModifiedGravity();
|
||||
}
|
||||
+12
-2
@@ -59,6 +59,8 @@ public class BigBerthaItem extends SwordItem implements GeoItem {
|
||||
private static final ResourceLocation TEXTURE_LOCATION = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/item/big_bertha/big_bertha.png");
|
||||
private static final ResourceLocation ANIMATION_LOCATION = ResourceLocation.fromNamespaceAndPath("antarchy", "animations/static_item.animation.json");
|
||||
private static final String MODE_TAG = "antarchy.big_bertha_mode";
|
||||
public static final int MODE_TREMORSAURUS = 8;
|
||||
public static final int MODE_FORSAKEN = 9;
|
||||
private static final String SPIN_TICKS_TAG = "antarchy.big_bertha_spin_ticks";
|
||||
private static final String SPIN_DIRECTION_X_TAG = "antarchy.big_bertha_spin_direction_x";
|
||||
private static final String SPIN_DIRECTION_Z_TAG = "antarchy.big_bertha_spin_direction_z";
|
||||
@@ -161,7 +163,7 @@ public class BigBerthaItem extends SwordItem implements GeoItem {
|
||||
case NIGHTMARE -> applyNightmareHit(player, target, serverLevel);
|
||||
case KRAKEN -> applyKrakenHit(player, target, serverLevel);
|
||||
case HERCULES -> applyHerculesHit(player, target, serverLevel);
|
||||
case MOLEVORE, TORETERROR -> {
|
||||
case MOLEVORE, TORETERROR, TREMORSAURUS, FORSAKEN -> {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -389,6 +391,10 @@ public class BigBerthaItem extends SwordItem implements GeoItem {
|
||||
}
|
||||
}
|
||||
|
||||
public static int getModeId(ItemStack stack) {
|
||||
return getMode(stack).id;
|
||||
}
|
||||
|
||||
public boolean tryCycleModeFromInput(Level level, Player player, ItemStack stack) {
|
||||
if (player.getMainHandItem() != stack) {
|
||||
return false;
|
||||
@@ -650,6 +656,8 @@ public class BigBerthaItem extends SwordItem implements GeoItem {
|
||||
MOLEVORE(3, "tooltip.antarchy.big_bertha.mode.molevore", "message.antarchy.big_bertha.mode.molevore"),
|
||||
LUCID(4, "tooltip.antarchy.big_bertha.mode.lucid", "message.antarchy.big_bertha.mode.lucid"),
|
||||
TORETERROR(5, "tooltip.antarchy.big_bertha.mode.toreterror", "message.antarchy.big_bertha.mode.toreterror"),
|
||||
TREMORSAURUS(MODE_TREMORSAURUS, "tooltip.antarchy.big_bertha.mode.tremorsaurus", "message.antarchy.big_bertha.mode.tremorsaurus"),
|
||||
FORSAKEN(MODE_FORSAKEN, "tooltip.antarchy.big_bertha.mode.forsaken", "message.antarchy.big_bertha.mode.forsaken"),
|
||||
HERCULES(6, "tooltip.antarchy.big_bertha.mode.hercules", "message.antarchy.big_bertha.mode.hercules");
|
||||
|
||||
private final int id;
|
||||
@@ -671,7 +679,9 @@ public class BigBerthaItem extends SwordItem implements GeoItem {
|
||||
case KRAKEN -> HERCULES;
|
||||
case HERCULES -> MOLEVORE;
|
||||
case MOLEVORE -> TORETERROR;
|
||||
case TORETERROR -> NONE;
|
||||
case TORETERROR -> TREMORSAURUS;
|
||||
case TREMORSAURUS -> FORSAKEN;
|
||||
case FORSAKEN -> NONE;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
-173
@@ -1,173 +0,0 @@
|
||||
package com.craisinlord.antarchy.content.item;
|
||||
|
||||
import com.craisinlord.antarchy.content.AntarchyTags;
|
||||
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.util.Mth;
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
|
||||
public final class MinersDreamCaveGenerator {
|
||||
private static final double MIN_HORIZONTAL_RADIUS = 2.0D;
|
||||
private static final double MAX_HORIZONTAL_RADIUS = 5.0D;
|
||||
private static final double MIN_VERTICAL_RADIUS = 1.5D;
|
||||
private static final double MAX_VERTICAL_RADIUS = 4.0D;
|
||||
private static final double MAX_VERTICAL_DEVIATION = 8.0D;
|
||||
private static final double SOFT_VERTICAL_DEVIATION = 4.0D;
|
||||
private static final int WORLD_MARGIN = 3;
|
||||
|
||||
private MinersDreamCaveGenerator() {
|
||||
}
|
||||
|
||||
public record CaveNode(BlockPos center, double horizontalRadius, double verticalRadius) {
|
||||
}
|
||||
|
||||
public record CaveGenerationResult(List<CaveNode> nodes, Set<Integer> torchCheckpoints) {
|
||||
}
|
||||
|
||||
public static CaveGenerationResult generate(BlockPos start, Direction initialDirection, int length,
|
||||
int torchSpacing, int minBuildHeight, int maxBuildHeight, RandomSource random) {
|
||||
List<CaveNode> nodes = new ArrayList<>(length);
|
||||
Set<Integer> torchCheckpoints = new HashSet<>();
|
||||
|
||||
double x = start.getX() + 0.5D;
|
||||
double y = start.getY() + 0.5D;
|
||||
double z = start.getZ() + 0.5D;
|
||||
double startY = y;
|
||||
|
||||
double headingX = initialDirection.getStepX();
|
||||
double headingZ = initialDirection.getStepZ();
|
||||
if (headingX == 0.0D && headingZ == 0.0D) {
|
||||
headingX = 1.0D;
|
||||
}
|
||||
|
||||
double verticalVelocity = 0.0D;
|
||||
double horizontalRadius = 3.0D;
|
||||
double verticalRadius = 2.0D;
|
||||
int minY = minBuildHeight + WORLD_MARGIN;
|
||||
int maxY = maxBuildHeight - WORLD_MARGIN;
|
||||
|
||||
int spacing = Math.max(4, torchSpacing);
|
||||
int traveledSinceTorch = 0;
|
||||
int nextTorchDistance = spacing + random.nextInt(5) - 2;
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
BlockPos center = BlockPos.containing(x, Mth.clamp(y, minY, maxY), z);
|
||||
nodes.add(new CaveNode(center, horizontalRadius, verticalRadius));
|
||||
|
||||
traveledSinceTorch++;
|
||||
if (traveledSinceTorch >= Math.max(1, nextTorchDistance)) {
|
||||
torchCheckpoints.add(i);
|
||||
traveledSinceTorch = 0;
|
||||
nextTorchDistance = spacing + random.nextInt(5) - 2;
|
||||
}
|
||||
|
||||
double turnDegrees = random.nextGaussian() * 8.0D;
|
||||
if (random.nextFloat() < 0.1F) {
|
||||
turnDegrees = random.nextGaussian() * 15.0D;
|
||||
}
|
||||
turnDegrees = Mth.clamp(turnDegrees, -25.0D, 25.0D);
|
||||
double turnRadians = Math.toRadians(turnDegrees);
|
||||
double cos = Math.cos(turnRadians);
|
||||
double sin = Math.sin(turnRadians);
|
||||
double newHeadingX = headingX * cos - headingZ * sin;
|
||||
double newHeadingZ = headingX * sin + headingZ * cos;
|
||||
double headingLength = Math.sqrt(newHeadingX * newHeadingX + newHeadingZ * newHeadingZ);
|
||||
headingX = newHeadingX / headingLength;
|
||||
headingZ = newHeadingZ / headingLength;
|
||||
|
||||
verticalVelocity += random.nextGaussian() * 0.15D;
|
||||
double deviation = y - startY;
|
||||
verticalVelocity -= deviation * 0.06D;
|
||||
if (deviation > SOFT_VERTICAL_DEVIATION) {
|
||||
verticalVelocity -= 0.08D;
|
||||
} else if (deviation < -SOFT_VERTICAL_DEVIATION) {
|
||||
verticalVelocity += 0.08D;
|
||||
}
|
||||
verticalVelocity = Mth.clamp(verticalVelocity, -0.6D, 0.6D);
|
||||
|
||||
x += headingX;
|
||||
z += headingZ;
|
||||
y = Mth.clamp(y + verticalVelocity, startY - MAX_VERTICAL_DEVIATION, startY + MAX_VERTICAL_DEVIATION);
|
||||
|
||||
horizontalRadius = Mth.clamp(horizontalRadius + random.nextGaussian() * 0.15D, MIN_HORIZONTAL_RADIUS, MAX_HORIZONTAL_RADIUS);
|
||||
verticalRadius = Mth.clamp(verticalRadius + random.nextGaussian() * 0.12D, MIN_VERTICAL_RADIUS, MAX_VERTICAL_RADIUS);
|
||||
if (random.nextFloat() < 0.05F) {
|
||||
horizontalRadius = Mth.clamp(horizontalRadius + 0.6D, MIN_HORIZONTAL_RADIUS, MAX_HORIZONTAL_RADIUS);
|
||||
verticalRadius = Mth.clamp(verticalRadius + 0.5D, MIN_VERTICAL_RADIUS, MAX_VERTICAL_RADIUS);
|
||||
}
|
||||
}
|
||||
|
||||
return new CaveGenerationResult(nodes, torchCheckpoints);
|
||||
}
|
||||
|
||||
public static void collectNodeCandidates(ServerLevel level, CaveNode node, LongOpenHashSet visited, RandomSource random, List<BlockPos> out) {
|
||||
BlockPos center = node.center();
|
||||
int hr = Mth.ceil(node.horizontalRadius());
|
||||
double upExtent = node.verticalRadius() * 1.25D;
|
||||
double downExtent = node.verticalRadius() * 0.65D;
|
||||
int upR = Mth.ceil(upExtent);
|
||||
int downR = Mth.ceil(downExtent);
|
||||
int minBuildHeight = level.getMinBuildHeight();
|
||||
int maxBuildHeight = level.getMaxBuildHeight();
|
||||
|
||||
for (int dy = -downR; dy <= upR; dy++) {
|
||||
double vExtent = dy >= 0 ? upExtent : downExtent;
|
||||
if (vExtent <= 0.0D) {
|
||||
continue;
|
||||
}
|
||||
double vTerm = (dy * dy) / (vExtent * vExtent);
|
||||
if (vTerm > 1.05D) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int dx = -hr; dx <= hr; dx++) {
|
||||
double hxTerm = (dx * dx) / (node.horizontalRadius() * node.horizontalRadius());
|
||||
for (int dz = -hr; dz <= hr; dz++) {
|
||||
double hzTerm = (dz * dz) / (node.horizontalRadius() * node.horizontalRadius());
|
||||
double distance = hxTerm + vTerm + hzTerm;
|
||||
if (distance > 1.0D) {
|
||||
continue;
|
||||
}
|
||||
if (distance > 0.7D) {
|
||||
double edgeChance = (1.0D - distance) / 0.3D * 0.85D;
|
||||
if (random.nextFloat() > edgeChance) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
BlockPos pos = center.offset(dx, dy, dz);
|
||||
if (pos.getY() < minBuildHeight || pos.getY() >= maxBuildHeight) {
|
||||
continue;
|
||||
}
|
||||
|
||||
long packed = pos.asLong();
|
||||
if (visited.contains(packed)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
BlockState state = level.getBlockState(pos);
|
||||
if (!state.is(AntarchyTags.Blocks.MINERS_DREAM_CARVABLE)) {
|
||||
continue;
|
||||
}
|
||||
if (level.getBlockEntity(pos) != null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
visited.add(packed);
|
||||
out.add(pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isChunkLoaded(ServerLevel level, BlockPos pos) {
|
||||
return level.isLoaded(pos);
|
||||
}
|
||||
}
|
||||
-152
@@ -1,152 +0,0 @@
|
||||
package com.craisinlord.antarchy.content.item;
|
||||
|
||||
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.particles.BlockParticleOption;
|
||||
import net.minecraft.core.particles.DustParticleOptions;
|
||||
import net.minecraft.core.particles.ParticleTypes;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.sounds.SoundEvents;
|
||||
import net.minecraft.sounds.SoundSource;
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import org.joml.Vector3f;
|
||||
|
||||
public final class MinersDreamExcavation {
|
||||
private static final Vector3f FLYTRAP_GOO_COLOR = new Vector3f(0.35F, 0.85F, 0.4F);
|
||||
private static final int SLICES_BETWEEN_RUMBLE = 4;
|
||||
|
||||
private final UUID owner;
|
||||
private final ResourceKey<Level> dimension;
|
||||
private final List<MinersDreamCaveGenerator.CaveNode> nodes;
|
||||
private final Set<Integer> torchCheckpoints;
|
||||
private final LongOpenHashSet visited = new LongOpenHashSet();
|
||||
|
||||
private int nodeIndex = 0;
|
||||
private List<BlockPos> currentNodePositions = null;
|
||||
private int positionCursor = 0;
|
||||
private int slicesSinceRumble = 0;
|
||||
private boolean finished = false;
|
||||
|
||||
public MinersDreamExcavation(UUID owner, ResourceKey<Level> dimension, List<MinersDreamCaveGenerator.CaveNode> nodes, Set<Integer> torchCheckpoints) {
|
||||
this.owner = owner;
|
||||
this.dimension = dimension;
|
||||
this.nodes = nodes;
|
||||
this.torchCheckpoints = torchCheckpoints;
|
||||
}
|
||||
|
||||
public UUID owner() {
|
||||
return owner;
|
||||
}
|
||||
|
||||
public ResourceKey<Level> dimension() {
|
||||
return dimension;
|
||||
}
|
||||
|
||||
public boolean isFinished() {
|
||||
return finished;
|
||||
}
|
||||
|
||||
public void tick(ServerLevel level, int blockBudget) {
|
||||
RandomSource random = level.getRandom();
|
||||
int remaining = blockBudget;
|
||||
|
||||
while (remaining > 0 && !finished) {
|
||||
if (currentNodePositions == null) {
|
||||
if (nodeIndex >= nodes.size()) {
|
||||
finished = true;
|
||||
playCompletionSound(level);
|
||||
return;
|
||||
}
|
||||
|
||||
MinersDreamCaveGenerator.CaveNode node = nodes.get(nodeIndex);
|
||||
if (!MinersDreamCaveGenerator.isChunkLoaded(level, node.center())) {
|
||||
advanceNode(level, node);
|
||||
continue;
|
||||
}
|
||||
|
||||
currentNodePositions = new ArrayList<>();
|
||||
MinersDreamCaveGenerator.collectNodeCandidates(level, node, visited, random, currentNodePositions);
|
||||
positionCursor = 0;
|
||||
}
|
||||
|
||||
if (currentNodePositions.isEmpty()) {
|
||||
MinersDreamCaveGenerator.CaveNode node = nodes.get(nodeIndex);
|
||||
advanceNode(level, node);
|
||||
continue;
|
||||
}
|
||||
|
||||
int endIndex = Math.min(currentNodePositions.size(), positionCursor + remaining);
|
||||
for (; positionCursor < endIndex; positionCursor++) {
|
||||
BlockPos pos = currentNodePositions.get(positionCursor);
|
||||
BlockState removedState = level.getBlockState(pos);
|
||||
if (removedState.isAir()) {
|
||||
continue;
|
||||
}
|
||||
level.setBlock(pos, Blocks.AIR.defaultBlockState(), 3);
|
||||
remaining--;
|
||||
if (random.nextFloat() < 0.12F) {
|
||||
spawnBreakParticles(level, pos, removedState, random);
|
||||
}
|
||||
}
|
||||
|
||||
if (positionCursor >= currentNodePositions.size()) {
|
||||
MinersDreamCaveGenerator.CaveNode node = nodes.get(nodeIndex);
|
||||
advanceNode(level, node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void advanceNode(ServerLevel level, MinersDreamCaveGenerator.CaveNode node) {
|
||||
RandomSource random = level.getRandom();
|
||||
spawnFrontDust(level, node, random);
|
||||
slicesSinceRumble++;
|
||||
if (slicesSinceRumble >= SLICES_BETWEEN_RUMBLE) {
|
||||
slicesSinceRumble = 0;
|
||||
playRumble(level, node);
|
||||
}
|
||||
|
||||
if (torchCheckpoints.contains(nodeIndex)) {
|
||||
MinersDreamTorchPlacer.tryPlaceTorch(level, node.center(), random);
|
||||
}
|
||||
|
||||
currentNodePositions = null;
|
||||
positionCursor = 0;
|
||||
nodeIndex++;
|
||||
}
|
||||
|
||||
private void spawnBreakParticles(ServerLevel level, BlockPos pos, BlockState state, RandomSource random) {
|
||||
level.sendParticles(new BlockParticleOption(ParticleTypes.BLOCK, state),
|
||||
pos.getX() + 0.5D, pos.getY() + 0.5D, pos.getZ() + 0.5D, 2, 0.25D, 0.25D, 0.25D, 0.02D);
|
||||
if (random.nextFloat() < 0.2F) {
|
||||
level.sendParticles(new DustParticleOptions(FLYTRAP_GOO_COLOR, 1.0F),
|
||||
pos.getX() + 0.5D, pos.getY() + 0.5D, pos.getZ() + 0.5D, 1, 0.2D, 0.2D, 0.2D, 0.0D);
|
||||
}
|
||||
}
|
||||
|
||||
private void spawnFrontDust(ServerLevel level, MinersDreamCaveGenerator.CaveNode node, RandomSource random) {
|
||||
BlockPos center = node.center();
|
||||
level.sendParticles(ParticleTypes.CLOUD, center.getX() + 0.5D, center.getY() + 0.5D, center.getZ() + 0.5D,
|
||||
3, node.horizontalRadius() * 0.4D, node.verticalRadius() * 0.3D, node.horizontalRadius() * 0.4D, 0.01D);
|
||||
}
|
||||
|
||||
private void playRumble(ServerLevel level, MinersDreamCaveGenerator.CaveNode node) {
|
||||
BlockPos center = node.center();
|
||||
level.playSound(null, center, SoundEvents.STONE_BREAK, SoundSource.BLOCKS, 0.5F, 0.7F);
|
||||
}
|
||||
|
||||
private void playCompletionSound(ServerLevel level) {
|
||||
if (nodes.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
BlockPos center = nodes.get(nodes.size() - 1).center();
|
||||
level.playSound(null, center, SoundEvents.BEACON_ACTIVATE, SoundSource.PLAYERS, 0.4F, 1.4F);
|
||||
}
|
||||
}
|
||||
-81
@@ -1,81 +0,0 @@
|
||||
package com.craisinlord.antarchy.content.item;
|
||||
|
||||
import com.craisinlord.antarchy.config.AntarchySettings;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.util.Mth;
|
||||
import net.minecraft.util.RandomSource;
|
||||
|
||||
public final class MinersDreamExcavationManager {
|
||||
private static final int MAX_ACTIVE_TASKS = 8;
|
||||
private static final int SAFE_MIN_RANGE = 8;
|
||||
private static final int SAFE_MAX_RANGE = 256;
|
||||
private static final Map<UUID, MinersDreamExcavation> ACTIVE = new ConcurrentHashMap<>();
|
||||
|
||||
private MinersDreamExcavationManager() {
|
||||
}
|
||||
|
||||
public static boolean hasActiveExcavation(UUID owner) {
|
||||
return ACTIVE.containsKey(owner);
|
||||
}
|
||||
|
||||
public static MinersDreamExcavation tryStart(ServerLevel level, UUID owner, BlockPos startPos, Direction direction) {
|
||||
if (ACTIVE.containsKey(owner) || ACTIVE.size() >= MAX_ACTIVE_TASKS) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int minRange = Mth.clamp(AntarchySettings.minersDreamMinimumRange(), SAFE_MIN_RANGE, SAFE_MAX_RANGE);
|
||||
int maxRange = Mth.clamp(AntarchySettings.minersDreamMaximumRange(), SAFE_MIN_RANGE, SAFE_MAX_RANGE);
|
||||
if (minRange > maxRange) {
|
||||
int swap = minRange;
|
||||
minRange = maxRange;
|
||||
maxRange = swap;
|
||||
}
|
||||
|
||||
RandomSource random = level.getRandom();
|
||||
int length = minRange == maxRange ? minRange : minRange + random.nextInt(maxRange - minRange + 1);
|
||||
|
||||
MinersDreamCaveGenerator.CaveGenerationResult result = MinersDreamCaveGenerator.generate(
|
||||
startPos,
|
||||
direction,
|
||||
length,
|
||||
AntarchySettings.minersDreamTorchSpacing(),
|
||||
level.getMinBuildHeight(),
|
||||
level.getMaxBuildHeight(),
|
||||
random
|
||||
);
|
||||
|
||||
if (result.nodes().isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
MinersDreamExcavation excavation = new MinersDreamExcavation(owner, level.dimension(), result.nodes(), result.torchCheckpoints());
|
||||
ACTIVE.put(owner, excavation);
|
||||
return excavation;
|
||||
}
|
||||
|
||||
public static void tick(ServerLevel level) {
|
||||
if (ACTIVE.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
int budget = Math.max(1, AntarchySettings.minersDreamBlocksPerTick());
|
||||
Iterator<Map.Entry<UUID, MinersDreamExcavation>> iterator = ACTIVE.entrySet().iterator();
|
||||
while (iterator.hasNext()) {
|
||||
MinersDreamExcavation excavation = iterator.next().getValue();
|
||||
if (!excavation.dimension().equals(level.dimension())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
excavation.tick(level, budget);
|
||||
if (excavation.isFinished()) {
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-79
@@ -1,79 +0,0 @@
|
||||
package com.craisinlord.antarchy.content.item;
|
||||
|
||||
import com.craisinlord.antarchy.config.AntarchySettings;
|
||||
import java.util.List;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.sounds.SoundEvents;
|
||||
import net.minecraft.sounds.SoundSource;
|
||||
import net.minecraft.world.InteractionResult;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.TooltipFlag;
|
||||
import net.minecraft.world.item.context.UseOnContext;
|
||||
import net.minecraft.world.level.Level;
|
||||
|
||||
public final class MinersDreamItem extends Item {
|
||||
public MinersDreamItem(Properties properties) {
|
||||
super(properties);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InteractionResult useOn(UseOnContext context) {
|
||||
Level level = context.getLevel();
|
||||
Player player = context.getPlayer();
|
||||
if (player == null) {
|
||||
return InteractionResult.PASS;
|
||||
}
|
||||
|
||||
if (!AntarchySettings.minersDreamEnabled()) {
|
||||
if (!level.isClientSide()) {
|
||||
player.displayClientMessage(Component.translatable("message.antarchy.miners_dream_disabled"), true);
|
||||
}
|
||||
return InteractionResult.FAIL;
|
||||
}
|
||||
|
||||
if (level.isClientSide()) {
|
||||
return InteractionResult.SUCCESS;
|
||||
}
|
||||
|
||||
ServerLevel serverLevel = (ServerLevel) level;
|
||||
BlockPos clickedPos = context.getClickedPos();
|
||||
Direction clickedFace = context.getClickedFace();
|
||||
Direction travelDirection = clickedFace.getAxis().isHorizontal() ? clickedFace : player.getDirection();
|
||||
BlockPos startPos = clickedPos.relative(travelDirection);
|
||||
|
||||
if (!serverLevel.isInWorldBounds(startPos) || !MinersDreamCaveGenerator.isChunkLoaded(serverLevel, startPos)) {
|
||||
return InteractionResult.FAIL;
|
||||
}
|
||||
|
||||
if (MinersDreamExcavationManager.hasActiveExcavation(player.getUUID())) {
|
||||
player.displayClientMessage(Component.translatable("message.antarchy.miners_dream_busy"), true);
|
||||
return InteractionResult.FAIL;
|
||||
}
|
||||
|
||||
MinersDreamExcavation excavation = MinersDreamExcavationManager.tryStart(serverLevel, player.getUUID(), startPos, travelDirection);
|
||||
if (excavation == null) {
|
||||
return InteractionResult.FAIL;
|
||||
}
|
||||
|
||||
serverLevel.playSound(null, clickedPos, SoundEvents.TNT_PRIMED, SoundSource.PLAYERS, 0.8F, 1.1F);
|
||||
|
||||
if (!player.getAbilities().instabuild) {
|
||||
context.getItemInHand().shrink(1);
|
||||
}
|
||||
|
||||
return InteractionResult.CONSUME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appendHoverText(ItemStack stack, Item.TooltipContext context, List<Component> tooltipComponents, TooltipFlag tooltipFlag) {
|
||||
tooltipComponents.add(Component.translatable("tooltip.antarchy.miners_dream.line1").withStyle(ChatFormatting.GRAY));
|
||||
tooltipComponents.add(Component.translatable("tooltip.antarchy.miners_dream.line2").withStyle(ChatFormatting.GRAY));
|
||||
super.appendHoverText(stack, context, tooltipComponents, tooltipFlag);
|
||||
}
|
||||
}
|
||||
-89
@@ -1,89 +0,0 @@
|
||||
package com.craisinlord.antarchy.content.item;
|
||||
|
||||
import java.util.List;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.sounds.SoundSource;
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
|
||||
|
||||
public final class MinersDreamTorchPlacer {
|
||||
private MinersDreamTorchPlacer() {
|
||||
}
|
||||
|
||||
public static void tryPlaceTorch(ServerLevel level, BlockPos center, RandomSource random) {
|
||||
BlockPos floorSpot = findFloorSpot(level, center);
|
||||
boolean floorFirst = random.nextBoolean();
|
||||
|
||||
if (floorFirst) {
|
||||
if (floorSpot != null && placeFloorTorch(level, floorSpot)) {
|
||||
return;
|
||||
}
|
||||
placeWallTorch(level, center, random);
|
||||
return;
|
||||
}
|
||||
|
||||
if (placeWallTorch(level, center, random)) {
|
||||
return;
|
||||
}
|
||||
if (floorSpot != null) {
|
||||
placeFloorTorch(level, floorSpot);
|
||||
}
|
||||
}
|
||||
|
||||
private static BlockPos findFloorSpot(ServerLevel level, BlockPos center) {
|
||||
for (int dy = -2; dy <= 1; dy++) {
|
||||
BlockPos candidate = center.offset(0, dy, 0);
|
||||
if (!isOpenForTorch(level, candidate)) {
|
||||
continue;
|
||||
}
|
||||
BlockState below = level.getBlockState(candidate.below());
|
||||
if (below.isFaceSturdy(level, candidate.below(), Direction.UP)) {
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static boolean placeFloorTorch(ServerLevel level, BlockPos pos) {
|
||||
if (!isOpenForTorch(level, pos)) {
|
||||
return false;
|
||||
}
|
||||
BlockState state = Blocks.TORCH.defaultBlockState();
|
||||
if (!state.canSurvive(level, pos)) {
|
||||
return false;
|
||||
}
|
||||
level.setBlock(pos, state, 3);
|
||||
level.playSound(null, pos, state.getSoundType().getPlaceSound(), SoundSource.BLOCKS, 1.0F, 1.0F);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean placeWallTorch(ServerLevel level, BlockPos center, RandomSource random) {
|
||||
List<Direction> order = Direction.Plane.HORIZONTAL.shuffledCopy(random);
|
||||
for (int dy = -1; dy <= 1; dy++) {
|
||||
BlockPos candidate = center.offset(0, dy, 0);
|
||||
if (!isOpenForTorch(level, candidate)) {
|
||||
continue;
|
||||
}
|
||||
for (Direction wallSide : order) {
|
||||
Direction facing = wallSide.getOpposite();
|
||||
BlockState state = Blocks.WALL_TORCH.defaultBlockState().setValue(BlockStateProperties.HORIZONTAL_FACING, facing);
|
||||
if (!state.canSurvive(level, candidate)) {
|
||||
continue;
|
||||
}
|
||||
level.setBlock(candidate, state, 3);
|
||||
level.playSound(null, candidate, state.getSoundType().getPlaceSound(), SoundSource.BLOCKS, 1.0F, 1.0F);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean isOpenForTorch(ServerLevel level, BlockPos pos) {
|
||||
BlockState state = level.getBlockState(pos);
|
||||
return (state.isAir() || state.canBeReplaced()) && level.getFluidState(pos).isEmpty();
|
||||
}
|
||||
}
|
||||
+441
-144
@@ -3,6 +3,8 @@ package com.craisinlord.antarchy.content.item;
|
||||
import com.craisinlord.antarchy.Antarchy;
|
||||
import com.craisinlord.antarchy.content.block.entity.PortalGunPortalBaseBlockEntity;
|
||||
import com.craisinlord.antarchy.content.block.entity.PortalGunPortalMasterBlockEntity;
|
||||
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.network.PortalGunPrimaryPayload;
|
||||
@@ -11,11 +13,14 @@ 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.PortalGunResetManager;
|
||||
import com.craisinlord.antarchy.config.AntarchySettings;
|
||||
import java.util.HashSet;
|
||||
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;
|
||||
@@ -27,8 +32,6 @@ import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.sounds.SoundEvent;
|
||||
import net.minecraft.sounds.SoundEvents;
|
||||
import net.minecraft.sounds.SoundSource;
|
||||
import net.minecraft.stats.Stats;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
@@ -52,6 +55,7 @@ import net.minecraft.world.level.material.FluidState;
|
||||
import net.minecraft.world.phys.BlockHitResult;
|
||||
import net.minecraft.world.phys.HitResult;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import net.minecraft.util.Mth;
|
||||
import software.bernie.geckolib.animatable.GeoItem;
|
||||
import software.bernie.geckolib.animatable.client.GeoRenderProvider;
|
||||
import software.bernie.geckolib.animatable.instance.AnimatableInstanceCache;
|
||||
@@ -69,6 +73,15 @@ 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");
|
||||
@@ -104,81 +117,126 @@ public class PortalGunItem extends Item implements GeoItem {
|
||||
}
|
||||
ServerLevel serverLevel = (ServerLevel) level;
|
||||
if (player.isShiftKeyDown()) {
|
||||
PortalGunSavedData.clearAllPortals(serverLevel.getServer(), player.getUUID());
|
||||
this.setLastSide(stack, null);
|
||||
this.setMoonSide(stack, null);
|
||||
level.playSound(null, player.getX(), player.getY(), player.getZ(), PortalGunPortalEntity.sound("portal_fizzle", SoundEvents.ENDER_CHEST_CLOSE), SoundSource.PLAYERS, 0.6F, 1.0F);
|
||||
if (player instanceof ServerPlayer serverPlayer) {
|
||||
this.resetPortals(serverPlayer, stack);
|
||||
}
|
||||
return InteractionResultHolder.consume(stack);
|
||||
}
|
||||
boolean fired = this.firePortal(serverLevel, player, stack, PortalGunPortalEntity.PortalSide.ORANGE);
|
||||
if (player instanceof ServerPlayer serverPlayer && PortalGunResetManager.handleOrangeReset(serverPlayer, stack)) {
|
||||
return InteractionResultHolder.consume(stack);
|
||||
}
|
||||
boolean fired = this.firePortal(serverLevel, player, stack, PortalGunPortalEntity.PortalSide.ORANGE, usedHand == InteractionHand.OFF_HAND);
|
||||
return fired ? InteractionResultHolder.consume(stack) : InteractionResultHolder.fail(stack);
|
||||
}
|
||||
|
||||
public void firePrimary(ServerLevel level, ServerPlayer player, ItemStack stack) {
|
||||
this.firePortal(level, player, stack, PortalGunPortalEntity.PortalSide.BLUE);
|
||||
public void firePrimary(ServerLevel level, ServerPlayer player, ItemStack stack, boolean offhand) {
|
||||
this.firePortal(level, player, stack, PortalGunPortalEntity.PortalSide.BLUE, offhand);
|
||||
}
|
||||
|
||||
private boolean firePortal(ServerLevel level, Player player, ItemStack stack, PortalGunPortalEntity.PortalSide side) {
|
||||
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));
|
||||
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);
|
||||
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());
|
||||
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);
|
||||
}
|
||||
|
||||
private boolean firePortal(ServerLevel level, Player player, ItemStack stack, PortalGunPortalEntity.PortalSide side, boolean offhand) {
|
||||
if (this.raycast(level, player).getType() == HitResult.Type.MISS) {
|
||||
if (this.tryArmMoonShot(level, player, stack, side)) {
|
||||
if (this.tryArmMoonShot(level, player, stack, side, offhand)) {
|
||||
return true;
|
||||
}
|
||||
level.playSound(null, player.getX(), player.getY(), player.getZ(), PortalGunPortalEntity.sound("portal_gun_invalid_surface", SoundEvents.DISPENSER_FAIL), SoundSource.PLAYERS, 0.55F, 1.0F);
|
||||
return false;
|
||||
}
|
||||
this.triggerFireAnimation(level, player, stack);
|
||||
this.setLastSide(stack, side);
|
||||
stack.hurtAndBreak(1, player, EquipmentSlot.MAINHAND);
|
||||
this.ensureGunId(stack, player.getUUID(), player.getGameProfile().getName());
|
||||
stack.hurtAndBreak(1, player, offhand ? EquipmentSlot.OFFHAND : EquipmentSlot.MAINHAND);
|
||||
player.awardStat(Stats.ITEM_USED.get(this));
|
||||
player.getCooldowns().addCooldown(this, 6);
|
||||
String firePath = side == PortalGunPortalEntity.PortalSide.BLUE ? "portal_gun_fire_blue" : "portal_gun_fire_orange";
|
||||
level.playSound(null, player.getX(), player.getY(), player.getZ(), PortalGunPortalEntity.sound(firePath, SoundEvents.BEACON_ACTIVATE), SoundSource.PLAYERS, 0.65F, side == PortalGunPortalEntity.PortalSide.BLUE ? 1.15F : 0.88F);
|
||||
level.playSound(null, player.getX(), player.getY(), player.getZ(), PortalGunPortalEntity.sound(firePath), SoundSource.PLAYERS, 0.65F, side == PortalGunPortalEntity.PortalSide.BLUE ? 1.15F : 0.88F);
|
||||
this.spawnProjectile(level, player, stack, side);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void handlePortalImpact(ServerLevel level, Player player, ItemStack stack, PortalGunPortalEntity.PortalSide side, BlockHitResult hitResult, Vec3 impactPos) {
|
||||
PortalGunPortalEntity existing = this.findPlacedPortal(level, player.getUUID(), side);
|
||||
PortalGunPlacement placement = this.findPlacement(level, player, side, hitResult, existing);
|
||||
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);
|
||||
if (placement == null) {
|
||||
Antarchy.LOGGER.info("Portal gun placement failed owner={} side={} hitBlock={} hitFace={} hitLocation={}", player.getUUID(), side, hitResult.getBlockPos(), hitResult.getDirection(), hitResult.getLocation());
|
||||
level.playSound(null, impactPos.x, impactPos.y, impactPos.z, PortalGunPortalEntity.sound("portal_gun_invalid_surface", SoundEvents.DISPENSER_FAIL), SoundSource.PLAYERS, 0.55F, 1.0F);
|
||||
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);
|
||||
return;
|
||||
}
|
||||
if (existing != null && this.isSamePortalPlacement(existing, placement)) {
|
||||
existing.restartOpeningAnimation();
|
||||
if (stack.getItem() == this) {
|
||||
this.setLastSide(stack, side);
|
||||
}
|
||||
level.playSound(null, existing.getX(), existing.getY(), existing.getZ(), PortalGunPortalEntity.sound("portal_fizzle"), SoundSource.PLAYERS, 0.6F, 1.0F);
|
||||
return;
|
||||
}
|
||||
if (existing != null) {
|
||||
Antarchy.LOGGER.info("Portal gun replacing existing portal owner={} side={} oldPortal={}", player.getUUID(), side, existing.getUUID());
|
||||
Antarchy.LOGGER.debug("Portal gun replacing existing portal owner={} side={} oldPortal={}", shooter.getUUID(), side, existing.getUUID());
|
||||
existing.discard();
|
||||
}
|
||||
if (this.isMoonSideArmed(stack, side)) {
|
||||
this.setMoonSide(stack, null);
|
||||
}
|
||||
if (this.isMoonSideArmed(stack, this.otherSide(side))) {
|
||||
if (shooter instanceof Player player && this.isMoonSideArmed(stack, this.otherSide(side))) {
|
||||
this.spawnBlackHole(level, player, stack, side, placement.center());
|
||||
return;
|
||||
}
|
||||
PortalGunPortalEntity portal = new PortalGunPortalEntity(this.portalType.get(), level);
|
||||
portal.configure(player.getUUID(), side, placement);
|
||||
portal.configure(portalOwnerId, gunId, portalOwnerIdentity, channelName, side, placement);
|
||||
portal.moveTo(placement.center().x, placement.center().y, placement.center().z, placement.yaw(), 0.0F);
|
||||
if (!this.placePortalBlocks(level, player.getUUID(), side, portal, placement)) {
|
||||
Antarchy.LOGGER.info("Portal gun block placement failed owner={} side={} portal={} center={} facing={} up={} master={} base={}", player.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", SoundEvents.DISPENSER_FAIL), SoundSource.PLAYERS, 0.55F, 1.0F);
|
||||
if (!this.placePortalBlocks(level, portalOwnerId, gunId, portalOwnerIdentity, channelName, 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.getServer(), player.getUUID(), side, portal.getUUID());
|
||||
Antarchy.LOGGER.info("Portal gun portal placed owner={} side={} portal={} center={} facing={} up={} width={} master={} base={}", player.getUUID(), side, portal.getUUID(), placement.center(), placement.facing(), placement.upAxis(), placement.widthAxis(), placement.masterPos(), placement.basePos());
|
||||
PortalGunPortalEntity other = this.findCounterpart(level, player.getUUID(), side);
|
||||
PortalGunSavedData.setPortal(level, portalOwnerId, gunId, channelName, side, portal.getUUID());
|
||||
PortalGunPortalEntity other = this.findCounterpart(level, portalOwnerId, gunId, channelName, side);
|
||||
if (other != null) {
|
||||
portal.linkTo(other);
|
||||
other.linkTo(portal);
|
||||
this.syncPortalBlocks(level, portal);
|
||||
this.syncPortalBlocks(level, other);
|
||||
Antarchy.LOGGER.info("Portal gun portals linked owner={} placedSide={} placedPortal={} otherPortal={} otherSide={}", player.getUUID(), side, portal.getUUID(), other.getUUID(), other.getPortalSide());
|
||||
} else {
|
||||
Antarchy.LOGGER.info("Portal gun portal waiting for counterpart owner={} side={} portal={}", player.getUUID(), side, portal.getUUID());
|
||||
}
|
||||
if (stack.getItem() == this) {
|
||||
this.setLastSide(stack, side);
|
||||
}
|
||||
String openPath = side == PortalGunPortalEntity.PortalSide.BLUE ? "portal_open_blue" : "portal_open_orange";
|
||||
level.playSound(null, portal.getX(), portal.getY(), portal.getZ(), PortalGunPortalEntity.sound(openPath, SoundEvents.END_PORTAL_SPAWN), SoundSource.PLAYERS, 0.45F, side == PortalGunPortalEntity.PortalSide.BLUE ? 1.05F : 0.92F);
|
||||
level.playSound(null, portal.getX(), portal.getY(), portal.getZ(), PortalGunPortalEntity.sound(openPath), SoundSource.PLAYERS, 0.45F, side == PortalGunPortalEntity.PortalSide.BLUE ? 1.05F : 0.92F);
|
||||
}
|
||||
|
||||
private boolean isSamePortalPlacement(PortalGunPortalEntity portal, PortalGunPlacement placement) {
|
||||
if (portal.getPortalWidth() != placement.width()
|
||||
|| portal.getPortalHeight() != placement.height()
|
||||
|| portal.getFacingDirection() != placement.facing()
|
||||
|| portal.getUpAxis() != placement.upAxis()
|
||||
|| !portal.getMasterPos().equals(placement.masterPos())) {
|
||||
return false;
|
||||
}
|
||||
return new HashSet<>(java.util.Arrays.asList(portal.getPortalSpots()))
|
||||
.equals(new HashSet<>(java.util.Arrays.asList(placement.portalSpots())));
|
||||
}
|
||||
|
||||
public ItemStack findMatchingGunStack(Player player, UUID gunId) {
|
||||
@@ -199,21 +257,22 @@ public class PortalGunItem extends Item implements GeoItem {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
private boolean tryArmMoonShot(ServerLevel level, Player player, ItemStack stack, PortalGunPortalEntity.PortalSide side) {
|
||||
private boolean tryArmMoonShot(ServerLevel level, Player player, ItemStack stack, PortalGunPortalEntity.PortalSide side, boolean offhand) {
|
||||
if (!level.dimensionType().hasSkyLight() || !level.isNight() || player.getXRot() > -40.0F) {
|
||||
return false;
|
||||
}
|
||||
PortalGunPortalEntity existing = this.findPlacedPortal(level, player.getUUID(), side);
|
||||
UUID ownerId = getPortalOwnerId(stack, player.getUUID());
|
||||
PortalGunPortalEntity existing = this.findPlacedPortal(level, ownerId, this.ensureGunId(stack, player.getUUID(), player.getGameProfile().getName()), getChannelName(stack), side);
|
||||
if (existing != null) {
|
||||
existing.discard();
|
||||
}
|
||||
this.setMoonSide(stack, side);
|
||||
this.setLastSide(stack, side);
|
||||
this.triggerFireAnimation(level, player, stack);
|
||||
stack.hurtAndBreak(1, player, EquipmentSlot.MAINHAND);
|
||||
stack.hurtAndBreak(1, player, offhand ? EquipmentSlot.OFFHAND : EquipmentSlot.MAINHAND);
|
||||
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", SoundEvents.BEACON_ACTIVATE), SoundSource.PLAYERS, 0.65F, side == PortalGunPortalEntity.PortalSide.BLUE ? 1.15F : 0.88F);
|
||||
level.playSound(null, player.getX(), player.getY(), player.getZ(), PortalGunPortalEntity.sound(side == PortalGunPortalEntity.PortalSide.BLUE ? "portal_gun_fire_blue" : "portal_gun_fire_orange"), SoundSource.PLAYERS, 0.65F, side == PortalGunPortalEntity.PortalSide.BLUE ? 1.15F : 0.88F);
|
||||
this.spawnShotTrail(level, player, player.getEyePosition().add(player.getLookAngle().scale(64.0D)), side);
|
||||
return true;
|
||||
}
|
||||
@@ -224,7 +283,7 @@ public class PortalGunItem extends Item implements GeoItem {
|
||||
blackHole.configure(player.getUUID());
|
||||
blackHole.moveTo(spawnPos.x, spawnPos.y, spawnPos.z, 0.0F, 0.0F);
|
||||
level.addFreshEntity(blackHole);
|
||||
level.playSound(null, spawnPos.x, spawnPos.y, spawnPos.z, PortalGunPortalEntity.sound("portal_gun_black_hole", SoundEvents.WARDEN_SONIC_BOOM), SoundSource.PLAYERS, 0.6F, 0.6F);
|
||||
level.playSound(null, spawnPos.x, spawnPos.y, spawnPos.z, PortalGunPortalEntity.sound("portal_open_orange"), SoundSource.PLAYERS, 0.6F, 0.6F);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -241,20 +300,38 @@ public class PortalGunItem extends Item implements GeoItem {
|
||||
}
|
||||
}
|
||||
|
||||
private void spawnProjectile(ServerLevel level, Player player, ItemStack stack, PortalGunPortalEntity.PortalSide side) {
|
||||
UUID gunId = this.ensureGunId(stack);
|
||||
PortalGunProjectileEntity projectile = new PortalGunProjectileEntity(this.projectileType.get(), player, level);
|
||||
private void spawnProjectile(ServerLevel level, LivingEntity shooter, ItemStack stack, PortalGunPortalEntity.PortalSide side) {
|
||||
UUID gunId = this.ensureGunId(stack, shooter.getUUID(), shooter.getName().getString());
|
||||
PortalGunProjectileEntity projectile = new PortalGunProjectileEntity(this.projectileType.get(), shooter, level);
|
||||
projectile.configure(side, gunId, stack);
|
||||
Vec3 look = player.getLookAngle();
|
||||
Vec3 start = player.getEyePosition().add(look.scale(0.4D));
|
||||
Vec3 look = shooter.getLookAngle();
|
||||
Vec3 start = shooter.getEyePosition();
|
||||
double launchSpeed = PortalGunProjectileEntity.MIN_LAUNCH_SPEED + level.getRandom().nextDouble() * PortalGunProjectileEntity.LAUNCH_SPEED_VARIANCE;
|
||||
projectile.setProjectileSpeed(launchSpeed);
|
||||
projectile.setPos(start.x, start.y, start.z);
|
||||
projectile.setDeltaMovement(look.scale(2.5D));
|
||||
projectile.setDeltaMovement(look.scale(projectile.getProjectileSpeed()));
|
||||
projectile.syncLaunchState();
|
||||
level.addFreshEntity(projectile);
|
||||
}
|
||||
|
||||
private PortalGunPortalEntity findCounterpart(ServerLevel level, UUID owner, PortalGunPortalEntity.PortalSide side) {
|
||||
public static int getPortalWidth(ItemStack stack) {
|
||||
net.minecraft.world.item.component.CustomData data = stack.get(DataComponents.CUSTOM_DATA);
|
||||
return Mth.clamp(data == null ? 1 : data.copyTag().getInt(PORTAL_WIDTH_TAG), 1, MAX_PORTAL_WIDTH);
|
||||
}
|
||||
|
||||
public static int getPortalHeight(ItemStack stack) {
|
||||
net.minecraft.world.item.component.CustomData data = stack.get(DataComponents.CUSTOM_DATA);
|
||||
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) {
|
||||
PortalGunPortalEntity.PortalSide otherSide = side == PortalGunPortalEntity.PortalSide.BLUE ? PortalGunPortalEntity.PortalSide.ORANGE : PortalGunPortalEntity.PortalSide.BLUE;
|
||||
Optional<UUID> otherId = PortalGunSavedData.getPortalId(level.getServer(), owner, otherSide);
|
||||
Optional<UUID> otherId = PortalGunSavedData.getPortalId(level.getServer(), owner, gunId, channelName, otherSide, level.dimension().location());
|
||||
if (otherId.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
@@ -262,8 +339,8 @@ public class PortalGunItem extends Item implements GeoItem {
|
||||
return entity instanceof PortalGunPortalEntity portal ? portal : null;
|
||||
}
|
||||
|
||||
private PortalGunPortalEntity findPlacedPortal(ServerLevel level, UUID owner, PortalGunPortalEntity.PortalSide side) {
|
||||
Optional<UUID> portalId = PortalGunSavedData.getPortalId(level.getServer(), owner, side);
|
||||
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());
|
||||
if (portalId.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
@@ -281,136 +358,247 @@ public class PortalGunItem extends Item implements GeoItem {
|
||||
return level.clip(new ClipContext(start, end, ClipContext.Block.COLLIDER, ClipContext.Fluid.NONE, player));
|
||||
}
|
||||
|
||||
private PortalGunPlacement findPlacement(Level level, Player player, PortalGunPortalEntity.PortalSide side, BlockHitResult hitResult, PortalGunPortalEntity replacingPortal) {
|
||||
private PortalGunPlacement findPlacement(Level level, LivingEntity shooter, UUID ownerId, String channelName, 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();
|
||||
for (int height = maxHeight; height >= 2; height--) {
|
||||
for (int width = maxWidth; width >= 1; width--) {
|
||||
if (!canResize && (width != maxWidth || height != maxHeight)) {
|
||||
continue;
|
||||
}
|
||||
PortalGunPlacement placement = this.findPlacementForSize(level, shooter, ownerId, channelName, side, hitResult, replacingPortal, width, height);
|
||||
if (placement != null) {
|
||||
return placement;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private PortalGunPlacement findPlacementForSize(Level level, LivingEntity shooter, UUID ownerId, String channelName, PortalGunPortalEntity.PortalSide side, BlockHitResult hitResult, PortalGunPortalEntity replacingPortal, int width, int height) {
|
||||
Direction facing = hitResult.getDirection();
|
||||
Direction heightAxis = this.resolveUpAxis(player, facing);
|
||||
Direction heightAxis = this.resolveUpAxis(shooter, facing);
|
||||
Direction widthAxis = PortalGunPlacement.widthAxis(facing, heightAxis);
|
||||
float yaw = PortalGunPlacement.yawFor(facing, heightAxis);
|
||||
BlockPos hitPos = hitResult.getBlockPos();
|
||||
Vec3 hitLocation = hitResult.getLocation();
|
||||
UUID replacingPortalId = replacingPortal == null ? null : replacingPortal.getUUID();
|
||||
PortalGunPlacement bestPlacement = null;
|
||||
double bestScore = Double.MAX_VALUE;
|
||||
for (int widthOffset = -3; widthOffset <= 3; widthOffset++) {
|
||||
for (int heightOffset = -4; heightOffset <= 3; heightOffset++) {
|
||||
BlockPos supportOrigin = hitPos.relative(widthAxis, widthOffset).relative(heightAxis, heightOffset);
|
||||
if (!this.canPlaceAt(level, player.getUUID(), side, supportOrigin, facing, heightAxis, replacingPortalId)) {
|
||||
int maxRadius = Math.max(width, height);
|
||||
List<Integer> radiusOrder = new java.util.ArrayList<>(maxRadius);
|
||||
for (int radius = 0; radius < maxRadius; radius++) {
|
||||
radiusOrder.add(radius);
|
||||
}
|
||||
int preferredRadius = maxRadius / 2;
|
||||
radiusOrder.remove(Integer.valueOf(preferredRadius));
|
||||
radiusOrder.add(0, preferredRadius);
|
||||
for (int radius : radiusOrder) {
|
||||
for (int horizontalOffset = radius; horizontalOffset >= 0; horizontalOffset--) {
|
||||
for (int verticalOffset = radius; verticalOffset >= 0; verticalOffset--) {
|
||||
List<BlockPos> supports = new java.util.ArrayList<>(width * height);
|
||||
for (int widthCell = 1 - width; widthCell <= 0; widthCell++) {
|
||||
for (int heightCell = 1 - height; heightCell <= 0; heightCell++) {
|
||||
supports.add(this.referenceSupportPosition(hitPos, facing, heightAxis, horizontalOffset, verticalOffset, widthCell, heightCell));
|
||||
}
|
||||
}
|
||||
if (!supports.contains(hitPos)) {
|
||||
continue;
|
||||
}
|
||||
BlockPos masterPos = supportOrigin.relative(facing);
|
||||
BlockPos basePos = masterPos.relative(heightAxis);
|
||||
BlockPos[] portalSpots = new BlockPos[] {masterPos, basePos};
|
||||
Vec3 firstCellCenter = masterPos.getCenter();
|
||||
Vec3 center = firstCellCenter
|
||||
.add(Vec3.atLowerCornerOf(heightAxis.getNormal()).scale(0.5D))
|
||||
.subtract(Vec3.atLowerCornerOf(facing.getNormal()).scale(0.5D));
|
||||
Vec3 local = hitLocation.subtract(center);
|
||||
double widthLocal = local.dot(Vec3.atLowerCornerOf(widthAxis.getNormal()));
|
||||
double heightLocal = local.dot(Vec3.atLowerCornerOf(heightAxis.getNormal()));
|
||||
double depthLocal = local.dot(Vec3.atLowerCornerOf(facing.getNormal()));
|
||||
double widthDistance = Math.max(0.0D, Math.abs(widthLocal) - 0.5D);
|
||||
double heightDistance = Math.max(0.0D, Math.abs(heightLocal) - 1.0D);
|
||||
double score = widthDistance * widthDistance
|
||||
+ heightDistance * heightDistance
|
||||
+ Math.abs(depthLocal) * 0.25D
|
||||
+ supportOrigin.distSqr(hitPos) * 0.12D
|
||||
+ Math.abs(widthOffset) * 0.04D
|
||||
+ Math.abs(heightOffset) * 0.025D
|
||||
+ (widthOffset != 0 || heightOffset != 0 ? 0.02D : 0.0D);
|
||||
if (score < bestScore) {
|
||||
bestScore = score;
|
||||
BlockPos[] portalSpots = new BlockPos[supports.size()];
|
||||
boolean valid = true;
|
||||
int spotIndex = 0;
|
||||
for (BlockPos supportPos : supports) {
|
||||
BlockPos portalPos = supportPos.relative(facing);
|
||||
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)) {
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
portalSpots[spotIndex++] = portalPos;
|
||||
}
|
||||
if (!valid) {
|
||||
continue;
|
||||
}
|
||||
BlockPos masterPos = null;
|
||||
for (BlockPos portalSpot : portalSpots) {
|
||||
BlockState state = level.getBlockState(portalSpot);
|
||||
boolean belongsToReplacingPortal = replacingPortal != null && replacingPortal.containsPortalSpot(portalSpot);
|
||||
boolean existingPortalCell = level.getBlockEntity(portalSpot) instanceof PortalGunPortalCellAccess;
|
||||
if ((state.canBeReplaced() || belongsToReplacingPortal || existingPortalCell) && level.getFluidState(portalSpot).isEmpty()) {
|
||||
masterPos = portalSpot;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (masterPos == null) {
|
||||
return null;
|
||||
}
|
||||
BlockPos[] orderedPortalSpots = new BlockPos[portalSpots.length];
|
||||
orderedPortalSpots[0] = masterPos;
|
||||
int orderedSpotIndex = 1;
|
||||
for (BlockPos portalSpot : portalSpots) {
|
||||
if (!portalSpot.equals(masterPos)) {
|
||||
orderedPortalSpots[orderedSpotIndex++] = portalSpot;
|
||||
}
|
||||
}
|
||||
portalSpots = orderedPortalSpots;
|
||||
Vec3 center = Vec3.ZERO;
|
||||
Set<BlockPos> compensatedSpots = new HashSet<>();
|
||||
if (widthOffset != 0 || heightOffset != 0) {
|
||||
compensatedSpots.add(hitPos.relative(facing));
|
||||
compensatedSpots.add(masterPos);
|
||||
compensatedSpots.add(basePos);
|
||||
compensatedSpots.add(supportOrigin);
|
||||
compensatedSpots.add(basePos.relative(facing.getOpposite()));
|
||||
for (BlockPos portalSpot : portalSpots) {
|
||||
center = center.add(portalSpot.getCenter());
|
||||
if (horizontalOffset != 0 || verticalOffset != 0) {
|
||||
compensatedSpots.add(portalSpot);
|
||||
compensatedSpots.add(portalSpot.relative(facing.getOpposite()));
|
||||
}
|
||||
bestPlacement = new PortalGunPlacement(center, facing, heightAxis, widthAxis, yaw, supportOrigin, masterPos, basePos, portalSpots, compensatedSpots);
|
||||
}
|
||||
center = center.scale(1.0D / portalSpots.length)
|
||||
.subtract(Vec3.atLowerCornerOf(facing.getNormal()).scale(0.5D));
|
||||
BlockPos supportOrigin = masterPos.relative(facing.getOpposite());
|
||||
BlockPos basePos = portalSpots.length > 1 ? portalSpots[1] : portalSpots[0];
|
||||
return new PortalGunPlacement(center, facing, heightAxis, widthAxis, yaw, supportOrigin, masterPos, basePos, portalSpots, compensatedSpots, width, height);
|
||||
}
|
||||
}
|
||||
}
|
||||
return bestPlacement;
|
||||
return null;
|
||||
}
|
||||
|
||||
private Direction resolveUpAxis(Player player, Direction facing) {
|
||||
private BlockPos referenceSupportPosition(BlockPos origin, Direction facing, Direction upAxis, int horizontalOffset, int verticalOffset, int widthCell, int heightCell) {
|
||||
int x;
|
||||
int y;
|
||||
int z;
|
||||
if (facing.getAxis() != Direction.Axis.Y) {
|
||||
if (facing.getAxis() == Direction.Axis.Z) {
|
||||
x = horizontalOffset + widthCell;
|
||||
z = 0;
|
||||
} else {
|
||||
x = 0;
|
||||
z = horizontalOffset + widthCell;
|
||||
}
|
||||
y = -verticalOffset - heightCell;
|
||||
} else if (upAxis == Direction.NORTH) {
|
||||
x = horizontalOffset + widthCell;
|
||||
y = 0;
|
||||
z = verticalOffset + heightCell;
|
||||
} else if (upAxis == Direction.SOUTH) {
|
||||
x = horizontalOffset + widthCell;
|
||||
y = 0;
|
||||
z = -verticalOffset - heightCell;
|
||||
} else if (upAxis == Direction.WEST) {
|
||||
x = -verticalOffset - heightCell;
|
||||
y = 0;
|
||||
z = horizontalOffset + widthCell;
|
||||
} else {
|
||||
x = verticalOffset + heightCell;
|
||||
y = 0;
|
||||
z = horizontalOffset + widthCell;
|
||||
}
|
||||
return origin.offset(x, y, z);
|
||||
}
|
||||
|
||||
private Direction resolveUpAxis(LivingEntity shooter, Direction facing) {
|
||||
if (facing.getAxis() != Direction.Axis.Y) {
|
||||
return Direction.UP;
|
||||
}
|
||||
Direction horizontal = player.getDirection();
|
||||
Direction horizontal = shooter.getDirection();
|
||||
if (horizontal.getAxis() == Direction.Axis.Y) {
|
||||
return Direction.NORTH;
|
||||
}
|
||||
return horizontal.getOpposite();
|
||||
return horizontal;
|
||||
}
|
||||
|
||||
private boolean canPlaceAt(Level level, UUID ownerId, PortalGunPortalEntity.PortalSide side, BlockPos supportOrigin, Direction facing, Direction heightAxis, UUID replacingPortalId) {
|
||||
for (int h = 0; h < 2; h++) {
|
||||
BlockPos supportPos = supportOrigin.relative(heightAxis, h);
|
||||
BlockPos airPos = supportPos.relative(facing);
|
||||
BlockState supportState = level.getBlockState(supportPos);
|
||||
BlockState airState = level.getBlockState(airPos);
|
||||
FluidState fluidState = level.getFluidState(airPos);
|
||||
if (!supportState.isFaceSturdy(level, supportPos, facing)) {
|
||||
return false;
|
||||
}
|
||||
if (!this.isPortalSpaceAvailable(level, airPos, airState, ownerId, side, replacingPortalId)) {
|
||||
return false;
|
||||
}
|
||||
if (!fluidState.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean isPortalSpaceAvailable(Level level, BlockPos pos, BlockState state, UUID ownerId, PortalGunPortalEntity.PortalSide side, UUID replacingPortalId) {
|
||||
if (state.isAir() || state.canBeReplaced()) {
|
||||
return true;
|
||||
}
|
||||
private boolean isPortalSpaceAvailable(Level level, BlockPos pos, BlockState state, UUID ownerId, String channelName, PortalGunPortalEntity.PortalSide side, Direction face) {
|
||||
BlockEntity blockEntity = level.getBlockEntity(pos);
|
||||
if (blockEntity instanceof PortalGunPortalMasterBlockEntity master) {
|
||||
return ownerId != null
|
||||
&& ownerId.equals(master.getOwnerId())
|
||||
&& master.getSide() == side
|
||||
&& (replacingPortalId == null || replacingPortalId.equals(master.getPortalId()));
|
||||
}
|
||||
if (blockEntity instanceof PortalGunPortalBaseBlockEntity base) {
|
||||
return ownerId != null
|
||||
&& ownerId.equals(base.getOwnerId())
|
||||
&& base.getSide() == side
|
||||
&& (replacingPortalId == null || replacingPortalId.equals(base.getPortalId()));
|
||||
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;
|
||||
}
|
||||
if (blockEntity instanceof PortalGunPortalBaseBlockEntity base && base.getPortalId() != null) {
|
||||
BlockEntity masterEntity = level.getBlockEntity(base.getMasterPos());
|
||||
if (!(masterEntity instanceof PortalGunPortalMasterBlockEntity master)) {
|
||||
return false;
|
||||
}
|
||||
return master.getFacing() != face
|
||||
|| (ownerId != null
|
||||
&& ownerId.equals(base.getOwnerId())
|
||||
&& channelName.equals(master.getChannelName())
|
||||
&& base.getSide() == side
|
||||
&& master.getSide() == side);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return ownerId != null
|
||||
&& ownerId.equals(record.ownerId())
|
||||
&& channelName.equals(record.channelName())
|
||||
&& record.side() == side;
|
||||
}
|
||||
return state.getCollisionShape(level, pos).isEmpty();
|
||||
}
|
||||
|
||||
private boolean placePortalBlocks(ServerLevel level, UUID ownerId, PortalGunPortalEntity.PortalSide side, PortalGunPortalEntity portal, PortalGunPlacement placement) {
|
||||
if (!level.setBlock(placement.masterPos(), this.portalMasterBlock.get().defaultBlockState(), 3)) {
|
||||
private boolean placePortalBlocks(ServerLevel level, UUID ownerId, UUID gunId, String ownerIdentity, String channelName, 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];
|
||||
BlockEntity existing = level.getBlockEntity(spot);
|
||||
boolean masterRole = i == 0;
|
||||
if (!(existing instanceof PortalGunPortalCellAccess)) {
|
||||
BlockState block = masterRole ? this.portalMasterBlock.get().defaultBlockState() : this.portalBaseBlock.get().defaultBlockState();
|
||||
if (!level.setBlock(spot, block, 3)) {
|
||||
for (BlockPos placed : placement.portalSpots()) {
|
||||
BlockEntity cell = level.getBlockEntity(placed);
|
||||
if (cell instanceof PortalGunPortalCellAccess portalCell) {
|
||||
portalCell.portalGun$removeFaceRecord(placement.facing(), portal.getUUID());
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (!level.setBlock(placement.basePos(), this.portalBaseBlock.get().defaultBlockState(), 3)) {
|
||||
level.setBlock(placement.masterPos(), Blocks.AIR.defaultBlockState(), 3);
|
||||
existing = level.getBlockEntity(spot);
|
||||
}
|
||||
if (!(existing instanceof PortalGunPortalCellAccess cell)
|
||||
|| !cell.portalGun$putFaceRecord(new PortalGunPortalFaceRecord(
|
||||
ownerId,
|
||||
ownerIdentity,
|
||||
gunId,
|
||||
channelName,
|
||||
portal.getUUID(),
|
||||
portal.getLinkedPortalId(),
|
||||
side,
|
||||
placement.facing(),
|
||||
placement.upAxis(),
|
||||
placement.masterPos(),
|
||||
placement.basePos(),
|
||||
java.util.Arrays.asList(placement.portalSpots()),
|
||||
placement.compensatedSpots(),
|
||||
pairTime,
|
||||
placement.width(),
|
||||
placement.height(),
|
||||
masterRole
|
||||
))) {
|
||||
return false;
|
||||
}
|
||||
if (!(level.getBlockEntity(placement.masterPos()) instanceof PortalGunPortalMasterBlockEntity master) || !(level.getBlockEntity(placement.basePos()) instanceof PortalGunPortalBaseBlockEntity base)) {
|
||||
level.setBlock(placement.masterPos(), Blocks.AIR.defaultBlockState(), 3);
|
||||
level.setBlock(placement.basePos(), Blocks.AIR.defaultBlockState(), 3);
|
||||
return false;
|
||||
}
|
||||
master.configure(ownerId, portal.getUUID(), side, placement.facing(), placement.upAxis(), placement.basePos(), placement.portalSpots(), placement.compensatedSpots(), level.getServer().getTickCount());
|
||||
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());
|
||||
} else if (!masterRole && existing instanceof PortalGunPortalBaseBlockEntity base
|
||||
&& (base.getPortalId() == null || base.getPortalId().equals(portal.getUUID()))) {
|
||||
base.configure(ownerId, portal.getUUID(), side, placement.masterPos());
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void syncPortalBlocks(ServerLevel level, PortalGunPortalEntity portal) {
|
||||
if (level.getBlockEntity(portal.getMasterPos()) instanceof PortalGunPortalMasterBlockEntity master) {
|
||||
master.updatePair(portal.getLinkedPortalId(), portal.getPairTime());
|
||||
for (BlockPos portalSpot : portal.getPortalSpots()) {
|
||||
if (level.getBlockEntity(portalSpot) instanceof PortalGunPortalCellAccess cell) {
|
||||
cell.portalGun$updateFacePair(portal.getUUID(), portal.getLinkedPortalId(), portal.getPairTime());
|
||||
}
|
||||
if (level.getBlockEntity(portal.getBasePos()) instanceof PortalGunPortalBaseBlockEntity base) {
|
||||
if (portalSpot.equals(portal.getMasterPos()) && level.getBlockEntity(portalSpot) instanceof PortalGunPortalMasterBlockEntity master && portal.getUUID().equals(master.getPortalId())) {
|
||||
master.updatePair(portal.getLinkedPortalId(), portal.getPairTime());
|
||||
} else if (!portalSpot.equals(portal.getMasterPos()) && level.getBlockEntity(portalSpot) instanceof PortalGunPortalBaseBlockEntity base && portal.getUUID().equals(base.getPortalId())) {
|
||||
base.updatePair(portal.getLinkedPortalId(), portal.getPairTime());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void setLastSide(ItemStack stack, PortalGunPortalEntity.PortalSide side) {
|
||||
if (side == null) {
|
||||
@@ -452,16 +640,115 @@ public class PortalGunItem extends Item implements GeoItem {
|
||||
return side.name().equals(sideName);
|
||||
}
|
||||
|
||||
private UUID ensureGunId(ItemStack stack) {
|
||||
public static UUID getGunId(ItemStack stack) {
|
||||
net.minecraft.world.item.component.CustomData customData = stack.get(DataComponents.CUSTOM_DATA);
|
||||
if (customData != null && customData.copyTag().hasUUID(GUN_ID_TAG)) {
|
||||
if (customData == null || !customData.copyTag().hasUUID(GUN_ID_TAG)) {
|
||||
return null;
|
||||
}
|
||||
return customData.copyTag().getUUID(GUN_ID_TAG);
|
||||
}
|
||||
UUID gunId = UUID.randomUUID();
|
||||
net.minecraft.world.item.component.CustomData.update(DataComponents.CUSTOM_DATA, stack, tag -> tag.putUUID(GUN_ID_TAG, gunId));
|
||||
|
||||
private UUID ensureGunId(ItemStack stack) {
|
||||
net.minecraft.world.item.component.CustomData customData = stack.get(DataComponents.CUSTOM_DATA);
|
||||
UUID gunId;
|
||||
if (customData != null && customData.copyTag().hasUUID(GUN_ID_TAG)) {
|
||||
gunId = customData.copyTag().getUUID(GUN_ID_TAG);
|
||||
} 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());
|
||||
}
|
||||
});
|
||||
return gunId;
|
||||
}
|
||||
|
||||
private UUID ensureGunId(ItemStack stack, UUID ownerId, String ownerName) {
|
||||
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;
|
||||
}
|
||||
return item.ensureGunId(stack);
|
||||
}
|
||||
|
||||
public static UUID ensureGunIdentity(ItemStack stack, Player owner) {
|
||||
if (stack.isEmpty() || !(stack.getItem() instanceof PortalGunItem item)) {
|
||||
return null;
|
||||
}
|
||||
return item.ensureGunId(stack, owner.getUUID(), owner.getGameProfile().getName());
|
||||
}
|
||||
|
||||
public static UUID getPortalOwnerId(ItemStack stack, UUID fallback) {
|
||||
net.minecraft.world.item.component.CustomData customData = stack.get(DataComponents.CUSTOM_DATA);
|
||||
if (customData == null) {
|
||||
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) {
|
||||
return fallback;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
@@ -484,7 +771,17 @@ public class PortalGunItem extends Item implements GeoItem {
|
||||
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.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
|
||||
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
package com.craisinlord.antarchy.content.network;
|
||||
|
||||
import com.craisinlord.antarchy.Antarchy;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import java.util.UUID;
|
||||
import net.minecraft.network.codec.StreamCodec;
|
||||
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
public record PortalGunIndicatorPayload(UUID gunId, boolean blueAvailable, boolean orangeAvailable) implements CustomPacketPayload {
|
||||
public static final Type<PortalGunIndicatorPayload> TYPE = new Type<>(ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "portal_gun_indicator"));
|
||||
public static final StreamCodec<ByteBuf, PortalGunIndicatorPayload> STREAM_CODEC = StreamCodec.of(
|
||||
(buffer, payload) -> {
|
||||
buffer.writeLong(payload.gunId().getMostSignificantBits());
|
||||
buffer.writeLong(payload.gunId().getLeastSignificantBits());
|
||||
buffer.writeBoolean(payload.blueAvailable());
|
||||
buffer.writeBoolean(payload.orangeAvailable());
|
||||
},
|
||||
buffer -> new PortalGunIndicatorPayload(new UUID(buffer.readLong(), buffer.readLong()), buffer.readBoolean(), buffer.readBoolean())
|
||||
);
|
||||
|
||||
@Override
|
||||
public Type<PortalGunIndicatorPayload> type() {
|
||||
return TYPE;
|
||||
}
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
package com.craisinlord.antarchy.content.network;
|
||||
|
||||
import com.craisinlord.antarchy.Antarchy;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import java.util.UUID;
|
||||
import net.minecraft.network.codec.StreamCodec;
|
||||
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
public record PortalGunIndicatorRequestPayload(UUID gunId) implements CustomPacketPayload {
|
||||
public static final Type<PortalGunIndicatorRequestPayload> TYPE = new Type<>(ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "portal_gun_indicator_request"));
|
||||
public static final StreamCodec<ByteBuf, PortalGunIndicatorRequestPayload> STREAM_CODEC = StreamCodec.of(
|
||||
(buffer, payload) -> {
|
||||
buffer.writeLong(payload.gunId().getMostSignificantBits());
|
||||
buffer.writeLong(payload.gunId().getLeastSignificantBits());
|
||||
},
|
||||
buffer -> new PortalGunIndicatorRequestPayload(new UUID(buffer.readLong(), buffer.readLong()))
|
||||
);
|
||||
|
||||
@Override
|
||||
public Type<PortalGunIndicatorRequestPayload> type() {
|
||||
return TYPE;
|
||||
}
|
||||
}
|
||||
+6
-2
@@ -2,13 +2,17 @@ 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 PortalGunPrimaryPayload() implements CustomPacketPayload {
|
||||
public record PortalGunPrimaryPayload(boolean offhand) implements CustomPacketPayload {
|
||||
public static final Type<PortalGunPrimaryPayload> TYPE = new Type<>(ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "portal_gun_primary"));
|
||||
public static final StreamCodec<ByteBuf, PortalGunPrimaryPayload> STREAM_CODEC = StreamCodec.unit(new PortalGunPrimaryPayload());
|
||||
public static final StreamCodec<ByteBuf, PortalGunPrimaryPayload> STREAM_CODEC = StreamCodec.composite(
|
||||
ByteBufCodecs.BOOL, PortalGunPrimaryPayload::offhand,
|
||||
PortalGunPrimaryPayload::new
|
||||
);
|
||||
|
||||
@Override
|
||||
public Type<PortalGunPrimaryPayload> type() {
|
||||
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
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 PortalGunResetPayload(int action) implements CustomPacketPayload {
|
||||
public static final Type<PortalGunResetPayload> TYPE = new Type<>(ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "portal_gun_reset"));
|
||||
public static final StreamCodec<ByteBuf, PortalGunResetPayload> STREAM_CODEC = StreamCodec.composite(
|
||||
ByteBufCodecs.VAR_INT, PortalGunResetPayload::action, PortalGunResetPayload::new
|
||||
);
|
||||
|
||||
@Override
|
||||
public Type<PortalGunResetPayload> type() {
|
||||
return TYPE;
|
||||
}
|
||||
}
|
||||
+1
-2
@@ -4,7 +4,6 @@ import java.util.List;
|
||||
import java.util.UUID;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.sounds.SoundEvents;
|
||||
import net.minecraft.sounds.SoundSource;
|
||||
import net.minecraft.world.damagesource.DamageSource;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
@@ -73,7 +72,7 @@ public class PortalGunBlackHoleEntity extends Entity implements GeoEntity {
|
||||
}
|
||||
this.pullNearby(serverLevel);
|
||||
if (this.ageTicks % 10 == 0) {
|
||||
serverLevel.playSound(null, this.blockPosition(), SoundEvents.PORTAL_AMBIENT, SoundSource.HOSTILE, 0.5F, 0.6F);
|
||||
serverLevel.playSound(null, this.blockPosition(), PortalGunPortalEntity.sound("portal_ambient"), SoundSource.HOSTILE, 0.5F, 0.6F);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+22
-86
@@ -23,13 +23,12 @@ public final class PortalGunCollisionHelper {
|
||||
return collided;
|
||||
}
|
||||
AABB pathBox = startBox.expandTowards(movement).inflate(SEARCH_PADDING);
|
||||
List<Entity> nearby = entity.level().getEntities(entity, pathBox, candidate -> candidate instanceof PortalGunPortalEntity && candidate.isAlive());
|
||||
List<PortalGunPortalEntity> nearby = PortalGunPortalEntity.findPortalsNearBounds(entity.level(), pathBox);
|
||||
if (nearby.isEmpty()) {
|
||||
return collided;
|
||||
}
|
||||
Vec3 adjusted = collided;
|
||||
for (Entity candidate : nearby) {
|
||||
PortalGunPortalEntity portal = (PortalGunPortalEntity) candidate;
|
||||
for (PortalGunPortalEntity portal : nearby) {
|
||||
Vec3 candidateAdjusted = resolveCollisionAgainstPortal(entity, startBox, movement, adjusted, portal);
|
||||
if (candidateAdjusted.distanceToSqr(movement) < adjusted.distanceToSqr(movement)) {
|
||||
adjusted = candidateAdjusted;
|
||||
@@ -39,6 +38,10 @@ public final class PortalGunCollisionHelper {
|
||||
}
|
||||
|
||||
private static Vec3 resolveCollisionAgainstPortal(Entity entity, AABB startBox, Vec3 movement, Vec3 collided, PortalGunPortalEntity portal) {
|
||||
PortalGunPortalEntity linked = portal.getLinkedPortal();
|
||||
if (linked == null || !linked.isAlive() || portal.isTeleportCoolingDown(entity)) {
|
||||
return collided;
|
||||
}
|
||||
PortalGunWorldPortalShape shape = portal.getWorldPortalShape();
|
||||
Vec3 normal = portal.getNormalVec().normalize();
|
||||
double desiredNormal = movement.dot(normal);
|
||||
@@ -60,21 +63,21 @@ public final class PortalGunCollisionHelper {
|
||||
&& !portal.getPortalInsides(entity).intersects(endBox)
|
||||
&& !intersectsPortalWindow(startBox, shape)
|
||||
&& !intersectsPortalWindow(endBox, shape)
|
||||
&& !segmentIntersectsPortalWindow(shape, startBox, endBox)) {
|
||||
&& !segmentIntersectsPortalWindow(entity, portal, startBox, endBox)) {
|
||||
return false;
|
||||
}
|
||||
if (crossesPortal(shape, entity, startBox, movement)) {
|
||||
Vec3 startProbe = portal.teleportProbePosition(entity, startBox);
|
||||
Vec3 endProbe = portal.teleportProbePosition(entity, endBox);
|
||||
if (portal.crossesPortal(startProbe, endProbe)) {
|
||||
return true;
|
||||
}
|
||||
Vec3 startCenter = startBox.getCenter();
|
||||
Vec3 endCenter = endBox.getCenter();
|
||||
PortalGunWorldPortalShape.PortalLocalCoords startCoords = shape.localCoords(startCenter);
|
||||
PortalGunWorldPortalShape.PortalLocalCoords endCoords = shape.localCoords(endCenter);
|
||||
PortalGunWorldPortalShape.PortalLocalCoords startCoords = shape.localCoords(startProbe);
|
||||
PortalGunWorldPortalShape.PortalLocalCoords endCoords = shape.localCoords(endProbe);
|
||||
return startCoords.depth() > 0.0D
|
||||
&& endCoords.depth() >= -PLANE_TOLERANCE
|
||||
&& Math.abs(endCoords.horizontal()) <= shape.halfWidth() + EDGE_PADDING
|
||||
&& Math.abs(endCoords.vertical()) <= shape.halfHeight() + EDGE_PADDING
|
||||
&& projectedWindowOverlap(shape, startBox, endBox);
|
||||
&& projectedWindowOverlap(shape, startProbe, endProbe);
|
||||
}
|
||||
|
||||
private static boolean intersectsPortalWindow(AABB box, PortalGunWorldPortalShape shape) {
|
||||
@@ -90,39 +93,17 @@ public final class PortalGunCollisionHelper {
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean segmentIntersectsPortalWindow(PortalGunWorldPortalShape shape, AABB startBox, AABB endBox) {
|
||||
for (Vec3 startCorner : corners(startBox)) {
|
||||
Vec3 endCorner = matchSample(startCorner, startBox, endBox);
|
||||
if (shape.crosses(startCorner, endCorner)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
private static boolean segmentIntersectsPortalWindow(Entity entity, PortalGunPortalEntity portal, AABB startBox, AABB endBox) {
|
||||
return portal.crossesPortal(portal.teleportProbePosition(entity, startBox), portal.teleportProbePosition(entity, endBox));
|
||||
}
|
||||
|
||||
private static boolean projectedWindowOverlap(PortalGunWorldPortalShape shape, AABB startBox, AABB endBox) {
|
||||
double minHorizontal = Double.POSITIVE_INFINITY;
|
||||
double maxHorizontal = Double.NEGATIVE_INFINITY;
|
||||
double minVertical = Double.POSITIVE_INFINITY;
|
||||
double maxVertical = Double.NEGATIVE_INFINITY;
|
||||
for (Vec3 corner : corners(startBox)) {
|
||||
PortalGunWorldPortalShape.PortalLocalCoords coords = shape.localCoords(corner);
|
||||
minHorizontal = Math.min(minHorizontal, coords.horizontal());
|
||||
maxHorizontal = Math.max(maxHorizontal, coords.horizontal());
|
||||
minVertical = Math.min(minVertical, coords.vertical());
|
||||
maxVertical = Math.max(maxVertical, coords.vertical());
|
||||
}
|
||||
for (Vec3 corner : corners(endBox)) {
|
||||
PortalGunWorldPortalShape.PortalLocalCoords coords = shape.localCoords(corner);
|
||||
minHorizontal = Math.min(minHorizontal, coords.horizontal());
|
||||
maxHorizontal = Math.max(maxHorizontal, coords.horizontal());
|
||||
minVertical = Math.min(minVertical, coords.vertical());
|
||||
maxVertical = Math.max(maxVertical, coords.vertical());
|
||||
}
|
||||
return maxHorizontal >= -shape.halfWidth() - BORDER_TOLERANCE
|
||||
&& minHorizontal <= shape.halfWidth() + BORDER_TOLERANCE
|
||||
&& maxVertical >= -shape.halfHeight() - BORDER_TOLERANCE
|
||||
&& minVertical <= shape.halfHeight() + BORDER_TOLERANCE;
|
||||
private static boolean projectedWindowOverlap(PortalGunWorldPortalShape shape, Vec3 startProbe, Vec3 endProbe) {
|
||||
PortalGunWorldPortalShape.PortalLocalCoords start = shape.localCoords(startProbe);
|
||||
PortalGunWorldPortalShape.PortalLocalCoords end = shape.localCoords(endProbe);
|
||||
return Math.abs(start.horizontal()) <= shape.halfWidth() + BORDER_TOLERANCE
|
||||
&& Math.abs(start.vertical()) <= shape.halfHeight() + BORDER_TOLERANCE
|
||||
&& Math.abs(end.horizontal()) <= shape.halfWidth() + BORDER_TOLERANCE
|
||||
&& Math.abs(end.vertical()) <= shape.halfHeight() + BORDER_TOLERANCE;
|
||||
}
|
||||
|
||||
private static Vec3 applyBorderCollision(Entity entity, AABB startBox, Vec3 movement, PortalGunPortalEntity portal) {
|
||||
@@ -143,51 +124,6 @@ public final class PortalGunCollisionHelper {
|
||||
return collided.distanceToSqr(movement) < 1.0E-7D ? movement : collided;
|
||||
}
|
||||
|
||||
private static boolean crossesPortal(PortalGunWorldPortalShape shape, Entity entity, AABB startBox, Vec3 movement) {
|
||||
AABB endBox = startBox.move(movement);
|
||||
for (Vec3 startSample : portalSamples(entity, startBox, true)) {
|
||||
Vec3 endSample = matchSample(startSample, startBox, endBox);
|
||||
if (shape.crosses(startSample, endSample)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static List<Vec3> portalSamples(Entity entity, AABB box, boolean includeEye) {
|
||||
Vec3 center = new Vec3((box.minX + box.maxX) * 0.5D, (box.minY + box.maxY) * 0.5D, (box.minZ + box.maxZ) * 0.5D);
|
||||
Vec3 topCenter = new Vec3(center.x, box.maxY, center.z);
|
||||
Vec3 bottomCenter = new Vec3(center.x, box.minY, center.z);
|
||||
if (includeEye) {
|
||||
Vec3 eye = new Vec3(entity.getX(), entity.getY() + entity.getEyeHeight(), entity.getZ());
|
||||
return List.of(center, eye, topCenter, bottomCenter);
|
||||
}
|
||||
return List.of(center, topCenter, bottomCenter);
|
||||
}
|
||||
|
||||
private static Vec3 matchSample(Vec3 startSample, AABB startBox, AABB endBox) {
|
||||
double xLerp = axisRatio(startSample.x, startBox.minX, startBox.maxX);
|
||||
double yLerp = axisRatio(startSample.y, startBox.minY, startBox.maxY);
|
||||
double zLerp = axisRatio(startSample.z, startBox.minZ, startBox.maxZ);
|
||||
return new Vec3(
|
||||
lerp(endBox.minX, endBox.maxX, xLerp),
|
||||
lerp(endBox.minY, endBox.maxY, yLerp),
|
||||
lerp(endBox.minZ, endBox.maxZ, zLerp)
|
||||
);
|
||||
}
|
||||
|
||||
private static double axisRatio(double value, double min, double max) {
|
||||
double size = max - min;
|
||||
if (Math.abs(size) < 1.0E-6D) {
|
||||
return 0.5D;
|
||||
}
|
||||
return (value - min) / size;
|
||||
}
|
||||
|
||||
private static double lerp(double min, double max, double delta) {
|
||||
return min + (max - min) * delta;
|
||||
}
|
||||
|
||||
private static Vec3[] corners(AABB box) {
|
||||
return new Vec3[] {
|
||||
new Vec3(box.minX, box.minY, box.minZ),
|
||||
|
||||
+280
@@ -0,0 +1,280 @@
|
||||
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) {
|
||||
}
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
package com.craisinlord.antarchy.content.portalgun;
|
||||
|
||||
import com.craisinlord.antarchy.content.item.PortalGunItem;
|
||||
import com.craisinlord.antarchy.content.network.PortalGunIndicatorPayload;
|
||||
import com.craisinlord.antarchy.content.network.PortalGunIndicatorRequestPayload;
|
||||
import java.util.UUID;
|
||||
import java.util.function.BiConsumer;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
|
||||
public final class PortalGunIndicatorSync {
|
||||
private static BiConsumer<ServerPlayer, PortalGunIndicatorPayload> sender = (player, payload) -> {};
|
||||
|
||||
private PortalGunIndicatorSync() {
|
||||
}
|
||||
|
||||
public static void setSender(BiConsumer<ServerPlayer, PortalGunIndicatorPayload> payloadSender) {
|
||||
sender = payloadSender;
|
||||
}
|
||||
|
||||
public static void handleRequest(ServerPlayer player, PortalGunIndicatorRequestPayload request) {
|
||||
ItemStack main = player.getMainHandItem();
|
||||
ItemStack off = player.getOffhandItem();
|
||||
ItemStack stack = main.getItem() instanceof PortalGunItem && request.gunId().equals(PortalGunItem.getGunId(main))
|
||||
? main
|
||||
: off.getItem() instanceof PortalGunItem && request.gunId().equals(PortalGunItem.getGunId(off)) ? off : ItemStack.EMPTY;
|
||||
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();
|
||||
sender.accept(player, new PortalGunIndicatorPayload(request.gunId(), blue, orange));
|
||||
}
|
||||
}
|
||||
+57
-6
@@ -1,6 +1,7 @@
|
||||
package com.craisinlord.antarchy.content.portalgun;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.HashSet;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
@@ -15,13 +16,18 @@ public record PortalGunPlacement(
|
||||
BlockPos masterPos,
|
||||
BlockPos basePos,
|
||||
BlockPos[] portalSpots,
|
||||
Set<BlockPos> compensatedSpots
|
||||
Set<BlockPos> compensatedSpots,
|
||||
int width,
|
||||
int height
|
||||
) {
|
||||
public static PortalGunPlacement fromStored(Direction facing, Direction upAxis, BlockPos masterPos, BlockPos basePos, Set<BlockPos> compensatedSpots) {
|
||||
public static PortalGunPlacement fromStored(Direction facing, Direction upAxis, BlockPos masterPos, BlockPos basePos, BlockPos[] portalSpots, Set<BlockPos> compensatedSpots, int width, int height) {
|
||||
Direction widthAxis = widthAxis(facing, upAxis);
|
||||
BlockPos supportOrigin = masterPos.relative(facing.getOpposite());
|
||||
Vec3 center = masterPos.getCenter()
|
||||
.add(Vec3.atLowerCornerOf(upAxis.getNormal()).scale(0.5D))
|
||||
Vec3 center = Vec3.ZERO;
|
||||
for (BlockPos portalSpot : portalSpots) {
|
||||
center = center.add(portalSpot.getCenter());
|
||||
}
|
||||
center = center.scale(1.0D / portalSpots.length)
|
||||
.subtract(Vec3.atLowerCornerOf(facing.getNormal()).scale(0.5D));
|
||||
return new PortalGunPlacement(
|
||||
center,
|
||||
@@ -32,8 +38,10 @@ public record PortalGunPlacement(
|
||||
supportOrigin,
|
||||
masterPos,
|
||||
basePos,
|
||||
new BlockPos[] {masterPos, basePos},
|
||||
compensatedSpots
|
||||
portalSpots,
|
||||
compensatedSpots,
|
||||
width,
|
||||
height
|
||||
);
|
||||
}
|
||||
|
||||
@@ -47,4 +55,47 @@ public record PortalGunPlacement(
|
||||
public static float yawFor(Direction facing, Direction upAxis) {
|
||||
return facing.getAxis() == Direction.Axis.Y ? upAxis.toYRot() : facing.toYRot();
|
||||
}
|
||||
|
||||
public static boolean isRectangularFootprint(BlockPos[] portalSpots, Direction facing, Direction upAxis, int width, int height) {
|
||||
if (portalSpots == null || portalSpots.length != width * height || portalSpots.length == 0) {
|
||||
return false;
|
||||
}
|
||||
Direction widthAxis = widthAxis(facing, upAxis);
|
||||
BlockPos origin = portalSpots[0];
|
||||
Set<Long> cells = new HashSet<>(portalSpots.length);
|
||||
int minWidth = Integer.MAX_VALUE;
|
||||
int maxWidth = Integer.MIN_VALUE;
|
||||
int minHeight = Integer.MAX_VALUE;
|
||||
int maxHeight = Integer.MIN_VALUE;
|
||||
for (BlockPos spot : portalSpots) {
|
||||
if (spot == null) {
|
||||
return false;
|
||||
}
|
||||
int dx = spot.getX() - origin.getX();
|
||||
int dy = spot.getY() - origin.getY();
|
||||
int dz = spot.getZ() - origin.getZ();
|
||||
int depth = dx * facing.getStepX() + dy * facing.getStepY() + dz * facing.getStepZ();
|
||||
int widthCell = dx * widthAxis.getStepX() + dy * widthAxis.getStepY() + dz * widthAxis.getStepZ();
|
||||
int heightCell = dx * upAxis.getStepX() + dy * upAxis.getStepY() + dz * upAxis.getStepZ();
|
||||
if (depth != 0 || !cells.add(((long) widthCell << 32) ^ (heightCell & 0xffffffffL))) {
|
||||
return false;
|
||||
}
|
||||
minWidth = Math.min(minWidth, widthCell);
|
||||
maxWidth = Math.max(maxWidth, widthCell);
|
||||
minHeight = Math.min(minHeight, heightCell);
|
||||
maxHeight = Math.max(maxHeight, heightCell);
|
||||
}
|
||||
if (maxWidth - minWidth != width - 1 || maxHeight - minHeight != height - 1
|
||||
|| (minWidth != 0 && maxWidth != 0) || (minHeight != 0 && maxHeight != 0)) {
|
||||
return false;
|
||||
}
|
||||
for (int widthCell = minWidth; widthCell <= maxWidth; widthCell++) {
|
||||
for (int heightCell = minHeight; heightCell <= maxHeight; heightCell++) {
|
||||
if (!cells.contains(((long) widthCell << 32) ^ (heightCell & 0xffffffffL))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
+426
-303
File diff suppressed because it is too large.
Load diff
+241
-28
@@ -1,40 +1,72 @@
|
||||
package com.craisinlord.antarchy.content.portalgun;
|
||||
|
||||
import com.craisinlord.antarchy.content.item.PortalGunItem;
|
||||
import com.craisinlord.antarchy.config.AntarchySettings;
|
||||
import java.util.Comparator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.component.DataComponents;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.network.syncher.EntityDataAccessor;
|
||||
import net.minecraft.network.syncher.EntityDataSerializers;
|
||||
import net.minecraft.network.syncher.SynchedEntityData;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.TicketType;
|
||||
import net.minecraft.tags.TagKey;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.world.entity.EntityDimensions;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
import net.minecraft.world.entity.Pose;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.entity.projectile.ThrowableItemProjectile;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.Items;
|
||||
import net.minecraft.world.level.ClipContext;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.block.StainedGlassBlock;
|
||||
import net.minecraft.world.level.block.StainedGlassPaneBlock;
|
||||
import net.minecraft.world.level.block.TintedGlassBlock;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.phys.BlockHitResult;
|
||||
import net.minecraft.world.phys.EntityHitResult;
|
||||
import net.minecraft.world.phys.HitResult;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
|
||||
public class PortalGunProjectileEntity extends ThrowableItemProjectile {
|
||||
private static final TagKey<Block> GLASS_BLOCKS = TagKey.create(Registries.BLOCK, ResourceLocation.fromNamespaceAndPath("c", "glass_blocks"));
|
||||
private static final TagKey<Block> GLASS_PANES = TagKey.create(Registries.BLOCK, ResourceLocation.fromNamespaceAndPath("c", "glass_panes"));
|
||||
private static final int MAX_TICKETED_PROJECTILES = 20;
|
||||
private static final int PROJECTILE_TICKET_LEVEL = 31;
|
||||
private static final TicketType<UUID> PROJECTILE_TICKET = TicketType.create("antarchy_portal_projectile", Comparator.comparing(UUID::toString));
|
||||
private static final LinkedHashMap<UUID, ProjectileTicket> PROJECTILE_TICKETS = new LinkedHashMap<>();
|
||||
private static final EntityDataAccessor<Integer> SIDE = SynchedEntityData.defineId(PortalGunProjectileEntity.class, EntityDataSerializers.INT);
|
||||
private static final EntityDataAccessor<Integer> SHOOTER_ID = SynchedEntityData.defineId(PortalGunProjectileEntity.class, EntityDataSerializers.INT);
|
||||
private static final EntityDataAccessor<Integer> DISTANCE = SynchedEntityData.defineId(PortalGunProjectileEntity.class, EntityDataSerializers.INT);
|
||||
private static final EntityDataAccessor<Integer> SPAWN_X = SynchedEntityData.defineId(PortalGunProjectileEntity.class, EntityDataSerializers.INT);
|
||||
private static final EntityDataAccessor<Integer> SPAWN_Y = SynchedEntityData.defineId(PortalGunProjectileEntity.class, EntityDataSerializers.INT);
|
||||
private static final EntityDataAccessor<Integer> SPAWN_Z = SynchedEntityData.defineId(PortalGunProjectileEntity.class, EntityDataSerializers.INT);
|
||||
private static final EntityDataAccessor<Long> SPAWN_X = SynchedEntityData.defineId(PortalGunProjectileEntity.class, EntityDataSerializers.LONG);
|
||||
private static final EntityDataAccessor<Long> SPAWN_Y = SynchedEntityData.defineId(PortalGunProjectileEntity.class, EntityDataSerializers.LONG);
|
||||
private static final EntityDataAccessor<Long> SPAWN_Z = SynchedEntityData.defineId(PortalGunProjectileEntity.class, EntityDataSerializers.LONG);
|
||||
private static final EntityDataAccessor<Integer> MAX_TRAVEL_DISTANCE_DATA = SynchedEntityData.defineId(PortalGunProjectileEntity.class, EntityDataSerializers.INT);
|
||||
private static final EntityDataAccessor<Float> VELOCITY_X = SynchedEntityData.defineId(PortalGunProjectileEntity.class, EntityDataSerializers.FLOAT);
|
||||
private static final EntityDataAccessor<Float> VELOCITY_Y = SynchedEntityData.defineId(PortalGunProjectileEntity.class, EntityDataSerializers.FLOAT);
|
||||
private static final EntityDataAccessor<Float> VELOCITY_Z = SynchedEntityData.defineId(PortalGunProjectileEntity.class, EntityDataSerializers.FLOAT);
|
||||
private static final int MAX_FLIGHT_TICKS = 80;
|
||||
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);
|
||||
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;
|
||||
private UUID gunId;
|
||||
private boolean spawnSynced;
|
||||
private boolean ticketRegistered;
|
||||
public int portalWidth = 1;
|
||||
public int portalHeight = 2;
|
||||
|
||||
@@ -53,30 +85,55 @@ public class PortalGunProjectileEntity extends ThrowableItemProjectile {
|
||||
this.noCulling = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityDimensions getDimensions(Pose pose) {
|
||||
return EntityDimensions.fixed(0.3F, 0.3F);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void defineSynchedData(SynchedEntityData.Builder builder) {
|
||||
super.defineSynchedData(builder);
|
||||
builder.define(SIDE, PortalGunPortalEntity.PortalSide.BLUE.ordinal());
|
||||
builder.define(SHOOTER_ID, -1);
|
||||
builder.define(DISTANCE, 0);
|
||||
builder.define(SPAWN_X, 0);
|
||||
builder.define(SPAWN_Y, 0);
|
||||
builder.define(SPAWN_Z, 0);
|
||||
builder.define(SPAWN_X, Double.doubleToRawLongBits(0.0D));
|
||||
builder.define(SPAWN_Y, Double.doubleToRawLongBits(0.0D));
|
||||
builder.define(SPAWN_Z, Double.doubleToRawLongBits(0.0D));
|
||||
builder.define(MAX_TRAVEL_DISTANCE_DATA, (int) MAX_TRAVEL_DISTANCE);
|
||||
builder.define(VELOCITY_X, 0.0F);
|
||||
builder.define(VELOCITY_Y, 0.0F);
|
||||
builder.define(VELOCITY_Z, 0.0F);
|
||||
builder.define(PROJECTILE_SPEED, 4.99F);
|
||||
builder.define(PASS_THROUGH_GLASS, false);
|
||||
builder.define(PASS_THROUGH_LIQUID, false);
|
||||
}
|
||||
|
||||
public void configure(PortalGunPortalEntity.PortalSide side, UUID gunId, ItemStack gunStack) {
|
||||
this.entityData.set(SIDE, side.ordinal());
|
||||
this.gunId = gunId;
|
||||
this.setItem(gunStack.copyWithCount(1));
|
||||
this.portalWidth = PortalGunItem.getPortalWidth(gunStack);
|
||||
this.portalHeight = PortalGunItem.getPortalHeight(gunStack);
|
||||
this.entityData.set(MAX_TRAVEL_DISTANCE_DATA, AntarchySettings.portalGunMaxShootDistance());
|
||||
this.entityData.set(PASS_THROUGH_GLASS, AntarchySettings.portalGunCanFireThroughGlass());
|
||||
this.entityData.set(PASS_THROUGH_LIQUID, AntarchySettings.portalGunCanFireThroughLiquid());
|
||||
Entity owner = this.getOwner();
|
||||
this.entityData.set(SHOOTER_ID, owner == null ? -1 : owner.getId());
|
||||
this.syncSpawnPosition(this.blockPosition());
|
||||
}
|
||||
|
||||
public void syncLaunchState() {
|
||||
this.syncSpawnPosition(this.position());
|
||||
this.syncVelocity();
|
||||
}
|
||||
|
||||
public void setProjectileSpeed(double speed) {
|
||||
this.entityData.set(PROJECTILE_SPEED, (float) speed);
|
||||
}
|
||||
|
||||
public double getProjectileSpeed() {
|
||||
return this.entityData.get(PROJECTILE_SPEED);
|
||||
}
|
||||
|
||||
public PortalGunPortalEntity.PortalSide getPortalSide() {
|
||||
return this.entityData.get(SIDE) == PortalGunPortalEntity.PortalSide.ORANGE.ordinal() ? PortalGunPortalEntity.PortalSide.ORANGE : PortalGunPortalEntity.PortalSide.BLUE;
|
||||
}
|
||||
@@ -89,8 +146,16 @@ public class PortalGunProjectileEntity extends ThrowableItemProjectile {
|
||||
return this.entityData.get(DISTANCE);
|
||||
}
|
||||
|
||||
public BlockPos getSyncedSpawnPos() {
|
||||
return new BlockPos(this.entityData.get(SPAWN_X), this.entityData.get(SPAWN_Y), this.entityData.get(SPAWN_Z));
|
||||
public int getMaxTravelDistance() {
|
||||
return this.entityData.get(MAX_TRAVEL_DISTANCE_DATA);
|
||||
}
|
||||
|
||||
public Vec3 getSyncedSpawnPosition() {
|
||||
return new Vec3(
|
||||
Double.longBitsToDouble(this.entityData.get(SPAWN_X)),
|
||||
Double.longBitsToDouble(this.entityData.get(SPAWN_Y)),
|
||||
Double.longBitsToDouble(this.entityData.get(SPAWN_Z))
|
||||
);
|
||||
}
|
||||
|
||||
public Vec3 getSyncedVelocity() {
|
||||
@@ -124,22 +189,82 @@ public class PortalGunProjectileEntity extends ThrowableItemProjectile {
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
super.tick();
|
||||
if (!this.spawnSynced) {
|
||||
this.syncSpawnPosition(this.blockPosition());
|
||||
if (this.level().isClientSide && !this.spawnSynced) {
|
||||
Vec3 spawnPosition = this.getSyncedSpawnPosition();
|
||||
this.setPos(spawnPosition.x, spawnPosition.y, spawnPosition.z);
|
||||
this.setDeltaMovement(this.getSyncedVelocity());
|
||||
this.spawnSynced = true;
|
||||
}
|
||||
if (!this.level().isClientSide && !this.isRemoved()) {
|
||||
this.updateProjectileTicket();
|
||||
}
|
||||
this.baseTick();
|
||||
if (this.isRemoved()) {
|
||||
return;
|
||||
}
|
||||
if (!this.spawnSynced && !this.level().isClientSide) {
|
||||
this.syncSpawnPosition(this.position());
|
||||
this.syncVelocity();
|
||||
this.spawnSynced = true;
|
||||
}
|
||||
this.entityData.set(DISTANCE, this.tickCount);
|
||||
if (this.tickCount > MAX_FLIGHT_TICKS) {
|
||||
int maxTravelDistance = this.getMaxTravelDistance();
|
||||
Vec3 spawnPosition = this.getSyncedSpawnPosition();
|
||||
if (this.tickCount > maxTravelDistance / 5.0D + 10.0D
|
||||
|| this.position().distanceToSqr(spawnPosition) > (maxTravelDistance + 5.0D) * (maxTravelDistance + 5.0D)) {
|
||||
this.discard();
|
||||
return;
|
||||
}
|
||||
Vec3 motion = this.getDeltaMovement();
|
||||
Vec3 start = this.position();
|
||||
Vec3 end = start.add(motion);
|
||||
ClipContext.Fluid fluidMode = this.entityData.get(PASS_THROUGH_LIQUID) ? ClipContext.Fluid.NONE : ClipContext.Fluid.ANY;
|
||||
HitResult hit = this.level().clip(new ClipContext(start, end, ClipContext.Block.COLLIDER, fluidMode, this));
|
||||
if (hit instanceof BlockHitResult blockHit) {
|
||||
BlockPos hitBlockPos = blockHit.getBlockPos();
|
||||
net.minecraft.world.level.block.state.BlockState hitBlockState = this.level().getBlockState(hitBlockPos);
|
||||
boolean passThroughGlass = this.shouldPassThroughGlass(blockHit);
|
||||
if (!hitBlockState.isAir()) {
|
||||
hitBlockState.entityInside(this.level(), hitBlockPos, this);
|
||||
if (this.isRemoved()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (!passThroughGlass) {
|
||||
this.onHit(blockHit);
|
||||
if (this.isRemoved()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.setPos(blockHit.getLocation().subtract(motion.scale(0.98D)));
|
||||
}
|
||||
if (motion.lengthSqr() > 1.0E-6D) {
|
||||
double horizontal = Math.sqrt(motion.x * motion.x + motion.z * motion.z);
|
||||
this.setYRot((float) (net.minecraft.util.Mth.atan2(motion.z, motion.x) * (180.0D / Math.PI)) - 90.0F);
|
||||
this.setXRot((float) (-(net.minecraft.util.Mth.atan2(motion.y, horizontal) * (180.0D / Math.PI))));
|
||||
}
|
||||
this.setPos(this.position().add(motion));
|
||||
this.checkInsideBlocks();
|
||||
if (this.isRemoved()) {
|
||||
return;
|
||||
}
|
||||
if (!this.level().isClientSide && !this.isRemoved()) {
|
||||
this.updateProjectileTicket();
|
||||
}
|
||||
if (this.isInWater()) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
float backtrack = 0.25F;
|
||||
this.level().addParticle(
|
||||
net.minecraft.core.particles.ParticleTypes.BUBBLE,
|
||||
this.getX() - motion.x * backtrack,
|
||||
this.getY() - motion.y * backtrack,
|
||||
this.getZ() - motion.z * backtrack,
|
||||
motion.x,
|
||||
motion.y,
|
||||
motion.z
|
||||
);
|
||||
}
|
||||
}
|
||||
if (this.level().isClientSide) {
|
||||
Vec3 point = this.position();
|
||||
if (this.getPortalSide() == PortalGunPortalEntity.PortalSide.BLUE) {
|
||||
@@ -152,6 +277,63 @@ public class PortalGunProjectileEntity extends ThrowableItemProjectile {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(Entity.RemovalReason reason) {
|
||||
this.releaseProjectileTicket();
|
||||
super.remove(reason);
|
||||
}
|
||||
|
||||
private void updateProjectileTicket() {
|
||||
if (!(this.level() instanceof ServerLevel serverLevel)) {
|
||||
return;
|
||||
}
|
||||
if (!AntarchySettings.portalGunCanPortalProjectilesChunkload()) {
|
||||
this.releaseProjectileTicket();
|
||||
return;
|
||||
}
|
||||
ChunkPos chunkPos = this.chunkPosition();
|
||||
UUID ticketId = this.getUUID();
|
||||
ProjectileTicket current = PROJECTILE_TICKETS.get(ticketId);
|
||||
if (current != null && current.level == serverLevel && current.chunkPos.equals(chunkPos)) {
|
||||
return;
|
||||
}
|
||||
if (current != null) {
|
||||
current.level.getChunkSource().removeRegionTicket(PROJECTILE_TICKET, current.chunkPos, PROJECTILE_TICKET_LEVEL, ticketId);
|
||||
PROJECTILE_TICKETS.remove(ticketId);
|
||||
}
|
||||
while (PROJECTILE_TICKETS.size() >= MAX_TICKETED_PROJECTILES) {
|
||||
Map.Entry<UUID, ProjectileTicket> oldest = PROJECTILE_TICKETS.entrySet().iterator().next();
|
||||
oldest.getValue().level.getChunkSource().removeRegionTicket(PROJECTILE_TICKET, oldest.getValue().chunkPos, PROJECTILE_TICKET_LEVEL, oldest.getKey());
|
||||
PROJECTILE_TICKETS.remove(oldest.getKey());
|
||||
}
|
||||
serverLevel.getChunkSource().addRegionTicket(PROJECTILE_TICKET, chunkPos, PROJECTILE_TICKET_LEVEL, ticketId);
|
||||
PROJECTILE_TICKETS.put(ticketId, new ProjectileTicket(serverLevel, chunkPos));
|
||||
this.ticketRegistered = true;
|
||||
}
|
||||
|
||||
private void releaseProjectileTicket() {
|
||||
if (!this.ticketRegistered) {
|
||||
return;
|
||||
}
|
||||
UUID ticketId = this.getUUID();
|
||||
ProjectileTicket ticket = PROJECTILE_TICKETS.remove(ticketId);
|
||||
if (ticket != null) {
|
||||
ticket.level.getChunkSource().removeRegionTicket(PROJECTILE_TICKET, ticket.chunkPos, PROJECTILE_TICKET_LEVEL, ticketId);
|
||||
}
|
||||
this.ticketRegistered = false;
|
||||
}
|
||||
|
||||
public static void releaseAllProjectileTickets() {
|
||||
for (Map.Entry<UUID, ProjectileTicket> entry : PROJECTILE_TICKETS.entrySet()) {
|
||||
ProjectileTicket ticket = entry.getValue();
|
||||
ticket.level.getChunkSource().removeRegionTicket(PROJECTILE_TICKET, ticket.chunkPos, PROJECTILE_TICKET_LEVEL, entry.getKey());
|
||||
}
|
||||
PROJECTILE_TICKETS.clear();
|
||||
}
|
||||
|
||||
private record ProjectileTicket(ServerLevel level, ChunkPos chunkPos) {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onHitEntity(EntityHitResult result) {
|
||||
}
|
||||
@@ -161,13 +343,30 @@ public class PortalGunProjectileEntity extends ThrowableItemProjectile {
|
||||
if (!this.level().isClientSide && this.level() instanceof ServerLevel serverLevel) {
|
||||
this.spawnImpactParticles(serverLevel, result.getLocation());
|
||||
ItemStack sourceStack = this.resolveSourceStack();
|
||||
if (this.getOwner() instanceof Player player && sourceStack.getItem() instanceof PortalGunItem portalGunItem) {
|
||||
portalGunItem.handlePortalImpact(serverLevel, player, sourceStack, this.getPortalSide(), result, this.position());
|
||||
if (this.getOwner() instanceof LivingEntity shooter && sourceStack.getItem() instanceof PortalGunItem portalGunItem) {
|
||||
portalGunItem.handlePortalImpact(serverLevel, shooter, sourceStack, this.gunId, this.getPortalSide(), result, result.getLocation(), this.portalWidth, this.portalHeight);
|
||||
}
|
||||
}
|
||||
this.discard();
|
||||
}
|
||||
|
||||
private boolean shouldPassThroughGlass(BlockHitResult hit) {
|
||||
if (!this.entityData.get(PASS_THROUGH_GLASS)) {
|
||||
return false;
|
||||
}
|
||||
BlockPos blockPos = hit.getBlockPos();
|
||||
BlockState state = this.level().getBlockState(blockPos);
|
||||
Block block = state.getBlock();
|
||||
return state.is(GLASS_BLOCKS)
|
||||
|| state.is(GLASS_PANES)
|
||||
|| block == Blocks.GLASS
|
||||
|| block == Blocks.GLASS_PANE
|
||||
|| block == Blocks.TINTED_GLASS
|
||||
|| block instanceof StainedGlassBlock
|
||||
|| block instanceof StainedGlassPaneBlock
|
||||
|| block instanceof TintedGlassBlock;
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -178,10 +377,10 @@ public class PortalGunProjectileEntity extends ThrowableItemProjectile {
|
||||
level.sendParticles(net.minecraft.core.particles.ParticleTypes.CRIT, impactPos.x, impactPos.y, impactPos.z, 6, 0.04D, 0.04D, 0.04D, 0.01D);
|
||||
}
|
||||
|
||||
private void syncSpawnPosition(BlockPos pos) {
|
||||
this.entityData.set(SPAWN_X, pos.getX());
|
||||
this.entityData.set(SPAWN_Y, pos.getY());
|
||||
this.entityData.set(SPAWN_Z, pos.getZ());
|
||||
private void syncSpawnPosition(Vec3 pos) {
|
||||
this.entityData.set(SPAWN_X, Double.doubleToRawLongBits(pos.x));
|
||||
this.entityData.set(SPAWN_Y, Double.doubleToRawLongBits(pos.y));
|
||||
this.entityData.set(SPAWN_Z, Double.doubleToRawLongBits(pos.z));
|
||||
this.spawnSynced = true;
|
||||
}
|
||||
|
||||
@@ -207,12 +406,20 @@ public class PortalGunProjectileEntity extends ThrowableItemProjectile {
|
||||
tag.putInt("Side", this.entityData.get(SIDE));
|
||||
tag.putInt("ShooterId", this.entityData.get(SHOOTER_ID));
|
||||
tag.putInt("Distance", this.entityData.get(DISTANCE));
|
||||
tag.putInt("SpawnX", this.entityData.get(SPAWN_X));
|
||||
tag.putInt("SpawnY", this.entityData.get(SPAWN_Y));
|
||||
tag.putInt("SpawnZ", this.entityData.get(SPAWN_Z));
|
||||
Vec3 spawnPosition = this.getSyncedSpawnPosition();
|
||||
tag.putInt("SpawnX", net.minecraft.util.Mth.floor(spawnPosition.x));
|
||||
tag.putInt("SpawnY", net.minecraft.util.Mth.floor(spawnPosition.y));
|
||||
tag.putInt("SpawnZ", net.minecraft.util.Mth.floor(spawnPosition.z));
|
||||
tag.putDouble("SpawnPosX", spawnPosition.x);
|
||||
tag.putDouble("SpawnPosY", spawnPosition.y);
|
||||
tag.putDouble("SpawnPosZ", spawnPosition.z);
|
||||
tag.putInt("MaxTravelDistance", this.entityData.get(MAX_TRAVEL_DISTANCE_DATA));
|
||||
tag.putFloat("VelocityX", this.entityData.get(VELOCITY_X));
|
||||
tag.putFloat("VelocityY", this.entityData.get(VELOCITY_Y));
|
||||
tag.putFloat("VelocityZ", this.entityData.get(VELOCITY_Z));
|
||||
tag.putFloat("ProjectileSpeed", this.entityData.get(PROJECTILE_SPEED));
|
||||
tag.putBoolean("PassThroughGlass", this.entityData.get(PASS_THROUGH_GLASS));
|
||||
tag.putBoolean("PassThroughLiquid", this.entityData.get(PASS_THROUGH_LIQUID));
|
||||
tag.putInt("PortalWidth", this.portalWidth);
|
||||
tag.putInt("PortalHeight", this.portalHeight);
|
||||
if (this.gunId != null) {
|
||||
@@ -226,14 +433,20 @@ public class PortalGunProjectileEntity extends ThrowableItemProjectile {
|
||||
this.entityData.set(SIDE, tag.getInt("Side"));
|
||||
this.entityData.set(SHOOTER_ID, tag.getInt("ShooterId"));
|
||||
this.entityData.set(DISTANCE, tag.getInt("Distance"));
|
||||
this.entityData.set(SPAWN_X, tag.getInt("SpawnX"));
|
||||
this.entityData.set(SPAWN_Y, tag.getInt("SpawnY"));
|
||||
this.entityData.set(SPAWN_Z, tag.getInt("SpawnZ"));
|
||||
this.entityData.set(SPAWN_X, Double.doubleToRawLongBits(tag.contains("SpawnPosX") ? tag.getDouble("SpawnPosX") : tag.getInt("SpawnX")));
|
||||
this.entityData.set(SPAWN_Y, Double.doubleToRawLongBits(tag.contains("SpawnPosY") ? tag.getDouble("SpawnPosY") : tag.getInt("SpawnY")));
|
||||
this.entityData.set(SPAWN_Z, Double.doubleToRawLongBits(tag.contains("SpawnPosZ") ? tag.getDouble("SpawnPosZ") : tag.getInt("SpawnZ")));
|
||||
this.entityData.set(MAX_TRAVEL_DISTANCE_DATA, tag.contains("MaxTravelDistance")
|
||||
? Math.max(1, tag.getInt("MaxTravelDistance"))
|
||||
: AntarchySettings.portalGunMaxShootDistance());
|
||||
this.entityData.set(VELOCITY_X, tag.getFloat("VelocityX"));
|
||||
this.entityData.set(VELOCITY_Y, tag.getFloat("VelocityY"));
|
||||
this.entityData.set(VELOCITY_Z, tag.getFloat("VelocityZ"));
|
||||
this.portalWidth = Math.max(1, tag.getInt("PortalWidth"));
|
||||
this.portalHeight = Math.max(2, tag.getInt("PortalHeight"));
|
||||
this.entityData.set(PROJECTILE_SPEED, tag.contains("ProjectileSpeed") ? tag.getFloat("ProjectileSpeed") : 4.99F);
|
||||
this.entityData.set(PASS_THROUGH_GLASS, tag.contains("PassThroughGlass") ? tag.getBoolean("PassThroughGlass") : AntarchySettings.portalGunCanFireThroughGlass());
|
||||
this.entityData.set(PASS_THROUGH_LIQUID, tag.contains("PassThroughLiquid") ? tag.getBoolean("PassThroughLiquid") : AntarchySettings.portalGunCanFireThroughLiquid());
|
||||
this.portalWidth = net.minecraft.util.Mth.clamp(tag.getInt("PortalWidth"), 1, 16);
|
||||
this.portalHeight = net.minecraft.util.Mth.clamp(tag.getInt("PortalHeight"), 2, 16);
|
||||
this.spawnSynced = true;
|
||||
if (tag.hasUUID("GunId")) {
|
||||
this.gunId = tag.getUUID("GunId");
|
||||
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
package com.craisinlord.antarchy.content.portalgun;
|
||||
|
||||
import com.craisinlord.antarchy.content.item.PortalGunItem;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
|
||||
public final class PortalGunResetManager {
|
||||
public static final int ARM = 0;
|
||||
public static final int RELEASE = 1;
|
||||
public static final int RESET_ALL = 2;
|
||||
public static final int RESET_BLUE = 3;
|
||||
private static final Map<UUID, ResetState> STATES = new HashMap<>();
|
||||
|
||||
private PortalGunResetManager() {
|
||||
}
|
||||
|
||||
public static void handleInput(ServerPlayer player, int action) {
|
||||
ItemStack stack = heldPortalGun(player);
|
||||
if (stack.isEmpty()) {
|
||||
STATES.remove(player.getUUID());
|
||||
return;
|
||||
}
|
||||
UUID gunId = PortalGunItem.ensureGunIdentity(stack, player);
|
||||
if (action == ARM) {
|
||||
STATES.put(player.getUUID(), new ResetState(gunId, player.serverLevel().getGameTime(), false));
|
||||
return;
|
||||
}
|
||||
ResetState state = activeState(player, gunId);
|
||||
if (action == RESET_ALL) {
|
||||
PortalGunItem item = (PortalGunItem) stack.getItem();
|
||||
item.resetPortals(player, stack);
|
||||
STATES.remove(player.getUUID());
|
||||
} else if (action == RESET_BLUE && state != null && !state.sideReset()) {
|
||||
((PortalGunItem) stack.getItem()).resetPortalSide(player, stack, PortalGunPortalEntity.PortalSide.BLUE);
|
||||
STATES.put(player.getUUID(), new ResetState(gunId, state.startedAt(), true));
|
||||
} else if (action == RELEASE) {
|
||||
STATES.remove(player.getUUID());
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean handleOrangeReset(ServerPlayer player, ItemStack stack) {
|
||||
if (!(stack.getItem() instanceof PortalGunItem)) {
|
||||
return false;
|
||||
}
|
||||
UUID gunId = PortalGunItem.getGunId(stack);
|
||||
ResetState state = activeState(player, gunId);
|
||||
if (state == null) {
|
||||
return false;
|
||||
}
|
||||
if (!state.sideReset()) {
|
||||
((PortalGunItem) stack.getItem()).resetPortalSide(player, stack, PortalGunPortalEntity.PortalSide.ORANGE);
|
||||
STATES.put(player.getUUID(), new ResetState(gunId, state.startedAt(), true));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void clear(MinecraftServer server) {
|
||||
STATES.clear();
|
||||
}
|
||||
|
||||
private static ResetState activeState(ServerPlayer player, UUID gunId) {
|
||||
ResetState state = STATES.get(player.getUUID());
|
||||
if (state == null || gunId == null || !state.gunId().equals(gunId)
|
||||
|| player.serverLevel().getGameTime() - state.startedAt() > 100L) {
|
||||
STATES.remove(player.getUUID());
|
||||
return null;
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
private static ItemStack heldPortalGun(ServerPlayer player) {
|
||||
ItemStack main = player.getMainHandItem();
|
||||
if (main.getItem() instanceof PortalGunItem) {
|
||||
return main;
|
||||
}
|
||||
ItemStack offhand = player.getOffhandItem();
|
||||
return offhand.getItem() instanceof PortalGunItem ? offhand : ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
private record ResetState(UUID gunId, long startedAt, boolean sideReset) {
|
||||
}
|
||||
}
|
||||
+344
-39
@@ -1,12 +1,17 @@
|
||||
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;
|
||||
import net.minecraft.core.HolderLookup;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.ListTag;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
@@ -16,9 +21,14 @@ public final class PortalGunSavedData extends SavedData {
|
||||
private static final String ID = "antarchy_portal_gun_pairs";
|
||||
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 final Map<UUID, PairRecord> pairs = new HashMap<>();
|
||||
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();
|
||||
@@ -31,7 +41,20 @@ public final class PortalGunSavedData extends SavedData {
|
||||
UUID owner = entryTag.getUUID(OWNER_KEY);
|
||||
UUID blue = entryTag.hasUUID(BLUE_KEY) ? entryTag.getUUID(BLUE_KEY) : null;
|
||||
UUID orange = entryTag.hasUUID(ORANGE_KEY) ? entryTag.getUUID(ORANGE_KEY) : null;
|
||||
data.pairs.put(owner, new PairRecord(blue, orange));
|
||||
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));
|
||||
}
|
||||
}
|
||||
return data;
|
||||
}
|
||||
@@ -39,21 +62,44 @@ public final class PortalGunSavedData extends SavedData {
|
||||
@Override
|
||||
public CompoundTag save(CompoundTag tag, HolderLookup.Provider registries) {
|
||||
ListTag entries = new ListTag();
|
||||
for (Map.Entry<UUID, PairRecord> entry : this.pairs.entrySet()) {
|
||||
for (Map.Entry<PairKey, PairRecord> entry : this.pairs.entrySet()) {
|
||||
CompoundTag entryTag = new CompoundTag();
|
||||
entryTag.putUUID(OWNER_KEY, entry.getKey());
|
||||
if (entry.getValue().bluePortalId() != null) {
|
||||
entryTag.putUUID(BLUE_KEY, entry.getValue().bluePortalId());
|
||||
}
|
||||
if (entry.getValue().orangePortalId() != null) {
|
||||
entryTag.putUUID(ORANGE_KEY, entry.getValue().orangePortalId());
|
||||
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());
|
||||
}
|
||||
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),
|
||||
@@ -61,35 +107,95 @@ public final class PortalGunSavedData extends SavedData {
|
||||
);
|
||||
}
|
||||
|
||||
public static Optional<UUID> getPortalId(MinecraftServer server, UUID owner, PortalGunPortalEntity.PortalSide side) {
|
||||
PairRecord record = get(server).pairs.get(owner);
|
||||
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));
|
||||
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 boolean isRegistered(MinecraftServer server, UUID owner, PortalGunPortalEntity.PortalSide side, UUID portalId) {
|
||||
return getPortalId(server, owner, side).map(portalId::equals).orElse(false);
|
||||
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 void setPortal(MinecraftServer server, UUID owner, PortalGunPortalEntity.PortalSide side, UUID portalId) {
|
||||
public static Optional<UUID> getGunIdForPortal(MinecraftServer server, UUID owner, ResourceLocation dimension, UUID portalId) {
|
||||
PortalGunSavedData data = get(server);
|
||||
PairRecord current = data.pairs.getOrDefault(owner, new PairRecord(null, null));
|
||||
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());
|
||||
}
|
||||
}
|
||||
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);
|
||||
PairRecord current = data.pairs.getOrDefault(key, new PairRecord(null, null));
|
||||
PairRecord updated = side == PortalGunPortalEntity.PortalSide.BLUE
|
||||
? new PairRecord(portalId, current.orangePortalId())
|
||||
: new PairRecord(current.bluePortalId(), portalId);
|
||||
if (updated.bluePortalId() == null && updated.orangePortalId() == null) {
|
||||
data.pairs.remove(owner);
|
||||
data.pairs.remove(key);
|
||||
} else {
|
||||
data.pairs.put(owner, updated);
|
||||
data.pairs.put(key, updated);
|
||||
}
|
||||
data.setDirty();
|
||||
}
|
||||
|
||||
public static void clearPortal(MinecraftServer server, UUID owner, PortalGunPortalEntity.PortalSide side, UUID 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 owner, UUID gunId, String channelName, PortalGunPortalEntity.PortalSide side, UUID portalId, ResourceLocation dimension) {
|
||||
PortalGunSavedData data = get(server);
|
||||
PairRecord current = data.pairs.get(owner);
|
||||
UUID resolvedGunId = data.resolveChannelGunId(owner, channelName, gunId);
|
||||
data.migrateLegacyPair(server, owner);
|
||||
data.adoptLegacyPairs(server, owner, resolvedGunId, channelName);
|
||||
PairKey key = new PairKey(owner, dimension, resolvedGunId);
|
||||
PairRecord current = data.pairs.get(key);
|
||||
if (current == null) {
|
||||
return;
|
||||
}
|
||||
@@ -102,48 +208,247 @@ public final class PortalGunSavedData extends SavedData {
|
||||
orange = null;
|
||||
}
|
||||
if (blue == null && orange == null) {
|
||||
data.pairs.remove(owner);
|
||||
data.pairs.remove(key);
|
||||
} else {
|
||||
data.pairs.put(owner, new PairRecord(blue, orange));
|
||||
data.pairs.put(key, new PairRecord(blue, orange));
|
||||
}
|
||||
data.setDirty();
|
||||
}
|
||||
|
||||
public static void clearAllPortals(MinecraftServer server, UUID owner) {
|
||||
public static void clearAllPortals(ServerLevel level, UUID owner, UUID gunId) {
|
||||
clearAllPortals(level, owner, gunId, String.valueOf(gunId));
|
||||
}
|
||||
|
||||
public static void clearAllPortals(ServerLevel level, UUID owner, UUID gunId, String channelName) {
|
||||
MinecraftServer server = level.getServer();
|
||||
PortalGunSavedData data = get(server);
|
||||
PairRecord current = data.pairs.remove(owner);
|
||||
data.setDirty();
|
||||
if (current == null) {
|
||||
return;
|
||||
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)) {
|
||||
return false;
|
||||
}
|
||||
addPortalIds(portalIds, entry.getValue());
|
||||
return true;
|
||||
});
|
||||
data.setDirty();
|
||||
for (UUID portalId : portalIds) {
|
||||
discardIfLoaded(server, portalId);
|
||||
}
|
||||
discardIfLoaded(server, current.bluePortalId());
|
||||
discardIfLoaded(server, current.orangePortalId());
|
||||
}
|
||||
|
||||
public static PortalGunPortalEntity findLoadedPortal(MinecraftServer server, UUID owner, PortalGunPortalEntity.PortalSide side) {
|
||||
Optional<UUID> portalId = getPortalId(server, owner, side);
|
||||
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 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)) {
|
||||
continue;
|
||||
}
|
||||
PairRecord pair = entry.getValue();
|
||||
UUID removed = side == PortalGunPortalEntity.PortalSide.BLUE ? pair.bluePortalId() : pair.orangePortalId();
|
||||
if (removed == null) {
|
||||
continue;
|
||||
}
|
||||
portalIds.add(removed);
|
||||
PairRecord remaining = 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);
|
||||
}
|
||||
}
|
||||
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());
|
||||
if (portalId.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
for (ServerLevel level : server.getAllLevels()) {
|
||||
Entity entity = level.getEntity(portalId.get());
|
||||
if (entity instanceof PortalGunPortalEntity portal && !portal.isRemoved()) {
|
||||
return portal;
|
||||
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);
|
||||
} else {
|
||||
this.legacyPairs.put(owner, new PairRecord(legacyBlue, legacyOrange));
|
||||
}
|
||||
this.setDirty();
|
||||
}
|
||||
}
|
||||
|
||||
private void adoptLegacyPairs(MinecraftServer server, UUID owner, UUID gunId, String channelName) {
|
||||
if (gunId == null) {
|
||||
return;
|
||||
}
|
||||
boolean changed = false;
|
||||
for (Map.Entry<PairKey, PairRecord> entry : new HashMap<>(this.pairs).entrySet()) {
|
||||
PairKey legacyKey = entry.getKey();
|
||||
if (!legacyKey.owner().equals(owner) || legacyKey.gunId() != null) {
|
||||
continue;
|
||||
}
|
||||
PairKey ownedKey = new PairKey(owner, legacyKey.dimension(), gunId);
|
||||
if (this.pairs.containsKey(ownedKey)) {
|
||||
continue;
|
||||
}
|
||||
this.pairs.remove(legacyKey);
|
||||
this.pairs.put(ownedKey, entry.getValue());
|
||||
synchronizeLoadedPortal(server, owner, legacyKey.dimension(), entry.getValue().bluePortalId(), gunId, channelName);
|
||||
synchronizeLoadedPortal(server, owner, legacyKey.dimension(), entry.getValue().orangePortalId(), gunId, channelName);
|
||||
changed = true;
|
||||
}
|
||||
if (changed) {
|
||||
this.setDirty();
|
||||
}
|
||||
}
|
||||
|
||||
private static void synchronizeLoadedPortal(MinecraftServer server, UUID owner, ResourceLocation dimension, UUID portalId, UUID gunId, String channelName) {
|
||||
if (portalId == null) {
|
||||
return;
|
||||
}
|
||||
ServerLevel level = server.getLevel(net.minecraft.resources.ResourceKey.create(net.minecraft.core.registries.Registries.DIMENSION, dimension));
|
||||
if (level == null || !(level.getEntity(portalId) instanceof PortalGunPortalEntity portal) || !owner.equals(portal.getOwnerId())) {
|
||||
return;
|
||||
}
|
||||
portal.adoptChannelIdentity(gunId, channelName);
|
||||
}
|
||||
|
||||
private static PairRecord withPortal(PairRecord current, PortalGunPortalEntity.PortalSide side, UUID portalId) {
|
||||
PairRecord pair = current == null ? new PairRecord(null, null) : current;
|
||||
return side == PortalGunPortalEntity.PortalSide.BLUE
|
||||
? new PairRecord(pair.bluePortalId() == null ? portalId : pair.bluePortalId(), pair.orangePortalId())
|
||||
: new PairRecord(pair.bluePortalId(), pair.orangePortalId() == null ? portalId : pair.orangePortalId());
|
||||
}
|
||||
|
||||
private static ServerLevel findPortalLevel(MinecraftServer server, UUID portalId) {
|
||||
if (portalId == null) {
|
||||
return null;
|
||||
}
|
||||
for (ServerLevel level : server.getAllLevels()) {
|
||||
if (level.getEntity(portalId) instanceof PortalGunPortalEntity portal && !portal.isRemoved()) {
|
||||
return level;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void addPortalIds(Set<UUID> portalIds, PairRecord pair) {
|
||||
if (pair.bluePortalId() != null) {
|
||||
portalIds.add(pair.bluePortalId());
|
||||
}
|
||||
if (pair.orangePortalId() != null) {
|
||||
portalIds.add(pair.orangePortalId());
|
||||
}
|
||||
}
|
||||
|
||||
private static void discardIfLoaded(MinecraftServer server, UUID portalId) {
|
||||
if (portalId == null) {
|
||||
return;
|
||||
}
|
||||
for (ServerLevel level : server.getAllLevels()) {
|
||||
if (level.getEntity(portalId) instanceof PortalGunPortalEntity portal) {
|
||||
ServerLevel level = findPortalLevel(server, portalId);
|
||||
if (level != null && level.getEntity(portalId) instanceof PortalGunPortalEntity portal) {
|
||||
portal.discard();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private record PairKey(UUID owner, ResourceLocation dimension, UUID gunId) {
|
||||
}
|
||||
|
||||
private record OwnerChannelKey(UUID owner, String channelName) {
|
||||
}
|
||||
|
||||
private record PairRecord(UUID bluePortalId, UUID orangePortalId) {
|
||||
|
||||
+14
-14
@@ -14,19 +14,19 @@ public final class PortalGunTransformUtil {
|
||||
}
|
||||
|
||||
public static Quaternionf createTransform(PortalGunPortalEntity sourcePortal, PortalGunPortalEntity destinationPortal) {
|
||||
return createTransform(sourcePortal.getWidthVec(), sourcePortal.getUpVec(), sourcePortal.getNormalVec().scale(-1.0D), destinationPortal.getWidthVec(), destinationPortal.getUpVec(), destinationPortal.getNormalVec());
|
||||
return createTransform(sourcePortal.getWidthVec(), sourcePortal.getUpVec(), sourcePortal.getNormalVec().scale(-1.0D), destinationPortal.getWidthVec().scale(-1.0D), destinationPortal.getUpVec(), destinationPortal.getNormalVec());
|
||||
}
|
||||
|
||||
public static Quaternionf createTransform(Vec3 sourceWidth, Vec3 sourceUp, Vec3 sourceForward, Vec3 destinationWidth, Vec3 destinationUp, Vec3 destinationForward) {
|
||||
Matrix3f sourceBasis = new Matrix3f(
|
||||
(float) sourceWidth.x, (float) sourceUp.x, (float) sourceForward.x,
|
||||
(float) sourceWidth.y, (float) sourceUp.y, (float) sourceForward.y,
|
||||
(float) sourceWidth.z, (float) sourceUp.z, (float) sourceForward.z
|
||||
(float) sourceWidth.x, (float) sourceWidth.y, (float) sourceWidth.z,
|
||||
(float) sourceUp.x, (float) sourceUp.y, (float) sourceUp.z,
|
||||
(float) sourceForward.x, (float) sourceForward.y, (float) sourceForward.z
|
||||
);
|
||||
Matrix3f destinationBasis = new Matrix3f(
|
||||
(float) destinationWidth.x, (float) destinationUp.x, (float) destinationForward.x,
|
||||
(float) destinationWidth.y, (float) destinationUp.y, (float) destinationForward.y,
|
||||
(float) destinationWidth.z, (float) destinationUp.z, (float) destinationForward.z
|
||||
(float) destinationWidth.x, (float) destinationWidth.y, (float) destinationWidth.z,
|
||||
(float) destinationUp.x, (float) destinationUp.y, (float) destinationUp.z,
|
||||
(float) destinationForward.x, (float) destinationForward.y, (float) destinationForward.z
|
||||
);
|
||||
Matrix3f transform = destinationBasis.mul(sourceBasis.transpose(new Matrix3f()), new Matrix3f());
|
||||
return transform.getNormalizedRotation(new Quaternionf());
|
||||
@@ -48,9 +48,9 @@ public final class PortalGunTransformUtil {
|
||||
Vec3 destinationWidth = destinationPortal.getWidthVec().normalize();
|
||||
Vec3 destinationUp = destinationPortal.getUpVec().normalize();
|
||||
Vec3 destinationNormal = destinationPortal.getNormalVec().normalize();
|
||||
return destinationWidth.scale(relativePosition.dot(sourceWidth))
|
||||
return destinationWidth.scale(-relativePosition.dot(sourceWidth))
|
||||
.add(destinationUp.scale(relativePosition.dot(sourceUp)))
|
||||
.add(destinationNormal.scale(relativePosition.dot(sourceNormal)));
|
||||
.add(destinationNormal.scale(-relativePosition.dot(sourceNormal)));
|
||||
}
|
||||
|
||||
public static Vec3 transformVector(PortalGunPortalEntity sourcePortal, PortalGunPortalEntity destinationPortal, Vec3 vector) {
|
||||
@@ -60,7 +60,7 @@ public final class PortalGunTransformUtil {
|
||||
Vec3 destinationWidth = destinationPortal.getWidthVec().normalize();
|
||||
Vec3 destinationUp = destinationPortal.getUpVec().normalize();
|
||||
Vec3 destinationOut = destinationPortal.getNormalVec().normalize();
|
||||
return destinationWidth.scale(vector.dot(sourceWidth))
|
||||
return destinationWidth.scale(-vector.dot(sourceWidth))
|
||||
.add(destinationUp.scale(vector.dot(sourceUp)))
|
||||
.add(destinationOut.scale(vector.dot(sourceInto)));
|
||||
}
|
||||
@@ -102,11 +102,11 @@ public final class PortalGunTransformUtil {
|
||||
public static Quaternionf orientationQuaternion(Vec3 look, Vec3 up) {
|
||||
Vec3 forward = look.normalize();
|
||||
Vec3 correctedUp = up.subtract(forward.scale(up.dot(forward))).normalize();
|
||||
Vec3 right = correctedUp.cross(forward).normalize();
|
||||
Vec3 right = forward.cross(correctedUp).normalize();
|
||||
Matrix3f basis = new Matrix3f(
|
||||
(float) right.x, (float) correctedUp.x, (float) (-forward.x),
|
||||
(float) right.y, (float) correctedUp.y, (float) (-forward.y),
|
||||
(float) right.z, (float) correctedUp.z, (float) (-forward.z)
|
||||
(float) right.x, (float) right.y, (float) right.z,
|
||||
(float) correctedUp.x, (float) correctedUp.y, (float) correctedUp.z,
|
||||
(float) -forward.x, (float) -forward.y, (float) -forward.z
|
||||
);
|
||||
return basis.getNormalizedRotation(new Quaternionf());
|
||||
}
|
||||
|
||||
+8
-9
@@ -35,6 +35,11 @@ public record PortalGunWorldPortalShape(
|
||||
return this.createPlaneAround(offset, this.halfDepth);
|
||||
}
|
||||
|
||||
public PortalGunWorldPortalShape scaleAperture(double scale) {
|
||||
double safeScale = Math.max(0.0D, scale);
|
||||
return new PortalGunWorldPortalShape(this.center, this.normal, this.up, this.right, this.halfWidth * safeScale, this.halfHeight * safeScale, this.halfDepth);
|
||||
}
|
||||
|
||||
public Vec3[] getCorners(double normalOffset) {
|
||||
Vec3 center = this.center.add(this.normal.scale(normalOffset));
|
||||
Vec3 width = this.right.scale(this.halfWidth);
|
||||
@@ -76,13 +81,7 @@ public record PortalGunWorldPortalShape(
|
||||
}
|
||||
|
||||
public boolean shouldRenderFront(Vec3 cameraPos) {
|
||||
PortalLocalCoords coords = this.localCoords(cameraPos);
|
||||
if (coords.depth() <= -0.25D) {
|
||||
return false;
|
||||
}
|
||||
double depthScale = Math.min(DEFAULT_SCAN_DISTANCE, Math.max(0.0D, coords.depth()));
|
||||
return Math.abs(coords.horizontal()) <= this.halfWidth + depthScale
|
||||
&& Math.abs(coords.vertical()) <= this.halfHeight + depthScale;
|
||||
return this.localCoords(cameraPos).depth() > 0.0D;
|
||||
}
|
||||
|
||||
public boolean intersectsFront(AABB box, double tolerance) {
|
||||
@@ -128,8 +127,8 @@ public record PortalGunWorldPortalShape(
|
||||
double horizontalOuter = this.halfWidth + borderThickness;
|
||||
double verticalOuter = this.halfHeight + borderThickness;
|
||||
return List.of(
|
||||
this.createLocalBox(-horizontalOuter, this.halfWidth, -verticalOuter, verticalOuter, -this.halfDepth, this.halfDepth),
|
||||
this.createLocalBox(-this.halfWidth, horizontalOuter, -verticalOuter, verticalOuter, -this.halfDepth, this.halfDepth),
|
||||
this.createLocalBox(-horizontalOuter, -this.halfWidth, -verticalOuter, verticalOuter, -this.halfDepth, this.halfDepth),
|
||||
this.createLocalBox(this.halfWidth, horizontalOuter, -verticalOuter, verticalOuter, -this.halfDepth, this.halfDepth),
|
||||
this.createLocalBox(-this.halfWidth, this.halfWidth, -verticalOuter, -this.halfHeight, -this.halfDepth, this.halfDepth),
|
||||
this.createLocalBox(-this.halfWidth, this.halfWidth, this.halfHeight, verticalOuter, -this.halfDepth, this.halfDepth)
|
||||
);
|
||||
|
||||
+6
@@ -95,6 +95,12 @@ public class TimeDilationFieldEntity extends Entity {
|
||||
|| this.anchorId != null && this.anchorId.equals(entity.getUUID())) {
|
||||
return false;
|
||||
}
|
||||
if (this.ownerId != null && this.level() instanceof ServerLevel level
|
||||
&& level.getEntity(this.ownerId) instanceof QueenEntity
|
||||
&& entity instanceof LivingEntity living
|
||||
&& !com.craisinlord.antarchy.content.effect.RoyalEffectEligibility.canApplyDilated(living)) {
|
||||
return false;
|
||||
}
|
||||
if (this.chronosphere && !(entity instanceof LivingEntity || entity instanceof Projectile || entity instanceof ItemEntity)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
+1
-2
@@ -146,8 +146,7 @@ public final class TimeDilationManager {
|
||||
if (!(entity instanceof TimeDilationEntityAccess access) || entity instanceof TimeDilationFieldEntity) {
|
||||
continue;
|
||||
}
|
||||
if (entity.getType().is(com.craisinlord.antarchy.content.AntarchyTags.Entities.TIME_DILATION_IMMUNE)
|
||||
|| (entity instanceof net.minecraft.world.entity.LivingEntity living && !RoyalEffectEligibility.canApplyDilated(living))) {
|
||||
if (entity.getType().is(com.craisinlord.antarchy.content.AntarchyTags.Entities.TIME_DILATION_IMMUNE)) {
|
||||
if (RoyalEffectHooks.dilatedHolder() != null && entity instanceof net.minecraft.world.entity.LivingEntity living) {
|
||||
living.removeEffect(RoyalEffectHooks.dilatedHolder());
|
||||
}
|
||||
|
||||
+50
-2
@@ -2,9 +2,11 @@ package com.craisinlord.antarchy.content.worldgen.thoraxis;
|
||||
|
||||
import com.craisinlord.antarchy.Antarchy;
|
||||
import com.mojang.serialization.Codec;
|
||||
import com.mojang.serialization.DataResult;
|
||||
import com.mojang.serialization.MapCodec;
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder;
|
||||
import it.unimi.dsi.fastutil.longs.Long2DoubleOpenHashMap;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Holder;
|
||||
@@ -56,7 +58,8 @@ public final class ThoraxisBiomeSource extends BiomeSource {
|
||||
Codec.INT.optionalFieldOf("dream_dunes_max_y", 160).forGetter(ThoraxisBiomeSource::dreamDunesMaxY),
|
||||
Codec.INT.optionalFieldOf("umbral_hills_max_y", 160).forGetter(ThoraxisBiomeSource::umbralHillsMaxY),
|
||||
Codec.INT.optionalFieldOf("lucid_pools_min_y", 116).forGetter(ThoraxisBiomeSource::lucidPoolsMinY),
|
||||
Codec.INT.optionalFieldOf("nadir_forest_min_y", 128).forGetter(ThoraxisBiomeSource::nadirForestMinY)
|
||||
Codec.INT.optionalFieldOf("nadir_forest_min_y", 128).forGetter(ThoraxisBiomeSource::nadirForestMinY),
|
||||
RegionalBiomeOverlay.CODEC.listOf().optionalFieldOf("regional_overlays", List.of()).forGetter(ThoraxisBiomeSource::regionalOverlays)
|
||||
).apply(instance, ThoraxisBiomeSource::new));
|
||||
|
||||
private final Climate.ParameterList<Holder<Biome>> parameters;
|
||||
@@ -65,6 +68,7 @@ public final class ThoraxisBiomeSource extends BiomeSource {
|
||||
private final int umbralHillsMaxY;
|
||||
private final int lucidPoolsMinY;
|
||||
private final int nadirForestMinY;
|
||||
private final List<RegionalBiomeOverlay> regionalOverlays;
|
||||
private final int dreamDunesMaxQuartY;
|
||||
private final int umbralHillsMaxQuartY;
|
||||
private final int lucidPoolsMinQuartY;
|
||||
@@ -76,19 +80,21 @@ public final class ThoraxisBiomeSource extends BiomeSource {
|
||||
private Holder<Biome> umbralHillsBiome;
|
||||
private Holder<Biome> lucidPoolsBiome;
|
||||
private Holder<Biome> nadirForestBiome;
|
||||
private List<ResolvedRegionalBiomeOverlay> resolvedRegionalOverlays = List.of();
|
||||
|
||||
private static final ThreadLocal<Long2DoubleOpenHashMap> REGION_CACHE =
|
||||
ThreadLocal.withInitial(() -> new Long2DoubleOpenHashMap(512));
|
||||
private static final ThreadLocal<Long2DoubleOpenHashMap> VERTICAL_CACHE =
|
||||
ThreadLocal.withInitial(() -> new Long2DoubleOpenHashMap(64));
|
||||
|
||||
public ThoraxisBiomeSource(Climate.ParameterList<Holder<Biome>> parameters, int dreamDunesMaxY, int umbralHillsMaxY, int lucidPoolsMinY, int nadirForestMinY) {
|
||||
public ThoraxisBiomeSource(Climate.ParameterList<Holder<Biome>> parameters, int dreamDunesMaxY, int umbralHillsMaxY, int lucidPoolsMinY, int nadirForestMinY, List<RegionalBiomeOverlay> regionalOverlays) {
|
||||
this.parameters = parameters;
|
||||
this.delegate = MultiNoiseBiomeSource.createFromList(parameters);
|
||||
this.dreamDunesMaxY = dreamDunesMaxY;
|
||||
this.umbralHillsMaxY = umbralHillsMaxY;
|
||||
this.lucidPoolsMinY = lucidPoolsMinY;
|
||||
this.nadirForestMinY = nadirForestMinY;
|
||||
this.regionalOverlays = regionalOverlays;
|
||||
this.dreamDunesMaxQuartY = QuartPos.fromBlock(dreamDunesMaxY);
|
||||
this.umbralHillsMaxQuartY = QuartPos.fromBlock(umbralHillsMaxY);
|
||||
this.lucidPoolsMinQuartY = QuartPos.fromBlock(lucidPoolsMinY);
|
||||
@@ -109,6 +115,7 @@ public final class ThoraxisBiomeSource extends BiomeSource {
|
||||
this.umbralHillsBiome = this.findBiome(UMBRAL_HILLS);
|
||||
this.lucidPoolsBiome = this.findBiome(LUCID_POOLS);
|
||||
this.nadirForestBiome = this.findBiome(NADIR_FOREST);
|
||||
this.resolvedRegionalOverlays = this.resolveRegionalOverlays();
|
||||
this.biomesResolved = true;
|
||||
}
|
||||
}
|
||||
@@ -133,6 +140,10 @@ public final class ThoraxisBiomeSource extends BiomeSource {
|
||||
return this.umbralHillsMaxY;
|
||||
}
|
||||
|
||||
private List<RegionalBiomeOverlay> regionalOverlays() {
|
||||
return this.regionalOverlays;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MapCodec<? extends BiomeSource> codec() {
|
||||
return CODEC;
|
||||
@@ -179,6 +190,12 @@ public final class ThoraxisBiomeSource extends BiomeSource {
|
||||
return this.umbralHillsBiome;
|
||||
}
|
||||
|
||||
for (ResolvedRegionalBiomeOverlay overlay : this.resolvedRegionalOverlays) {
|
||||
if (region >= overlay.minNoise() && region < overlay.maxNoise()) {
|
||||
return overlay.biome();
|
||||
}
|
||||
}
|
||||
|
||||
Holder<Biome> delegateBiome = this.delegate.getNoiseBiome(x, y, z, sampler);
|
||||
return this.nightmareWastesBiome != null ? this.nightmareWastesBiome : delegateBiome;
|
||||
}
|
||||
@@ -198,6 +215,37 @@ public final class ThoraxisBiomeSource extends BiomeSource {
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
private Holder<Biome> findBiome(ResourceLocation location) {
|
||||
ResourceKey<Biome> key = ResourceKey.create(Registries.BIOME, location);
|
||||
return this.findBiome(key);
|
||||
}
|
||||
|
||||
private List<ResolvedRegionalBiomeOverlay> resolveRegionalOverlays() {
|
||||
return this.regionalOverlays.stream().map(overlay -> {
|
||||
Holder<Biome> biome = this.findBiome(overlay.biome());
|
||||
if (biome == null) {
|
||||
throw new IllegalStateException("Thoraxis regional overlay biome " + overlay.biome() + " must be listed in the biome source parameters");
|
||||
}
|
||||
return new ResolvedRegionalBiomeOverlay(biome, overlay.minNoise(), overlay.maxNoise());
|
||||
}).toList();
|
||||
}
|
||||
|
||||
private record RegionalBiomeOverlay(ResourceLocation biome, double minNoise, double maxNoise) {
|
||||
private static final Codec<RegionalBiomeOverlay> CODEC = RecordCodecBuilder.<RegionalBiomeOverlay>create(instance -> instance.group(
|
||||
ResourceLocation.CODEC.fieldOf("biome").forGetter(RegionalBiomeOverlay::biome),
|
||||
Codec.DOUBLE.fieldOf("min_noise").forGetter(RegionalBiomeOverlay::minNoise),
|
||||
Codec.DOUBLE.fieldOf("max_noise").forGetter(RegionalBiomeOverlay::maxNoise)
|
||||
).apply(instance, RegionalBiomeOverlay::new)).flatXmap(
|
||||
overlay -> overlay.minNoise() >= 0.0D && overlay.maxNoise() <= 1.0D && overlay.minNoise() < overlay.maxNoise()
|
||||
? DataResult.success(overlay)
|
||||
: DataResult.error(() -> "Regional biome overlay noise bounds must satisfy 0 <= min_noise < max_noise <= 1"),
|
||||
DataResult::success
|
||||
);
|
||||
}
|
||||
|
||||
private record ResolvedRegionalBiomeOverlay(Holder<Biome> biome, double minNoise, double maxNoise) {
|
||||
}
|
||||
|
||||
private double cachedRegionNoise(int quartX, int quartZ) {
|
||||
long key = net.minecraft.world.level.ChunkPos.asLong(quartX, quartZ);
|
||||
Long2DoubleOpenHashMap cache = REGION_CACHE.get();
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.craisinlord.antarchy.mixins;
|
||||
|
||||
import com.craisinlord.antarchy.content.client.renderer.SodiumCompat;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import org.objectweb.asm.tree.ClassNode;
|
||||
@@ -28,6 +29,17 @@ public final class AntarchyMixinPlugin implements IMixinConfigPlugin {
|
||||
if (!classPresent(targetClassName)) {
|
||||
return false;
|
||||
}
|
||||
if (SodiumCompat.isLoaded()
|
||||
&& (mixinClassName.endsWith(".LevelRendererPortalViewAreaAccessor")
|
||||
|| mixinClassName.endsWith(".LevelRendererPortalViewAreaMixin")
|
||||
|| mixinClassName.endsWith(".LevelRendererPortalChunkBudgetMixin")
|
||||
|| mixinClassName.endsWith(".PortalViewChunkCacheMixin")
|
||||
|| mixinClassName.endsWith(".PortalViewSectionSortingMixin"))) {
|
||||
return false;
|
||||
}
|
||||
if (mixinClassName.endsWith(".SodiumWorldRendererMixin")) {
|
||||
return SodiumCompat.isLoaded();
|
||||
}
|
||||
if (mixinClassName.startsWith("com.craisinlord.antarchy.mixins.compat.scguns.")) {
|
||||
return classPresent("top.ribs.scguns.entity.projectile.ProjectileEntity");
|
||||
}
|
||||
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
package com.craisinlord.antarchy.mixins;
|
||||
|
||||
import net.minecraft.world.entity.item.FallingBlockEntity;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
|
||||
@Mixin(FallingBlockEntity.class)
|
||||
public interface FallingBlockEntityAccessor {
|
||||
@Accessor("time")
|
||||
void antarchy$setTime(int time);
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.craisinlord.antarchy.mixins;
|
||||
|
||||
import com.craisinlord.antarchy.content.item.InnateEnchantmentHelper;
|
||||
import com.craisinlord.antarchy.content.item.GiantFryingPanItem;
|
||||
import net.minecraft.world.inventory.GrindstoneMenu;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
@@ -19,6 +20,11 @@ public abstract class GrindstoneMenuMixin {
|
||||
|
||||
@Inject(method = "computeResult", at = @At("RETURN"), cancellable = true)
|
||||
private void antarchy$blockInnateOnlyNoOp(ItemStack firstInput, ItemStack secondInput, CallbackInfoReturnable<ItemStack> cir) {
|
||||
if (firstInput.getItem() instanceof GiantFryingPanItem || secondInput.getItem() instanceof GiantFryingPanItem) {
|
||||
cir.setReturnValue(ItemStack.EMPTY);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!firstInput.isEmpty() && secondInput.isEmpty()
|
||||
&& InnateEnchantmentHelper.hasInnateNonCurseEnchantments(firstInput)
|
||||
&& !InnateEnchantmentHelper.hasRemovableNonCurseEnchantments(firstInput)) {
|
||||
|
||||
+29
-2
@@ -1,7 +1,7 @@
|
||||
package com.craisinlord.antarchy.mixins;
|
||||
|
||||
import com.craisinlord.antarchy.content.gravity.AntarchyGravityApi;
|
||||
import com.craisinlord.antarchy.content.portalgun.PortalGunCollisionHelper;
|
||||
import com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.phys.AABB;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
@@ -10,11 +10,37 @@ 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.CallbackInfoReturnable;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(Entity.class)
|
||||
public abstract class PortalGunEntityCollisionMixin {
|
||||
@Unique
|
||||
private AABB antarchy$portalGunStartBox;
|
||||
@Unique
|
||||
private AABB antarchy$portalGunMoveStartBox;
|
||||
@Unique
|
||||
private Vec3 antarchy$portalGunMoveStartPosition;
|
||||
|
||||
@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();
|
||||
}
|
||||
|
||||
@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)
|
||||
);
|
||||
}
|
||||
this.antarchy$portalGunMoveStartBox = null;
|
||||
this.antarchy$portalGunMoveStartPosition = null;
|
||||
}
|
||||
|
||||
@Inject(method = "collide", at = @At("HEAD"))
|
||||
private void antarchy$capturePortalGunStartBox(Vec3 movement, CallbackInfoReturnable<Vec3> cir) {
|
||||
@@ -24,10 +50,11 @@ public abstract class PortalGunEntityCollisionMixin {
|
||||
@Inject(method = "collide", at = @At("RETURN"), cancellable = true)
|
||||
private void antarchy$restorePortalCrossingMotion(Vec3 movement, CallbackInfoReturnable<Vec3> cir) {
|
||||
Entity entity = (Entity) (Object) this;
|
||||
if (this.antarchy$portalGunStartBox == null || AntarchyGravityApi.isGravityInverted(entity)) {
|
||||
if (this.antarchy$portalGunStartBox == null) {
|
||||
return;
|
||||
}
|
||||
Vec3 adjusted = PortalGunCollisionHelper.resolveCollision(entity, this.antarchy$portalGunStartBox, movement, cir.getReturnValue());
|
||||
cir.setReturnValue(adjusted);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.craisinlord.antarchy.mixins;
|
||||
|
||||
import com.craisinlord.antarchy.content.item.PortalGunItem;
|
||||
import com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.entity.monster.Zombie;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(Zombie.class)
|
||||
public abstract class PortalGunZombieMixin {
|
||||
@Inject(method = "tick", at = @At("TAIL"))
|
||||
private void antarchy$firePortalGun(CallbackInfo ci) {
|
||||
Zombie zombie = (Zombie) (Object) this;
|
||||
if (!(zombie.level() instanceof ServerLevel serverLevel)) {
|
||||
return;
|
||||
}
|
||||
ItemStack stack = zombie.getMainHandItem();
|
||||
if (stack.getItem() instanceof PortalGunItem portalGun && zombie.getRandom().nextFloat() < 0.008F) {
|
||||
PortalGunPortalEntity.PortalSide side = zombie.getRandom().nextBoolean()
|
||||
? PortalGunPortalEntity.PortalSide.BLUE
|
||||
: PortalGunPortalEntity.PortalSide.ORANGE;
|
||||
portalGun.fireMobPortal(serverLevel, zombie, stack, side);
|
||||
}
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package com.craisinlord.antarchy.mixins.client;
|
||||
|
||||
import net.minecraft.client.Camera;
|
||||
import org.joml.Vector3f;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
|
||||
@Mixin(Camera.class)
|
||||
public interface CameraBasisAccessor {
|
||||
@Accessor("forwards")
|
||||
Vector3f antarchy$getForwards();
|
||||
|
||||
@Accessor("up")
|
||||
Vector3f antarchy$getUp();
|
||||
|
||||
@Accessor("left")
|
||||
Vector3f antarchy$getLeft();
|
||||
}
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
package com.craisinlord.antarchy.mixins.client;
|
||||
|
||||
import com.craisinlord.antarchy.content.client.renderer.PortalGunPortalRendererPool;
|
||||
import net.minecraft.client.multiplayer.ClientLevel;
|
||||
import net.minecraft.client.renderer.LevelRenderer;
|
||||
import net.minecraft.client.renderer.chunk.RenderRegionCache;
|
||||
import net.minecraft.client.renderer.chunk.SectionRenderDispatcher;
|
||||
import net.minecraft.world.level.lighting.LevelLightEngine;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
|
||||
@Mixin(LevelRenderer.class)
|
||||
public abstract class LevelRendererPortalChunkBudgetMixin {
|
||||
@Redirect(
|
||||
method = "renderLevel",
|
||||
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/multiplayer/ClientLevel;pollLightUpdates()V")
|
||||
)
|
||||
private void antarchy$skipProxyLightQueuePoll(ClientLevel level) {
|
||||
if (!PortalGunPortalRendererPool.isProxy((LevelRenderer) (Object) this)) {
|
||||
level.pollLightUpdates();
|
||||
}
|
||||
}
|
||||
|
||||
@Redirect(
|
||||
method = "renderLevel",
|
||||
at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/lighting/LevelLightEngine;runLightUpdates()I")
|
||||
)
|
||||
private int antarchy$skipProxyLightQueueDrain(LevelLightEngine lightEngine) {
|
||||
return PortalGunPortalRendererPool.isProxy((LevelRenderer) (Object) this) ? 0 : lightEngine.runLightUpdates();
|
||||
}
|
||||
|
||||
@Redirect(
|
||||
method = "compileSections",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/client/renderer/chunk/SectionRenderDispatcher;rebuildSectionSync(Lnet/minecraft/client/renderer/chunk/SectionRenderDispatcher$RenderSection;Lnet/minecraft/client/renderer/chunk/RenderRegionCache;)V"
|
||||
)
|
||||
)
|
||||
private void antarchy$queuePortalSyncRebuilds(SectionRenderDispatcher dispatcher, SectionRenderDispatcher.RenderSection section, RenderRegionCache cache) {
|
||||
PortalGunPortalRendererPool.rebuildSection((LevelRenderer) (Object) this, dispatcher, section, cache);
|
||||
}
|
||||
|
||||
@Redirect(
|
||||
method = "compileSections",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/client/renderer/chunk/SectionRenderDispatcher$RenderSection;rebuildSectionAsync(Lnet/minecraft/client/renderer/chunk/SectionRenderDispatcher;Lnet/minecraft/client/renderer/chunk/RenderRegionCache;)V"
|
||||
)
|
||||
)
|
||||
private void antarchy$budgetPortalAsyncRebuilds(SectionRenderDispatcher.RenderSection section, SectionRenderDispatcher dispatcher, RenderRegionCache cache) {
|
||||
PortalGunPortalRendererPool.rebuildSectionAsync((LevelRenderer) (Object) this, dispatcher, section, cache);
|
||||
}
|
||||
|
||||
@Redirect(
|
||||
method = "compileSections",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/client/renderer/chunk/SectionRenderDispatcher$RenderSection;setNotDirty()V"
|
||||
)
|
||||
)
|
||||
private void antarchy$keepUnscheduledPortalSectionsDirty(SectionRenderDispatcher.RenderSection section) {
|
||||
PortalGunPortalRendererPool.setNotDirty(section);
|
||||
}
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
package com.craisinlord.antarchy.mixins.client;
|
||||
|
||||
import com.craisinlord.antarchy.content.client.PortalGunPortalRenderState;
|
||||
import com.craisinlord.antarchy.content.client.renderer.PortalGunPortalViewAreaManager;
|
||||
import com.craisinlord.antarchy.content.client.renderer.PortalGunPortalRendererPool;
|
||||
import com.craisinlord.antarchy.content.client.renderer.PortalGunPortalViewRenderer;
|
||||
import com.craisinlord.antarchy.content.client.renderer.PortalStencilTarget;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.multiplayer.ClientLevel;
|
||||
import net.minecraft.client.renderer.LevelRenderer;
|
||||
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(LevelRenderer.class)
|
||||
public abstract class LevelRendererPortalLifecycleMixin {
|
||||
@Inject(method = "renderLevel", at = @At("HEAD"))
|
||||
private void antarchy$preparePortalStencil(CallbackInfo ci) {
|
||||
if (!PortalGunPortalRendererPool.isProxy((LevelRenderer) (Object) this)) {
|
||||
PortalStencilTarget.prepareMainTarget(Minecraft.getInstance().getMainRenderTarget());
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "setLevel", at = @At("HEAD"))
|
||||
private void antarchy$clearPortalViewsOnLevelChange(ClientLevel level, CallbackInfo ci) {
|
||||
if (PortalGunPortalRendererPool.isProxy((LevelRenderer) (Object) this)) {
|
||||
return;
|
||||
}
|
||||
PortalGunPortalRendererPool.clear();
|
||||
PortalGunPortalViewAreaManager.clear();
|
||||
}
|
||||
|
||||
@Inject(method = "close", at = @At("HEAD"))
|
||||
private void antarchy$clearPortalViewsOnClose(CallbackInfo ci) {
|
||||
if (PortalGunPortalRendererPool.isProxy((LevelRenderer) (Object) this)) {
|
||||
return;
|
||||
}
|
||||
PortalGunPortalRendererPool.clear();
|
||||
PortalGunPortalViewAreaManager.clear();
|
||||
PortalGunPortalViewRenderer.releaseTargets();
|
||||
}
|
||||
|
||||
@Inject(method = "allChanged", at = @At("TAIL"))
|
||||
private void antarchy$clearPortalViewsOnRendererRebuild(CallbackInfo ci) {
|
||||
if (PortalGunPortalRendererPool.isProxy((LevelRenderer) (Object) this)) {
|
||||
return;
|
||||
}
|
||||
if (PortalGunPortalRenderState.getContext() == null) {
|
||||
PortalGunPortalRendererPool.clear();
|
||||
PortalGunPortalViewAreaManager.clear();
|
||||
PortalGunPortalViewRenderer.releaseTargets();
|
||||
}
|
||||
}
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
package com.craisinlord.antarchy.mixins.client;
|
||||
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import net.minecraft.client.Camera;
|
||||
import net.minecraft.client.renderer.culling.Frustum;
|
||||
import net.minecraft.client.renderer.LevelRenderer;
|
||||
import net.minecraft.client.renderer.RenderType;
|
||||
import net.minecraft.client.renderer.MultiBufferSource;
|
||||
import net.minecraft.client.renderer.LightTexture;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import org.joml.Matrix4f;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Invoker;
|
||||
|
||||
@Mixin(LevelRenderer.class)
|
||||
public interface LevelRendererPortalSceneInvoker {
|
||||
@Invoker("setupRender")
|
||||
void antarchy$setupPortalScene(Camera camera, Frustum frustum, boolean capturedFrustum, boolean spectator);
|
||||
|
||||
@Invoker("compileSections")
|
||||
void antarchy$compilePortalSections(Camera camera);
|
||||
|
||||
@Invoker("renderSectionLayer")
|
||||
void antarchy$renderPortalSectionLayer(RenderType renderType, double cameraX, double cameraY, double cameraZ, Matrix4f modelViewMatrix, Matrix4f projectionMatrix);
|
||||
|
||||
@Invoker("renderEntity")
|
||||
void antarchy$renderPortalEntity(Entity entity, double cameraX, double cameraY, double cameraZ, float partialTick, PoseStack poseStack, MultiBufferSource bufferSource);
|
||||
|
||||
@Invoker("renderSnowAndRain")
|
||||
void antarchy$renderPortalWeather(LightTexture lightTexture, float partialTick, double cameraX, double cameraY, double cameraZ);
|
||||
|
||||
@Invoker("renderWorldBorder")
|
||||
void antarchy$renderPortalWorldBorder(Camera camera);
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
package com.craisinlord.antarchy.mixins.client;
|
||||
|
||||
import net.minecraft.client.renderer.LevelRenderer;
|
||||
import net.minecraft.client.renderer.PostChain;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
|
||||
@Mixin(LevelRenderer.class)
|
||||
public interface LevelRendererPortalTargetsAccessor {
|
||||
@Accessor("entityEffect")
|
||||
PostChain antarchy$getEntityEffect();
|
||||
|
||||
@Accessor("transparencyChain")
|
||||
PostChain antarchy$getTransparencyChain();
|
||||
}
|
||||
+160
@@ -0,0 +1,160 @@
|
||||
package com.craisinlord.antarchy.mixins.client;
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
||||
import com.mojang.blaze3d.vertex.VertexBuffer;
|
||||
import net.minecraft.client.CloudStatus;
|
||||
import net.minecraft.client.renderer.LevelRenderer;
|
||||
import net.minecraft.client.renderer.SectionOcclusionGraph;
|
||||
import net.minecraft.client.renderer.ViewArea;
|
||||
import net.minecraft.client.renderer.chunk.SectionRenderDispatcher;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import java.util.Set;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Mutable;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
|
||||
@Mixin(LevelRenderer.class)
|
||||
public interface LevelRendererPortalViewAreaAccessor {
|
||||
@Accessor("viewArea")
|
||||
ViewArea antarchy$getViewArea();
|
||||
|
||||
@Accessor("viewArea")
|
||||
void antarchy$setViewArea(ViewArea viewArea);
|
||||
|
||||
@Accessor("sectionOcclusionGraph")
|
||||
SectionOcclusionGraph antarchy$getSectionOcclusionGraph();
|
||||
|
||||
@Mutable
|
||||
@Accessor("sectionOcclusionGraph")
|
||||
void antarchy$setSectionOcclusionGraph(SectionOcclusionGraph graph);
|
||||
|
||||
@Accessor("visibleSections")
|
||||
ObjectArrayList<SectionRenderDispatcher.RenderSection> antarchy$getVisibleSections();
|
||||
|
||||
@Mutable
|
||||
@Accessor("visibleSections")
|
||||
void antarchy$setVisibleSections(ObjectArrayList<SectionRenderDispatcher.RenderSection> sections);
|
||||
|
||||
@Accessor("globalBlockEntities")
|
||||
Set<BlockEntity> antarchy$getGlobalBlockEntities();
|
||||
|
||||
@Mutable
|
||||
@Accessor("globalBlockEntities")
|
||||
void antarchy$setGlobalBlockEntities(Set<BlockEntity> blockEntities);
|
||||
|
||||
@Accessor("lastCameraSectionX")
|
||||
int antarchy$getLastCameraSectionX();
|
||||
|
||||
@Accessor("lastCameraSectionX")
|
||||
void antarchy$setLastCameraSectionX(int value);
|
||||
|
||||
@Accessor("lastCameraSectionY")
|
||||
int antarchy$getLastCameraSectionY();
|
||||
|
||||
@Accessor("lastCameraSectionY")
|
||||
void antarchy$setLastCameraSectionY(int value);
|
||||
|
||||
@Accessor("lastCameraSectionZ")
|
||||
int antarchy$getLastCameraSectionZ();
|
||||
|
||||
@Accessor("lastCameraSectionZ")
|
||||
void antarchy$setLastCameraSectionZ(int value);
|
||||
|
||||
@Accessor("prevCamX")
|
||||
double antarchy$getPrevCamX();
|
||||
|
||||
@Accessor("prevCamX")
|
||||
void antarchy$setPrevCamX(double value);
|
||||
|
||||
@Accessor("prevCamY")
|
||||
double antarchy$getPrevCamY();
|
||||
|
||||
@Accessor("prevCamY")
|
||||
void antarchy$setPrevCamY(double value);
|
||||
|
||||
@Accessor("prevCamZ")
|
||||
double antarchy$getPrevCamZ();
|
||||
|
||||
@Accessor("prevCamZ")
|
||||
void antarchy$setPrevCamZ(double value);
|
||||
|
||||
@Accessor("prevCamRotX")
|
||||
double antarchy$getPrevCamRotX();
|
||||
|
||||
@Accessor("prevCamRotX")
|
||||
void antarchy$setPrevCamRotX(double value);
|
||||
|
||||
@Accessor("prevCamRotY")
|
||||
double antarchy$getPrevCamRotY();
|
||||
|
||||
@Accessor("prevCamRotY")
|
||||
void antarchy$setPrevCamRotY(double value);
|
||||
|
||||
@Accessor("xTransparentOld")
|
||||
double antarchy$getTransparentSortX();
|
||||
|
||||
@Accessor("xTransparentOld")
|
||||
void antarchy$setTransparentSortX(double value);
|
||||
|
||||
@Accessor("yTransparentOld")
|
||||
double antarchy$getTransparentSortY();
|
||||
|
||||
@Accessor("yTransparentOld")
|
||||
void antarchy$setTransparentSortY(double value);
|
||||
|
||||
@Accessor("zTransparentOld")
|
||||
double antarchy$getTransparentSortZ();
|
||||
|
||||
@Accessor("zTransparentOld")
|
||||
void antarchy$setTransparentSortZ(double value);
|
||||
|
||||
@Accessor("cloudBuffer")
|
||||
VertexBuffer antarchy$getCloudBuffer();
|
||||
|
||||
@Accessor("cloudBuffer")
|
||||
void antarchy$setCloudBuffer(VertexBuffer cloudBuffer);
|
||||
|
||||
@Accessor("generateClouds")
|
||||
boolean antarchy$getGenerateClouds();
|
||||
|
||||
@Accessor("generateClouds")
|
||||
void antarchy$setGenerateClouds(boolean generateClouds);
|
||||
|
||||
@Accessor("prevCloudX")
|
||||
int antarchy$getPrevCloudX();
|
||||
|
||||
@Accessor("prevCloudX")
|
||||
void antarchy$setPrevCloudX(int value);
|
||||
|
||||
@Accessor("prevCloudY")
|
||||
int antarchy$getPrevCloudY();
|
||||
|
||||
@Accessor("prevCloudY")
|
||||
void antarchy$setPrevCloudY(int value);
|
||||
|
||||
@Accessor("prevCloudZ")
|
||||
int antarchy$getPrevCloudZ();
|
||||
|
||||
@Accessor("prevCloudZ")
|
||||
void antarchy$setPrevCloudZ(int value);
|
||||
|
||||
@Accessor("prevCloudColor")
|
||||
Vec3 antarchy$getPrevCloudColor();
|
||||
|
||||
@Accessor("prevCloudColor")
|
||||
void antarchy$setPrevCloudColor(Vec3 value);
|
||||
|
||||
@Accessor("prevCloudsType")
|
||||
CloudStatus antarchy$getPrevCloudsType();
|
||||
|
||||
@Accessor("prevCloudsType")
|
||||
void antarchy$setPrevCloudsType(CloudStatus value);
|
||||
|
||||
@Accessor("rainSoundTime")
|
||||
int antarchy$getRainSoundTime();
|
||||
|
||||
@Accessor("rainSoundTime")
|
||||
void antarchy$setRainSoundTime(int value);
|
||||
|
||||
}
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
package com.craisinlord.antarchy.mixins.client;
|
||||
|
||||
import com.craisinlord.antarchy.content.client.renderer.PortalGunPortalViewAreaManager;
|
||||
import com.craisinlord.antarchy.content.client.PortalGunPortalRenderState;
|
||||
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
|
||||
import net.minecraft.client.multiplayer.ClientLevel;
|
||||
import net.minecraft.client.renderer.LevelRenderer;
|
||||
import net.minecraft.client.renderer.chunk.SectionRenderDispatcher;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.sounds.SoundEvent;
|
||||
import net.minecraft.sounds.SoundSource;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
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.Redirect;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(LevelRenderer.class)
|
||||
public abstract class LevelRendererPortalViewAreaMixin {
|
||||
@Redirect(method = "renderSnowAndRain", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/multiplayer/ClientLevel;playLocalSound(Lnet/minecraft/core/BlockPos;Lnet/minecraft/sounds/SoundEvent;Lnet/minecraft/sounds/SoundSource;FFZ)V"), require = 0)
|
||||
private void antarchy$suppressPortalWeatherSound(ClientLevel level, BlockPos pos, SoundEvent sound, SoundSource source, float volume, float pitch, boolean distanceDelay) {
|
||||
if (PortalGunPortalRenderState.getContext() == null) {
|
||||
level.playLocalSound(pos, sound, source, volume, pitch, distanceDelay);
|
||||
}
|
||||
}
|
||||
|
||||
@ModifyExpressionValue(method = "setupRender", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/player/LocalPlayer;getX()D"), require = 0)
|
||||
private double antarchy$usePortalCameraX(double original) {
|
||||
PortalGunPortalRenderState.PortalRenderContext context = PortalGunPortalRenderState.getContext();
|
||||
return context == null ? original : context.cameraPos().x;
|
||||
}
|
||||
|
||||
@ModifyExpressionValue(method = "setupRender", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/player/LocalPlayer;getY()D"), require = 0)
|
||||
private double antarchy$usePortalCameraY(double original) {
|
||||
PortalGunPortalRenderState.PortalRenderContext context = PortalGunPortalRenderState.getContext();
|
||||
return context == null ? original : context.cameraPos().y;
|
||||
}
|
||||
|
||||
@ModifyExpressionValue(method = "setupRender", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/player/LocalPlayer;getZ()D"), require = 0)
|
||||
private double antarchy$usePortalCameraZ(double original) {
|
||||
PortalGunPortalRenderState.PortalRenderContext context = PortalGunPortalRenderState.getContext();
|
||||
return context == null ? original : context.cameraPos().z;
|
||||
}
|
||||
|
||||
@Inject(method = "addRecentlyCompiledSection", at = @At("HEAD"), cancellable = true)
|
||||
private void antarchy$routePortalSectionCompile(SectionRenderDispatcher.RenderSection section, CallbackInfo ci) {
|
||||
if (PortalGunPortalViewAreaManager.routeSectionCompiled(section)) {
|
||||
ci.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "setSectionDirty(IIIZ)V", at = @At("TAIL"))
|
||||
private void antarchy$dirtyPortalSection(int sectionX, int sectionY, int sectionZ, boolean playerChanged, CallbackInfo ci) {
|
||||
PortalGunPortalViewAreaManager.onSectionDirty(sectionX, sectionY, sectionZ, playerChanged);
|
||||
}
|
||||
|
||||
@Inject(method = "onChunkLoaded", at = @At("TAIL"))
|
||||
private void antarchy$updatePortalChunkGraph(ChunkPos pos, CallbackInfo ci) {
|
||||
PortalGunPortalViewAreaManager.onChunkLoaded(pos);
|
||||
}
|
||||
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
package com.craisinlord.antarchy.mixins.client;
|
||||
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Mutable;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
|
||||
@Mixin(LivingEntity.class)
|
||||
public interface LivingEntityPortalLerpAccessor {
|
||||
@Mutable
|
||||
@Accessor("lerpSteps")
|
||||
void antarchy$setLerpSteps(int steps);
|
||||
|
||||
@Mutable
|
||||
@Accessor("lerpHeadSteps")
|
||||
void antarchy$setLerpHeadSteps(int steps);
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
package com.craisinlord.antarchy.mixins.client;
|
||||
|
||||
import com.mojang.blaze3d.pipeline.RenderTarget;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Mutable;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
|
||||
@Mixin(Minecraft.class)
|
||||
public interface MinecraftMainRenderTargetAccessor {
|
||||
@Mutable
|
||||
@Accessor("mainRenderTarget")
|
||||
void antarchy$setMainRenderTarget(RenderTarget renderTarget);
|
||||
}
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
package com.craisinlord.antarchy.mixins.client;
|
||||
|
||||
import com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity;
|
||||
import com.craisinlord.antarchy.content.portalgun.PortalGunWorldPortalShape;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.multiplayer.ClientPacketListener;
|
||||
import net.minecraft.network.protocol.game.ClientboundTeleportEntityPacket;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
import net.minecraft.world.phys.AABB;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(ClientPacketListener.class)
|
||||
public abstract class PortalGunEntityTeleportPacketMixin {
|
||||
@Inject(method = "handleTeleportEntity", at = @At("TAIL"))
|
||||
private void antarchy$snapPortalTransitEntity(ClientboundTeleportEntityPacket packet, CallbackInfo ci) {
|
||||
Minecraft minecraft = Minecraft.getInstance();
|
||||
if (minecraft.level == null) {
|
||||
return;
|
||||
}
|
||||
Entity entity = minecraft.level.getEntity(packet.getId());
|
||||
Vec3 destination = new Vec3(packet.getX(), packet.getY(), packet.getZ());
|
||||
if (entity == null) {
|
||||
return;
|
||||
}
|
||||
antarchy$snapToPortalExit(entity, destination);
|
||||
}
|
||||
|
||||
private static void antarchy$snapToPortalExit(Entity entity, Vec3 destination) {
|
||||
if (!isNearPortalExit(entity, destination)) {
|
||||
return;
|
||||
}
|
||||
entity.lerpTo(destination.x, destination.y, destination.z, entity.getYRot(), entity.getXRot(), 0);
|
||||
entity.setPortalCooldown(PortalGunPortalEntity.TELEPORT_COOLDOWN_TICKS);
|
||||
entity.setPos(destination.x, destination.y, destination.z);
|
||||
entity.xo = destination.x;
|
||||
entity.yo = destination.y;
|
||||
entity.zo = destination.z;
|
||||
entity.yRotO = entity.getYRot();
|
||||
entity.xRotO = entity.getXRot();
|
||||
if (entity instanceof LivingEntity livingEntity) {
|
||||
LivingEntityPortalLerpAccessor accessor = (LivingEntityPortalLerpAccessor) livingEntity;
|
||||
accessor.antarchy$setLerpHeadSteps(0);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isNearPortalExit(Entity entity, Vec3 destination) {
|
||||
Vec3 offset = destination.subtract(entity.position());
|
||||
double entityExtent = Math.max(entity.getBbWidth(), entity.getBbHeight());
|
||||
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)) {
|
||||
PortalGunWorldPortalShape.PortalLocalCoords coords = portal.getWorldPortalShape().localCoords(probe);
|
||||
if (coords.depth() >= -entityExtent - 0.5D
|
||||
&& coords.depth() <= PortalGunWorldPortalShape.DEFAULT_SCAN_DISTANCE + 0.5D
|
||||
&& Math.abs(coords.horizontal()) <= portal.getWorldPortalShape().halfWidth() + edgeAllowance
|
||||
&& Math.abs(coords.vertical()) <= portal.getWorldPortalShape().halfHeight() + edgeAllowance) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package com.craisinlord.antarchy.mixins.client;
|
||||
|
||||
import com.craisinlord.antarchy.content.client.PortalGunZoomClientState;
|
||||
import net.minecraft.client.Camera;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.GameRenderer;
|
||||
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(GameRenderer.class)
|
||||
public abstract class PortalGunZoomFovMixin {
|
||||
@Inject(method = "getFov", at = @At("RETURN"), cancellable = true)
|
||||
private void antarchy$applyPortalGunZoom(Camera camera, float partialTick, boolean useFovSetting, CallbackInfoReturnable<Double> cir) {
|
||||
cir.setReturnValue(PortalGunZoomClientState.applyFov(cir.getReturnValue(), partialTick, Minecraft.getInstance().options));
|
||||
}
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
package com.craisinlord.antarchy.mixins.client;
|
||||
|
||||
import com.craisinlord.antarchy.content.client.renderer.PortalGunPortalViewAreaManager;
|
||||
import net.minecraft.client.multiplayer.ClientChunkCache;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
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(ClientChunkCache.class)
|
||||
public abstract class PortalViewChunkCacheMixin {
|
||||
@Inject(method = "drop", at = @At("TAIL"))
|
||||
private void antarchy$invalidatePortalChunk(ChunkPos pos, CallbackInfo ci) {
|
||||
PortalGunPortalViewAreaManager.onChunkDropped(pos);
|
||||
}
|
||||
}
|
||||
Loaded 100 of 189 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user